【问题标题】:aggregate/merge over date range using data.table使用 data.table 在日期范围内聚合/合并
【发布时间】:2017-11-16 10:26:59
【问题描述】:

假设我有两个 data.tables:

summary <- data.table(period = c("A","B","C","D"),
                 from_date = ymd(c("2017-01-01", "2017-01-03", "2017-02-08", "2017-03-07")),
                 to_date = ymd(c("2017-01-31", "2017-04-01", "2017-03-08", "2017-05-01"))
)

log <- data.table(date = ymd(c("2017-01-03","2017-01-20","2017-02-01","2017-03-03",
                               "2017-03-15","2017-03-28","2017-04-03","2017-04-23")),
                  event1 = c(4,8,8,4,3,4,7,3), event2 = c(1,8,7,3,8,4,6,3))

看起来像这样:

> summary
   period  from_date    to_date
1:      A 2017-01-01 2017-01-31
2:      B 2017-01-03 2017-04-01
3:      C 2017-02-08 2017-03-08
4:      D 2017-03-07 2017-05-01
> log
         date event1 event2
1: 2017-01-03      4      1
2: 2017-01-20      8      8
3: 2017-02-01      8      7
4: 2017-03-03      4      3
5: 2017-03-15      3      8
6: 2017-03-28      4      4
7: 2017-04-03      7      6
8: 2017-04-23      3      3

我想得到 summary 表中每个时间段的 event1event2 的总和。

我知道我可以做到:

summary[, c("event1","event2") := .(sum(log[date>=from_date & date<=to_date, event1]),
                               sum(log[date>=from_date & date<=to_date, event2]))
   , by=period][]

得到想要的结果:

   period  from_date    to_date event1 event2
1:      A 2017-01-01 2017-01-31     12      9
2:      B 2017-01-03 2017-04-01     31     31
3:      C 2017-02-08 2017-03-08      4      3
4:      D 2017-03-07 2017-05-01     17     21

现在,在我的实际问题中,我有大约 30 列要求和,我可能想稍后更改,summary 有大约 35,000 行,log 有约 40,000,000 行。有没有一种有效的方法来实现这一点?

注意:这是我在这里的第一篇文章。我希望我的问题足够清楚和具体,如果我应该做些什么来改进这个问题,请提出建议。谢谢!

【问题讨论】:

  • 这 30 列是“事件”列吗?
  • 您可以做的一件事来提高性能是首先聚合日志数据:agg_log = log[, .( sum1 = sum(event1), sum2 = sum(event2), by = .(date)]
  • @akrun 是的,但是名称不完全是 event1event30,更像是 eventa1 eventa10eventb1eventb10

标签: r data.table


【解决方案1】:

是的,您可以执行 非 equi 连接

(请注意,我已将 logsummary 更改为 LogSummary,因为原件已经是 R 中的函数。)

Log[Summary,
   on = c("date>=from_date", "date<=to_date"),
   nomatch=0L, 
   allow.cartesian = TRUE][, .(from_date = date[1],
                               to_date = date.1[1],
                               event1 = sum(event1),
                               event2 = sum(event2)),
                           keyby = "period"]

要对列模式求和,请使用 lapply.SD

joined_result <- 
  Log[Summary,
      on = c("date>=from_date", "date<=to_date"),
      nomatch = 0L, 
      allow.cartesian = TRUE]

cols <- grep("event[a-z]?[0-9]", names(joined_result), value = TRUE)

joined_result[, lapply(.SD, sum),
              .SDcols = cols,
              keyby = .(period,
                        from_date = date,
                        to_date = date.1)]

【讨论】:

  • 有没有办法从列表中传递列名而不是一一输入?
【解决方案2】:

使用data.table,可以在非等值连接期间使用by = .EACHI 进行聚合。

log[summary, on = .(date >= from_date, date <= to_date), nomatch=0L, 
    lapply(.SD, sum), by = .EACHI]
         date       date event1 event2
1: 2017-01-01 2017-01-31     12      9
2: 2017-01-03 2017-04-01     31     31
3: 2017-02-08 2017-03-08      4      3
4: 2017-03-07 2017-05-01     17     21

加上一些额外的清理:

log[summary, on = .(date >= from_date, date <= to_date), nomatch=0L, 
    c(period = period, lapply(.SD, sum)), by = .EACHI][
      , setnames(.SD, 1:2, c("from_date", "to_date"))]
    from_date    to_date period event1 event2
1: 2017-01-01 2017-01-31      A     12      9
2: 2017-01-03 2017-04-01      B     31     31
3: 2017-02-08 2017-03-08      C      4      3
4: 2017-03-07 2017-05-01      D     17     21

【讨论】:

  • 非常感谢!这种方法也有效,而且代码看起来比 Hugh 的回答要干净一些。但是使用休的答案的代码对我的问题的工作速度大约快了 50%,所以我接受它而不是这个。我希望我能同时接受。
  • 没问题,感谢您的反馈。速度差异确实让我感到惊讶。我曾期望避免使用笛卡尔积在速度和内存消耗方面会更好。
  • 这可能是因为我的现实生活中的数据有另一列表示实体,并且对于每个实体,log data.table 并没有那么长。我做了一些额外的测试:如果我重复我的 data.table 5 次,这两种方法之间的差异可以忽略不计。如果重复 10 次以上,你的代码实际上更快。
猜你喜欢
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
  • 1970-01-01
  • 2021-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多