具有滚动子集的data.table聚合日期

时间:2014-04-30 15:53:19

标签: r data.table

我沿着这些行有一组数据

d1 <- data.frame(
  cat1 = sample(c('a', 'b', 'c'), 100, replace = TRUE),
  date = rep(Sys.Date() - sample(1:100)),
  val = rnorm(100, 50, 5)
  )

require(data.table)

d2 <- data.table(d1)

我可以毫无问题地获得每日总和

d2[ , list(.N, sum(val)), by = c("cat1", "date")]

我希望得到超过2天(然后是7天)的总和

这有效:

d.list <- sort(unique(d2$date))
o.list <- list()

for(i in seq_along(d.list)){
  o.list[[i]] <- d2[d2$date >= d.list[i] - 1 & d2$date <= d.list[i], list(.N, sum(val), max(date)), by = c("cat1")]
}

do.call(rbind, o.list)

但是放慢了更大的数据集,并且似乎不是数据的最佳用途。表。

有更有效的方法吗?

3 个答案:

答案 0 :(得分:3)

这有点快:

首先我们加入完全匹配并获得最后一个索引(如果是多个匹配)

setkey(d2, cat1, date)
tmp1 = d2[unique(d2, by=key(d2)), which=TRUE, mult="last", allow.cartesian=TRUE]

然后,我们构建copy d2并通过引用将date更改为date-1 。然后,我们使用roll=-Inf执行联接 - 这是下一次向后观察。换句话说,如果没有完全匹配,它将填充下一个可用值。

d3 = copy(d2)[, date := date-1]
setkey(d3, cat1, date)
tmp2 = d2[unique(d3, by=key(d2)), roll=-Inf, which=TRUE, allow.cartesian=TRUE]

从这里开始,我们整理了指数:

idx1 = tmp1-tmp2+1L
idx2 = data.table:::vecseq(tmp2, idx1, sum(idx1))

来自d2的子集idx2并从idx1生成唯一ID:

ans1 = d2[idx2][, grp := rep(seq_along(idx1), idx1)]

最后按grp汇总并获得所需的结果:

ans1 = ans1[, list(cat1=cat1[1L], date=date[.N], 
         N = .N, val=sum(val)), by=grp][, grp:=NULL]
> head(ans1, 10L)
#      cat1       date N       val
#   1:    a 2014-01-20 1  47.69178
#   2:    a 2014-01-25 1  52.01006
#   3:    a 2014-02-01 1  46.82132
#   4:    a 2014-02-06 1  44.62404
#   5:    a 2014-02-11 1  49.63218
#   6:    a 2014-02-14 1  48.80676
#   7:    a 2014-02-22 1  49.27800
#   8:    a 2014-02-23 2  96.17617
#   9:    a 2014-02-26 1  49.20623
#  10:    a 2014-02-28 1  46.72708

结果与解决方案中的结果相同。这个在我的笔记本电脑上花了0.02秒,而你的笔记本电脑花了0.58秒。


连续7天,只需更改:

d3 = copy(d2)[, date := date-1]

d3 = copy(d2)[, date := date-6]

答案 1 :(得分:1)

OP在你想要的内容中解释得很糟糕,但这似乎就是这样:

# generate the [date-1,date] sequences for each date
# adjust length.out to suit your needs
dates = d2[, list(date.seq = seq(date, by = -1, length.out = 2)), by = date]

setkey(dates, date.seq)
setkey(d2, date)

# merge and extract info needed
dates[d2][, list(.N, sum(val), date.seq[.N]), by = list(date, cat1)][,
            !"date", with = F]
#     cat1 N        V2         V3
#  1:    a 1  38.95774 2014-01-21
#  2:    a 1  38.95774 2014-01-21
#  3:    c 1  55.68445 2014-01-22
#  4:    c 2 102.20806 2014-01-23
#  5:    c 1  46.52361 2014-01-23
# ---                            
#164:    c 1  50.17986 2014-04-27
#165:    b 1  51.43489 2014-04-28
#166:    b 2 100.91982 2014-04-29
#167:    b 1  49.48493 2014-04-29
#168:    c 1  54.93311 2014-04-30

答案 2 :(得分:0)

是否可以设置分箱日期,然后对其进行by

d2$day7 <- as.integer(d2$date) %/% 7
d2[ , list(.N, sum(val)), by = c("cat1", "day7")]

这会给出一个分档值 - 如果你想要一个7天的滑动窗口,我需要再考虑一下。此外,对于分箱方法,如果要选择组开始的星期几,则可能需要在执行%/%之前减去偏移量。