如果想要根据同一组中前/后面的非缺失观测值来填补一个变量的缺失值,可以使用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]]
这很复杂。这很遗憾因为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])
}
}
然后写下来
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value, by = id)]
这并不是很令人满意,因为有人希望写
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value), by = id]
然而,这需要大量时间才能运行。对于最终用户来说,学习使用 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
为什么我不直接使用na.locf.default
?尽管速度差异并不是很重要,但其他类型的data.table命令(那些依赖于"by"中变量进行合并的命令)也会出现同样的问题- 为了获得更简单的语法而系统地忽略它们是很遗憾的。我也非常喜欢所有的roll选项。
::
的调用,在我的情况下会快约 30%。也就是说,使用na.locf.default
而不是zoo::na.locf.default
。 - GSee::
是一个函数,使用它会增加额外的函数调用开销。 - GSeedata.table
中处理缺失值就太好了。你能否提交一个问题报告?只需将其链接回这个SO帖子即可。 - Arun