【问题标题】:data.table aggregation with rolling subset on datedata.table 聚合与日期滚动子集
【发布时间】:2014-04-30 15:53:18
【问题描述】:

我有一组数据

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)

但是在更大的数据集上会变慢,而且似乎不是 data.table 的最佳用途。

有没有更有效的方法?

【问题讨论】:

  • 首先,您需要N 列做什么?对于每日总和,只需执行d2 &lt;- setDT(d1)[ , sum(val), by = c("cat1", "date")]

标签: r data.table


【解决方案1】:

这有点快:

首先我们加入精确匹配并获得最后一个索引(在多个匹配的情况下)

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

然后,我们构造一个d2copy 并将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))

idx2 子集d2 并从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]

【讨论】:

  • 非常感谢@Arun。我会解决这个问题并尝试理解这些步骤
  • 嗨,你知道这是否仍然是当前版本的 data.table,或者在过去 3 年中 data.table 有一些变化,这将使计算更容易(理解:))?
  • @ira,我认为非 equi 连接将是可行的方法,但不确定。查看来自 SO 或 data.table wiki[r] non equi 搜索结果,我在其中尝试记录所有非 equi Q 以了解经常出现的问题类型。
【解决方案2】:

你想要什么在 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"]
#     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

【讨论】:

    【解决方案3】:

    是否可以设置一个分箱日期,然后在上面执行by

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

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

    【讨论】:

    • 我会试试看 - 谢谢。我确实需要一个滑动窗口,并且某些地方的日期不规则(跳过天数)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-07
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    • 1970-01-01
    相关资源
    最近更新 更多