【发布时间】:2014-10-03 01:13:56
【问题描述】:
如果要根据组内的先前/后验非 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]]
这是相当复杂的。很遗憾,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”中的变量合并的命令)也会出现同样的问题 - 系统地忽略它们以获得更简单的语法。我也很喜欢所有的滚动选项。
【问题讨论】:
-
na.locf解决方案在速度方面与此解决方案相比如何? -
包装整个东西(比如
dplyr::mutate)不是一个选项吗? -
如果您向create a sample data.table 提供代码,我们可以用来检查我们的结果并帮助进行基准测试,这将会很有帮助。
-
如果你摆脱了动物园一号的
::电话,对我来说它快了大约 30%。即调用na.locf.default而不是zoo::na.locf.default -
@ssdecontrol 因为
::是一个函数,并且有与该额外函数调用相关的开销。
标签: r data.table