【问题标题】:Counting previous rows in a data table based on date根据日期计算数据表中的前几行
【发布时间】:2016-03-01 14:40:47
【问题描述】:

(抱歉,如果这里的某些术语不正确 - 我来自 SQL 背景,我才刚刚进入 R 世界)

我有一个包含一系列按日期排序的条目的数据表。数据表中的一个字段是分组值,一个是时间值。 数据按组排序(或键控-我是 R 新手,但仍然不确定差异)然后是我想计算的日期,对于该组中的每一行在当前行之前有多少行(包括当前),在给定的时间跨度内。

这是我正在尝试做的一个简化示例,使用 Lobolly 数据集:

准备示例数据:

library(lubridate)
library(zoo)
library(data.table)
DT = as.data.table(Loblolly)
DT[,rd := Sys.time() + years(age)]
setkey(DT,Seed,rd)

现在我们有一个按 Seed(组)和 rd(我的日期列)排序的数据表。我有一个解决方案,它将基于 10 年的间隔产生我的计数值 (ct):

DT[,.ct:=mapply(function(x,y) DT[(rd>x-years(10) & rd<=x &Seed==y),.N],DT$rd,DT$Seed)]

这会在此示例数据集中产生所需的结果:

    height age Seed                  rd  ct
 1:   3.93   3  329 2019-03-01 13:38:00   1
 2:   9.34   5  329 2021-03-01 13:38:00   2
 3:  26.08  10  329 2026-03-01 13:38:00   3
 4:  37.79  15  329 2031-03-01 13:38:00   2
 5:  48.31  20  329 2036-03-01 13:38:00   2
 6:  56.43  25  329 2041-03-01 13:38:00   2
 7:   4.12   3  327 2019-03-01 13:38:00   1
 8:   9.92   5  327 2021-03-01 13:38:00   2
 9:  26.54  10  327 2026-03-01 13:38:00   3
10:  37.82  15  327 2031-03-01 13:38:00   2
...
...

但是,我需要扩展它以处理超过 500 万条记录,跨越大约 10,000 个组,并且在那里运行需要很长的时间。有没有更快、更简单的方法来做我想做的事情?

【问题讨论】:

    标签: r data.table zoo


    【解决方案1】:

    这是使用data.table::foverlaps 的可能解决方案。这里的想法是首先加入{Sys.time() - years(10), Sys.time() + years(age)} 的整个范围。然后,只计算差异小于

    DT <- as.data.table(Loblolly)
    DT[, c("rd", "rd2") := Sys.time() + years(age)] # create identical columns so foverlaps will work
    setkey(DT, Seed, rd, rd2) # key by all for same reason
    DT2 <- DT[, .(Seed, rd = rd - years(10), rd2, indx = .I)] # create minum range, create index to store row number
    DT[, ct := foverlaps(DT, DT2)[i.rd > rd, .N, by = indx]$N] # run foverlaps, subset by condition and count
    head(DT, 10)
    #     height age Seed                  rd                 rd2 ct
    #  1:   3.93   3  329 2019-03-01 22:59:02 2019-03-01 22:59:02  1
    #  2:   9.34   5  329 2021-03-01 22:59:02 2021-03-01 22:59:02  2
    #  3:  26.08  10  329 2026-03-01 22:59:02 2026-03-01 22:59:02  3
    #  4:  37.79  15  329 2031-03-01 22:59:02 2031-03-01 22:59:02  2
    #  5:  48.31  20  329 2036-03-01 22:59:02 2036-03-01 22:59:02  2
    #  6:  56.43  25  329 2041-03-01 22:59:02 2041-03-01 22:59:02  2
    #  7:   4.12   3  327 2019-03-01 22:59:02 2019-03-01 22:59:02  1
    #  8:   9.92   5  327 2021-03-01 22:59:02 2021-03-01 22:59:02  2
    #  9:  26.54  10  327 2026-03-01 22:59:02 2026-03-01 22:59:02  3
    # 10:  37.82  15  327 2031-03-01 22:59:02 2031-03-01 22:59:02  2
    

    2017 年 3 月 17 日编辑:

    使用 data.table v1.10.4+,您现在可以使用与 by = .EACHI 结合的非 uqui 连接。这基本上允许您使用&gt;=&lt;= 加入,而不仅仅是精确加入,还可以在加入时运行计算(为了避免像你的情况那样的笛卡尔连接)并只返回最终结果。所以在你的具体情况下,你可以这样做

    DT[, rd10 := rd - years(10)]
    DT[, ct := DT[DT, .N, on = .(Seed, rd <= rd, rd > rd10), by = .EACHI]$N]
    

    【讨论】:

    • 这在大约 100,000 行时表现出色,但在 100 万行及以上时,内存使用量会迅速增加(工作设置为 14GB)——我假设是因为 R 在 foverlaps 期间创建了一个非常大的中间数据结构。有没有一种方法可以避免这种情况?是否有可能以某种方式循环组?
    • 是的,我很害怕...这是因为我进行了完全联接。我想对于这个解决方案,您将需要一个大 RAM,或者等待其他人提供更好的解决方案。
    • 我用更现代的解决方案更新了答案。我认为现在它应该更快,内存效率更高。试用最新的 data.table 版本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    相关资源
    最近更新 更多