Более быстрый эквивалент group_by%>% в R - PullRequest
3 голосов
/ 11 марта 2019

Я пытаюсь создать последовательность лет для нескольких идентификаторов в R. Моя входная таблица содержит по одной строке для каждого идентификатора и дает Start_year. Это выглядит так:

ID    Start_year
01          1999
02          2004
03          2015
04          2007

и т.д ...

Мне нужно создать таблицу с несколькими строками для каждого идентификатора, показывающую каждый год от их Start_year до 2015 года. Затем я буду использовать это для присоединения к другой таблице. Так что в моем примере ID1 будет иметь 17 строк с 1999: 2015. ID2 будет иметь 12 строк 2004: 2015, ID3 будет иметь 1 строку 2015, а ID4 будет 9 строк 2007: 2015.

Для подмножества моих данных я могу заставить это работать, используя следующий код:

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

Однако мой полный набор данных имеет около 5 миллионов идентификаторов, и эта команда выглядит очень медленной и занимает много часов.

Поэтому я ищу более быструю реализацию этой команды в R. По моему опыту, команды data.table часто кажутся более быстрыми, чем dplyr / tidyr, однако я совершенно не знаком с синтаксисом data.table.

Ответы [ 3 ]

9 голосов
/ 11 марта 2019

Вы могли бы сделать

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]

A 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)
4 голосов
/ 11 марта 2019

Если у вас достаточно памяти, вы можете взять полный набор идентификаторов х лет и отфильтровать с помощью скользящего соединения:

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 принимает «перекрестное соединение» вектора идентификаторов и лет,Если вы не используете последнюю версию data.table, вам может потребоваться назвать оба аргумента (т.е. CJ(ID = ID, Start_year = seq.int(min(Start_year), 2015L))).

Комментарий .ОП говорит, что подход @markus уже сводит операцию к секундам, поэтому, возможно, дальнейшее улучшение не требуется ... Кроме того, я не совсем уверен, что есть какие-то обстоятельства, при которых мой подход будет быстрее.

1 голос
/ 11 марта 2019

решение может быть:

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")
...