更快地相当于group_by%>%R中的展开

时间:2019-03-11 13:47:02

标签: r dplyr data.table data-manipulation

我正在尝试为R中的多个ID创建年份序列。我的输入表的每个ID都有一行,并提供了一个Start_year。看起来像这样:

ID    Start_year
01          1999
02          2004
03          2015
04          2007

等...

我需要为每个ID创建一个包含多行的表,以显示从其Start_year到2015年的每一年。然后,我将使用它连接到另一个表。因此,在我的示例中,ID1在1999:2015年将有17行。 ID2将在2004:2015年有12行,ID3在2015年将有1行,而ID4在2007:2015年将有9行。

对于我的部分数据,我可以使用以下代码来使其工作:

df %>% group_by(ID) %>% expand(year = Start_year:2015, Start_year) %>% select(-Start_year)

但是,我的完整数据集有大约500万个ID,此命令似乎非常慢,耗时数小时。

因此,我希望在R中更快地执行此命令。以我的经验,data.table命令似乎通常比dplyr / tidyr更快-但是,我对data.table语法不甚了解。

3 个答案:

答案 0 :(得分:9)

你可以做

out <- DT[, .(col = seq.int(Start_year, 2015L)), by = ID]
out
#    ID  col
# 1:  1 1999
# 2:  1 2000
# 3:  1 2001
# 4:  1 2002
# 5:  1 2003
# 6:  1 2004
# 7:  1 2005
# 8:  1 2006
# 9:  1 2007
# ...

您可能需要这样做

setDT(df)[, .(col = seq.int(Start_year, 2015L)), by = ID]

具有相同想法的tidyverse方式

library(readr); library(dplyr); library(tidyr)
tbl <- read_table(text)

tbl %>% 
  group_by(ID) %>% 
  mutate(Start_year = list(seq.int(Start_year, 2015L))) %>%
  # rename(new_col = Start_year)
  unnest()

数据

text <- "ID    Start_year
01          1999
02          2004
03          2015
04          2007"

library(data.table)
DT <- fread(text)

答案 1 :(得分:4)

如果您有足够的内存,则可以使用完整的ID x年,并通过滚动联接进行过滤:

res <- DT[
  CJ(ID, Start_year = seq.int(min(Start_year), 2015L)), 
  on=.(ID, Start_year), 
  roll=TRUE, 
  nomatch=0
]

setnames(res, "Start_year", "Year")[]

CJ采用ID和年份向量的“交叉连接”。如果您使用的不是最新版本的data.table,则可能需要同时命名两个参数(即CJ(ID = ID, Start_year = seq.int(min(Start_year), 2015L)))。

评论。 OP表示@markus的方法已经使操作降低了几秒钟,因此也许不需要进一步的改进……而且,我不确定在任何情况下我的方法都会更快。

答案 2 :(得分:1)

tidyverse解决方案可能是:

df <- data.table::fread("
ID    Start_year
01          1999
02          2004
03          2015
04          2007")

library(padr)
library(tidyverse)

df %>% 
  pad_int('Start_year', 
          end_val = 2015, 
          group = "ID")