【问题标题】:How to optimize lagged differences in data.table (r)如何优化 data.table (r) 中的滞后差异
【发布时间】:2016-09-02 02:01:43
【问题描述】:

我正在尝试优化 r 代码的 sn-p 以使用 R 中的 data.table 计算滞后差异。我有两个可行的解决方案,但在我的真实数据(5 亿行数据集)上运行都很慢。我很享受使用 data.table 的速度和效率,但我实现的两种解决方案都很慢(与其他 data.table 操作相比)。

谁能为这个特定任务在 data.table 中更有效的编码实践提供建议?

library(data.table)
set.seed(1)
id <- 1:10
date_samp <- seq.Date(as.Date("2010-01-01"),as.Date("2011-01-01"),"days")
dt1 <- 
  data.table(id = sample(id,size = 30,replace=T),
             date_1 = sample(date_samp,size = 30,replace=T))
setkey(dt1,id,date_1)
### Attempt to get lagged date
## Attempt 1
dt1[,date_diff:=c(0,diff(date_1)),
    by=id]
## Attempt 2
## Works but gives warnings
dt1[,date_diff:=NULL]
dt1[,n_group := .N,by=id]
dt1[,date_diff:=c(0,date_1[2:n_group]-date_1[1:(n_group-1)]),
    by=id]

【问题讨论】:

  • 我也经历过 data.table 的缓慢意外。大多数时候它真的很快。从[.data.table 中的 j 位置调用 table 函数似乎比从控制台使用时慢得多。
  • @42 我怀疑将 .N 参数与 by=list(x1,x2,x3...) 一起使用会为您提供最快的表格计算...然后您可以重新调整为所需的格式
  • 你说得对,[, .N, by=list(...)] 更快。差异很大。当然,在这种规模的例子中并不明显,但在数百万的例子中大约是 100 倍。

标签: r data.table


【解决方案1】:

经过一番努力,我在一个相关问题上找到了“shift()”函数。我已经将数据放大了一些,并进行了一些粗略的分析,并添加了更多方法……但如果有更有效的方法,请更新并提供不同的答案。

为了响应下面的 cmets,我添加并更改了一些内容...尝试是数字(不是整数),而我的 keyed by 不正确。我添加了一个整数比较和一个以整数为键的键(除了数字)。现在看起来将日期转换为整数然后使用“按每个 i 分组”是最快的解决方案。

library(data.table)
set.seed(1)
id <- 1:100
date_samp <- seq.Date(as.Date("2010-01-01"),as.Date("2011-01-01"),"days")
n_samp <- 1e7
dt1 <- 
  data.table(id = sample(id,size = n_samp,replace=T),
             date_1 = sample(date_samp,size = n_samp,replace=T))
setkey(dt1,id,date_1)
### Attempt to get lagged date
## Attempt 1
dt1[,date_diff:=NULL]
system.time(dt1[,date_diff:=c(0,diff(date_1)),
    by=id])
## Attempt 2
dt1[,date_diff:=NULL]
dt1[,n_group := .N,by=id]
system.time(dt1[,date_diff:=c(0,date_1[2:n_group]-date_1[1:(n_group-1)]),
    by=id])
## Attempt 3
dt1[,date_diff:=NULL]
system.time(dt1[,date_diff:=date_1-shift(date_1),
    by=id])
## Attempt 4
## Use numeric instead
dt1[,date_diff:=NULL]
dt1[,date_1num:=NULL]
dt1[,date_1num:=as.numeric(date_1)]
system.time(dt1[,date_diff:=date_1num-shift(date_1num),
                by=id])
## Attempt 5
## Use a keyed by
dt_key <- unique(dt1[,list(id)])
dt1[,date_diff:=NULL]
system.time(dt1[dt_key,
    date_diff:=date_1num-shift(date_1num),
    by=.EACHI])

## Attempt 6
## Use integers instead
dt1[,date_diff:=NULL]
dt1[,date_1int:=as.integer(date_1)]
system.time(dt1[,date_diff:=date_1int-shift(date_1int),
                by=id])
## Attempt 7
## Use integers with keyed by
dt1[,date_diff:=NULL]
dt1[,date_1int:=as.integer(date_1)]
system.time(dt1[dt_key,
                date_diff:=date_1int-shift(date_1int),
                by=.EACHI])


# attempt   user  system elapsed 
# 1         0.34    0.25    0.59     
# 2         0.37    0.28    0.67 
# 3         0.25    0.16    0.41
# 4         0.11    0.01    0.13 
# 5         0.06    0.03    0.10
# 6         0.09    0.00    0.09
# 7         0.05    0.00    0.04

【讨论】:

  • 那些不是整数。 as.numeric 转换为浮点数。请改用IDate 类。而且您的尝试 5 似乎是错误的。执行dt[key, j]时不会发生分组,您必须添加by=.EACHI选项。
  • @Frank - 使用as.integer 而不是as.numeric 似乎会浪费更多时间。我的测试似乎表明转换为 as.IDate 的变量会慢一个数量级,尽管按照 Attempt4 使用 shift 时。
  • 是的,我也看到了。奇怪的。似乎违背了拥有 IDate 类的目的。
  • 好的,在这里发帖@thela github.com/Rdatatable/data.table/issues/1836之前没有@你;由于您的快速回复,将您误认为是OP。
  • @Frank Perfect,删除了警告,我明白为什么现在会发生这种情况,谢谢!
【解决方案2】:

如果你想通过:

  dt1[order(id, date_1)
      ][, idP := shift(id, type='lag')
      ][, headP := is.na(idP) | idP != id
      ][, date_1P := shift(date_1, type='lag')
      ][headP == T, date_diff := 0
      ][headP == F, date_diff := date_1 - date_1P
      ][, c('headP', 'idP', 'date_1P') := NULL
      ][]

【讨论】: