如何在不丢失日期格式的情况下将日期从一个变量复制到 R data.table 中的另一个变量?

How do I copy a date from one variable to another in R data.table without losing the date format?

我有一个包含两个日期变量的 data.table。数据集从 .csv 文件(最初是 .xlsx 文件)作为 data.frame 读入 R,然后使用 as.Date() 将两个变量转换为日期格式,以便它们显示如下:

df
  id   specdate    recdate
1  1 2014-08-12 2014-08-17
2  2 2014-08-15 2014-08-20
3  3 2014-08-21 2014-08-26
4  4       <NA> 2014-08-28
5  5 2014-08-25 2014-08-30
6  6       <NA>       <NA>

然后我将 data.frame 转换为 data.table:

df <- data.table(df)

然后我想创建第三个变量,如果存在 "specdate",它将包含 "specdate",但如果缺少 "specdate",则将其替换为 "recdate" (NA)。这是我遇到一些困难的地方,因为似乎无论我如何处理,data.table 仅在复制已经采用日期格式的完整变量时才以日期格式显示日期。否则,单个值将显示为数字(即使使用 as.IDate),我认为需要一个原始日期来更正此问题。有什么方法可以避免提供起始日期,而是将日期显示为 data.table?

中的日期

下面是我尝试用 recdate 日期填充 specdate 的 NAs:

# Function to fill NAs:
fillnas <- function(dataref, lookupref, nacol, replacecol, replacelist=NULL) {
      nacol <- as.character(nacol)
      if(!is.null(replacelist)) nacol <- factor(ifelse(dataref==lookupref & (is.na(nacol) | nacol %in% replacelist), replacecol, nacol))
      else nacol <- factor(ifelse(dataref==lookupref & is.na(nacol), replacecol, nacol))
      nacol                
    }

# Fill the NAs in specdate with the function:
    df[, finaldate := fillnas(dataref=id, lookupref=id, nacol=specdate, replacecol=as.IDate(recdate, format="%Y-%m-%d"))]

事情是这样的:

    > df
   id   specdate    recdate  finaldate
1:  1 2014-08-12 2014-08-17 2014-08-12
2:  2 2014-08-15 2014-08-20 2014-08-15
3:  3 2014-08-21 2014-08-26 2014-08-21
4:  4       <NA> 2014-08-28      16310
5:  5 2014-08-25 2014-08-30 2014-08-25
6:  6       <NA>       <NA>         NA

如果我使用 ifelse 从头开始​​创建新变量,显示问题会更加复杂:

df[, finaldate := ifelse(!is.na(specdate), specdate, recdate)]

这给出:

> df
   id   specdate    recdate finaldate
1:  1 2014-08-12 2014-08-17     16294
2:  2 2014-08-15 2014-08-20     16297
3:  3 2014-08-21 2014-08-26     16303
4:  4       <NA> 2014-08-28     16310
5:  5 2014-08-25 2014-08-30     16307
6:  6       <NA>       <NA>        NA

或者,如果我尝试查找和替换类型的方法,我会收到有关要替换的项目数量与替换长度不匹配的错误(我猜这是因为该方法未矢量化?), recdate 中的值被回收并最终出现在错误的位置:

> df$finaldate <- df$specdate
> df$finaldate[is.na(df$specdate)] <- df$recdate
Warning message:
In NextMethod(.Generic) :
  number of items to replace is not a multiple of replacement length
> df
   id   specdate    recdate  finaldate
1:  1 2014-08-12 2014-08-17 2014-08-12
2:  2 2014-08-15 2014-08-20 2014-08-15
3:  3 2014-08-21 2014-08-26 2014-08-21
4:  4       <NA> 2014-08-28 2014-08-17
5:  5 2014-08-25 2014-08-30 2014-08-25
6:  6       <NA>       <NA> 2014-08-20

所以总而言之 - 我应用的函数使我最接近我想要的,除了 NA 被替换的地方,替换值显示为数字而不是日期格式。一旦显示为数字,原点就需要再次将其显示为日期(我想避免提供原点,因为我通常不知道它,而且在最初的日期时必须提供它似乎是不必要的重复格式正确)。

任何关于我哪里出错的见解都将不胜感激。

适合我。您可能需要测试以确保您的 NA 值不是字符串或因子 "<NA>";它们看起来像真实的 NA 值:

dt[, finaldate := ifelse(is.na(specdate), recdate, specdate)][
  ,finaldate := as.POSIXct(finaldate*86400, origin="1970-01-01", tz="UTC")]
#    id   specdate    recdate  finaldate
# 1:  1 2014-08-12 2014-08-17 2014-08-12
# 2:  2 2014-08-15 2014-08-20 2014-08-15
# 3:  3 2014-08-21 2014-08-26 2014-08-21
# 4:  4         NA 2014-08-28 2014-08-28
# 5:  5 2014-08-25 2014-08-30 2014-08-25
# 6:  6         NA         NA         NA

数据

df <- read.table(text="  id   specdate    recdate
1  1 2014-08-12 2014-08-17
2  2 2014-08-15 2014-08-20
3  3 2014-08-21 2014-08-26
4  4         NA 2014-08-28
5  5 2014-08-25 2014-08-30
6  6         NA         NA", header=T, stringsAsFactors=F)

dt <- as.data.table(df)

我会这样处理,也许 :

DT <- data.table(df)
DT[, finaldate := specdata]
DT[is.na(specdata), finaldate := recdate]

您似乎想添加一个新列,这样您也可以保留原来的列。我也经常这样做。有时,我只是原地更新 :

DT <- data.table(df)
DT[!is.na(specdate), specdate:=recdate]
setnames(DT, "specdate", "finaldate")

像这样使用 i 可以避免创建可能非常大的全新列的数据价值。取决于保留原始列对您有多重要,它们有多少以及您的数据大小。 (请注意,整个列的数据价值仍然由 is.na() 调用创建,然后由 ! 再次创建,但至少新的 finaldate 没有第三列的价值。会很高兴在将来优化 i=!is.na() (#1386),如果您现在以这种方式使用 data.table,您将来无需更改代码即可受益。)

您可能有多个要替换的 "NA" 字符串。请注意,CRAN 上的 v1.9.6 中的 fread 对此进行了修复。来自 README :

  • correctly handles na.strings argument for all types of columns - it detect possible NA values without coercion to character, like in base read.table. fixes #504. Thanks to @dselivanov for the PR. Also closes #1314, which closes this issue completely, i.e., na.strings = c("-999", "FALSE") etc. also work.

顺便说一句,您犯了这里提到的前 3 个错误之一:https://github.com/Rdatatable/data.table/wiki/Support