在data.table中按组填写缺失值

Mat*_*hew 15 r data.table

如果想要根据组内的前/后非NA观察填充变量的缺失值,则data.table命令为

setkey(DT,id,date)
DT[, value_filled_in := DT[!is.na(value), list(id, date, value)][DT[, list(id, date)], value, roll = TRUE]]
Run Code Online (Sandbox Code Playgroud)

这很复杂.这是一个耻辱,因为它roll是一个非常快速和强大的选项(特别是与应用zoo::na.locf每个组内的功能相比)

我可以写一个便利函数来填补缺失的值

   fill_na <-  function(x , by = NULL, roll =TRUE , rollends= if (roll=="nearest") c(TRUE,TRUE)
             else if (roll>=0) c(FALSE,TRUE)
             else c(TRUE,FALSE)){
    id <- seq_along(x)
    if (is.null(by)){
      DT <- data.table("x" = x, "id" = id, key = "id") 
      return(DT[!is.na(x)][DT[, list(id)], x, roll = roll, rollends = rollends, allow.cartesian = TRUE])

    } else{
      DT <- data.table("x" = x, "by" = by, "id" = id, key = c("by", "id")) 
      return(DT[!is.na(x)][DT[, list(by, id)], x, roll = roll, rollends = rollends, allow.cartesian = TRUE])
    }
  }
Run Code Online (Sandbox Code Playgroud)

然后写

setkey(DT,id, date)
DT[, value_filled_in := fill_na(value, by = id)]
Run Code Online (Sandbox Code Playgroud)

因为有人想写,所以这并不令人满意

setkey(DT,id, date)
DT[, value_filled_in := fill_na(value), by = id]
Run Code Online (Sandbox Code Playgroud)

但是,这需要花费大量时间来运行.并且,对于最终用户来说,学习fill_na应该使用该by选项调用并且不应该使用它是很麻烦的 data.table by.这周围有一个优雅的解决方案吗?

一些速度测试

N <- 2e6
set.seed(1)
DT <- data.table(
         date = sample(10, N, TRUE),
           id = sample(1e5, N, TRUE),   
        value = sample(c(NA,1:5), N, TRUE),
       value2 = sample(c(NA,1:5), N, TRUE)                   
      )
setkey(DT,id,date)
DT<- unique(DT)

system.time(DT[, filled0 := DT[!is.na(value), list(id, date, value)][DT[, list(id, date)], value, roll = TRUE]])
#> user  system elapsed 
#>  0.086   0.006   0.105 
system.time(DT[, filled1 := zoo::na.locf.default(value, na.rm = FALSE), by = id])
#> user  system elapsed 
#> 5.235   0.016   5.274 
# (lower speed and no built in option like roll=integer or roll=nearest, rollend, etc)
system.time(DT[, filled2 := fill_na(value, by = id)])
#>   user  system elapsed 
#>  0.194   0.019   0.221 
system.time(DT[, filled3 := fill_na(value), by = id])
#>    user  system elapsed 
#> 237.256   0.913 238.405 
Run Code Online (Sandbox Code Playgroud)

我为什么不用na.locf.default?即使速度差异并不重要,但同样的问题也出现在其他类型的data.table命令中(那些依赖于变量在"by"中合并的命令) - 为了得到一个系统地忽略它们是一种耻辱.语法更简单.我也非常喜欢所有的滚动选项.

ano*_*red 16

现在有一种data.table填充缺失值的本机方法(截至1.12.4)。

这个问题引发了一个 github问题,该问题最近随着函数nafillsetnafill. 您现在可以使用

DT[, value_filled_in := nafill(value, type = "locf")]
Run Code Online (Sandbox Code Playgroud)

也可以NA用常量值或带回的下一个观察值来填充。

与问题中的方法的一个区别是,这些函数目前仅适用于NAnotNaNis.nais TRUEfor NaN-计划通过额外的参数在下一个版本中修复此问题。

我没有参与该项目,但我看到虽然 github 问题链接到这里,但没有其他链接,所以我代表未来的访问者回答。

更新:默认情况下NaN现在与NA.


edd*_*ddi 13

这是一种更快,更紧凑的方式(版本1.9.3+):

DT[, filled4 := DT[!is.na(value)][DT, value, roll = T]]
Run Code Online (Sandbox Code Playgroud)