有效地转换data.table中的日期列

Dae*_*lus 8 r date data.table

我有一个包含许多列的大型数据集,其中包含两种不同格式的日期:

"1996-01-04" "1996-01-05" "1996-01-08" "1996-01-09" "1996-01-10" "1996-01-11"
Run Code Online (Sandbox Code Playgroud)

"02/01/1996" "03/01/1996" "04/01/1996" "05/01/1996" "08/01/1996" "09/01/1996"
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,class()都是"character".由于数据集有很多行(450万),我正在寻找一种有效的data.table转换方法.现在,我使用这个自建功能:

convert_to_date <- function(in_array){
  tmp <- try(as.Date(in_array, format = "%d/%m/%Y"),TRUE)
  if (all(!is.na(tmp)) & class(tmp) != "try-error"){
    return(tmp)
  } else{
    tmp2 <- try(as.Date(in_array),TRUE)
    if (all(!is.na(tmp2)) & class(tmp2) != "try-error"){
      return(tmp2)
    } else{
      return(in_array)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我用它转换我需要的列(data.table DF)

DF[,date:=convert_to_date(date)]
Run Code Online (Sandbox Code Playgroud)

然而,这仍然非常缓慢(每列接近45s).

有没有办法通过data.table方法优化这个?到目前为止,我还没有找到更好的方法,所以我会感谢任何提示.

PS:为了更好的可读性,我将该函数"外包"到第二个文件并在我的主程序中获取它.这是否会对R中的计算速度产生(负面)重大影响?

Uwe*_*Uwe 12

根据此基准测试,将标准明确格式(YYYY-MM-DD)中的字符日期转换为类的最快方法Date是使用as.Date(fasttime::fastPOSIXct()).

不幸的是,这需要事先测试格式,因为你的其他格式DD/MM/YYYY被错误解释了fasttime::fastPOSIXct().

因此,如果您不想打扰每个日期列的格式,可以使用以下anytime::anydate()函数:

# sample data
df <- data.frame(
    X1 = c("1996-01-04", "1996-01-05", "1996-01-08", "1996-01-09", "1996-01-10", "1996-01-11"), 
    X2 = c("02/01/1996", "03/01/1996", "04/01/1996", "05/01/1996", "08/01/1996", "09/01/1996"), 
    stringsAsFactors = FALSE)

library(data.table)
# convert date columns
date_cols <- c("X1", "X2")
setDT(df)[, (date_cols) := lapply(.SD, anytime::anydate), .SDcols = date_cols]
df
Run Code Online (Sandbox Code Playgroud)
           X1         X2
1: 1996-01-04 1996-02-01
2: 1996-01-05 1996-03-01
3: 1996-01-08 1996-04-01
4: 1996-01-09 1996-05-01
5: 1996-01-10 1996-08-01
6: 1996-01-11 1996-09-01
Run Code Online (Sandbox Code Playgroud)

基准时序表明,有一个权衡由所带来的便利与anytime封装和性能.因此,如果速度至关重要,则没有其他方法可以测试每列的格式并使用可用于格式的最快转换方法.

OP已将此try()功能用于此目的.下面的解决方案使用正则表达式来查找与给定格式匹配的所有列(仅使用行1来节省时间).这具有额外的好处,即相关列的名称是自动确定的,不需要输入.

# enhanced sample data with additional columns
df <- data.frame(
    X1 = c("1996-01-04", "1996-01-05", "1996-01-08", "1996-01-09", "1996-01-10", "1996-01-11"), 
    X2 = c("02/01/1996", "03/01/1996", "04/01/1996", "05/01/1996", "08/01/1996", "09/01/1996"), 
    X3 = "other data",
    X4 = 1:6,
    stringsAsFactors = FALSE)

library(data.table)
options(datatable.print.class = TRUE)

# coerce to data.table
setDT(df)[]
# convert date columns in standard unambiguous format YYYY-MM-DD
date_cols1 <- na.omit(names(df)[
  df[1, sapply(.SD, stringr::str_detect, pattern = "\\d{4}-\\d{2}-\\d{2}"),]])
# use fasttime package
df[, (date_cols1) := lapply(.SD, function(x) as.Date(fasttime::fastPOSIXct(x))), 
   .SDcols = date_cols1]
# convert date columns in DD/MM/YYYY format
date_cols2 <- na.omit(names(df)[
  df[1, sapply(.SD, stringr::str_detect, pattern = "\\d{2}/\\d{2}/\\d{4}"),]])
# use lubridate package
df[, (date_cols2) := lapply(.SD, lubridate::dmy), .SDcols = date_cols2]
df
Run Code Online (Sandbox Code Playgroud)
           X1         X2         X3    X4
       <Date>     <Date>     <char> <int>
1: 1996-01-04 1996-01-02 other data     1
2: 1996-01-05 1996-01-03 other data     2
3: 1996-01-08 1996-01-04 other data     3
4: 1996-01-09 1996-01-05 other data     4
5: 1996-01-10 1996-01-08 other data     5
6: 1996-01-11 1996-01-09 other data     6
Run Code Online (Sandbox Code Playgroud)

警告

如果其中一个日期列确实包含NA 在第一行中,则此列可能会转义为未转换.要处理这些情况,需要修改上述代码.

  • 不幸的是,fasttime :: fastPOSIXct(“ 1969-05-05”)`由于不解析1970年之前的年份而返回`NA`,因此使用`anytime :: anydate()`是另一个原因。 (2认同)