使用data.table实现tidyr::complete函数中的group_by功能

4
我有一个如下的数据框:
library(tidyverse)
df <- data_frame(
  id = c(1, 1, 2, 2), 
  date1 = as.Date(c("2013-01-01", "2013-02-01", "2015-04-01", "2015-05-01")), 
  date2 = as.Date(c("2012-12-09", "2012-12-09", "2015-03-10", "2015-03-10"))
)

# A tibble: 4 x 3
     id      date1      date2
  <dbl>     <date>     <date>
1     1 2013-01-01 2012-12-09
2     1 2013-02-01 2012-12-09
3     2 2015-04-01 2015-03-10
4     2 2015-05-01 2015-03-10

我希望完成这个数据框,使得对于每个id,都有另一个date1值。这个另一个date1值是下一个月计算出来的。同时,存在一个date2值,对于所有的id都是相同的。使用tidyr::complete函数可以实现此操作:

df %>% 
  group_by(id) %>% 
  complete(date1 = seq.Date(from = min(date1), length.out = 3, by = "month"), date2 = date2[1])

# A tibble: 6 x 3
# Groups:   id [2]
     id      date1      date2
  <dbl>     <date>     <date>
1     1 2013-01-01 2012-12-09
2     1 2013-02-01 2012-12-09
3     1 2013-03-01 2012-12-09
4     2 2015-04-01 2015-03-10
5     2 2015-05-01 2015-03-10
6     2 2015-06-01 2015-03-10

由于我的原始数据中有大约150K个组,tidyr的解决方案需要一个多小时才能完成。我认为使用data.table可以提高速度。是否可以在data.table中完成同样的事情?

类似的问题已经在data.table equivalent of tidyr::complete()中提出,但没有group_by子句。


2
使用 data.table setDT(df)[, .(date1 = seq(min(date1), length.out = 3, by = 'month'), date2 = date2[1]), id] - akrun
请参考@Uwe在这里的答案:https://stackoverflow.com/questions/46330683/inserting-rows-into-data-frame-when-values-missing-in-category - IceCreamToucan
2个回答

3

根据一些初步基准测试,data.table方法似乎更快。

library(data.table)
setDT(df)[, .(date1 = seq(min(date1), length.out = 3, by = 'month'), date2 = date2[1]), id]

基准测试

 df <- data_frame(
  id = rep(1:3000, each = 2), 
  date1 = rep(as.Date(c("2013-01-01", "2013-02-01", "2015-04-01", "2015-05-01")),
  length.out = 6000), 
  date2 = rep(as.Date(c("2012-12-09", "2012-12-09", "2015-03-10", "2015-03-10")), 
   length.out = 6000))

system.time({
df %>% 
  group_by(id) %>% 
  complete(date1 = seq.Date(from = min(date1), 
          length.out = 3, by = "month"), date2 = date2[1])
})
#user  system elapsed 
#64.05   21.27   86.05 

system.time({
setDT(df)[, .(date1 = seq(min(date1), length.out = 3, by = 'month'), date2 = date2[1]), id]
})
#user  system elapsed 
#  0.14    0.00    0.14 

3
那段 data.table 的代码是否等价?看起来你会拿像你的结果这样的对象,然后左连接到主表格中“完成”...? - Frank
2
@Frank 这是一种不同的方式,但是 `system.time({
  • setDT(df)[df[,.(date1 = seq(min(date1), length.out = 3, by = 'month'), date2 = date2[1]), id], on = .(id, date1, date2)]
  • })# user system elapsed 0.31 0.03 0.59` 仍然给了我更少的时间。
- akrun

0
如果您需要速度,尽可能保持代码的精简:
library(data.table)
library(lubridate)

> dt[, .SD
     ][, .(date1=max(date1)), .(id, date2)
     ][, date1Inc := date1 + months(1)
     ][, rbind(dt, .SD[, .(id, date1=date1Inc, date2)])
     ][order(id, date1)
     ]

   id      date1      date2
1:  1 2013-01-01 2012-12-09
2:  1 2013-02-01 2012-12-09
3:  1 2013-03-01 2012-12-09
4:  2 2015-04-01 2015-03-10
5:  2 2015-05-01 2015-03-10
6:  2 2015-06-01 2015-03-10
>   
> 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接