【问题标题】:Efficiently fill out (locf/nocb) values of data.table column, then aggregate by another column高效填写 data.table 列的 (locf/nocb) 值,然后由另一列聚合
【发布时间】:2017-01-13 22:31:26
【问题描述】:

我有一个 data.table,其中包含一列客户 ID、一列他们购买的天数以及一列包含该购买值的列。我想做的是计算客户每天购买价值的平均值,用下一个可用价值填充缺失值。

为简单起见,在我的最小示例中,我将没有重复的日子。

library(data.table)
dat <- data.table(custid=rep(seq(10),5), day=sample(50), val=rnorm(50,0,1))[order(custid,day)]

现在,我知道如何解决这个问题,但我不知道如何有效地做到这一点。一种解决方案是扩展 data.table 使缺失值变为 NA,然后使用来自 zoona.locf() 向后进行下一个观察:

library(zoo)
res <- dat[as.data.table(expand.grid(custid=seq(10), day=seq(50))), on=c('custid','day'), allow.cartesian=TRUE, nomatch=NA][order(custid,day)]
res[, val:=na.locf(val, fromLast=TRUE, na.rm=FALSE), by='custid']
res <- res[,list(meanVal=mean(val, na.rm=TRUE)), by='day']

但是,当有很多天和很多客户时,这会创建一个非常大的表,但大多数客户只在少数几天内购买。所以我不想那样。

另一种解决方案是循环天数,每天过滤和聚合,然后再次将行绑定到 data.table 中:

res2 <- list()
for (dy in seq(max(dat$day))) {
    res2 <- c(res2, 
              list(dat[day>=dy, .SD[1], by='custid'][,list(day=dy, meanVal=mean(val, na.rm=T))]))
}
res2 <- rbindlist(res2)

但是,这很慢。

谁能想出一个既不需要慢循环也不需要创建大型中间表的 data.table 解决方案?

【问题讨论】:

  • aggregate(dat, list(dat$day), mean)[, c('day', 'val')] 做你想做的事吗?
  • 如果想在求平均值前用下一个值替换NA,可以直接赋值dat[is.na(dat$val), ]$val &lt;- dat[which(is.na(dat$val)) + 1, ]$val

标签: r data.table


【解决方案1】:

在我有限的测试中,这比您的任何一个选项都快(顺便说一句,使用 CJ 而不是 data.table(expand.grid),并且不会使用太多内存:

dat[dat, on = .(day >= day), mean(val[!duplicated(custid)]), by = .EACHI]

这假设数据按照 OP 中的日期排序。

【讨论】:

  • 这就是我一直在寻找的 - 一个纯粹的 data.table 解决方案!谢谢!
猜你喜欢
  • 2018-12-29
  • 2019-05-06
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 1970-01-01
相关资源
最近更新 更多