3
votes

Équivalent plus rapide de group_by%>% expand dans R

J'essaie de créer une séquence d'années pour plusieurs ID dans R. Ma table d'entrée a une seule ligne pour chaque ID et donne un Start_year. Cela ressemble à ceci:

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

etc ...

J'ai besoin de créer un tableau avec plusieurs lignes pour chaque identifiant, montrant chaque année de leur Start_year jusqu'en 2015. Je vais ensuite l'utiliser pour rejoindre une autre table. Donc, dans mon exemple, ID1 aurait 17 lignes avec les années 1999: 2015. ID2 aurait 12 lignes 2004: 2015, ID3 aurait 1 ligne 2015 et ID4 aurait 9 lignes 2007: 2015.

Pour un sous-ensemble de mes données, je peux faire fonctionner cela en utilisant le code suivant:

ID    Start_year
01          1999
02          2004
03          2015
04          2007

Cependant, mon ensemble de données complet contient environ 5 millions d'identifiants, et cette commande semble être extrêmement lente, prenant plusieurs heures.

Je suis donc recherche d'une implémentation plus rapide de cette commande dans R. D'après mon expérience, les commandes data.table semblent souvent être plus rapides que dplyr / tidyr - cependant, je ne connais pas du tout la syntaxe data.table.


0 commentaires

3 Réponses :


9
votes

Vous pourriez faire

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

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

Dans votre cas, vous auriez probablement besoin de faire

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()

A tidyverse manière de la même idée

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

data

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
# ...


1 commentaires

Hou la la! Merci, cela réalise la même chose en quelques secondes! On estimait que ma méthode précédente prenait 22 heures



1
votes

une solution tidyverse pourrait être:

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


0 commentaires

4
votes

Si vous avez suffisamment de mémoire, vous pouvez prendre un ensemble complet d'ID x années et filtrer avec une jointure progressive:

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 prend la "jointure croisée" du vecteur des identifiants et des années. Si vous n'êtes pas sur la dernière version de data.table, vous devrez peut-être nommer les deux arguments (par exemple, CJ (ID = ID, Start_year = seq.int (min (Start_year), 2015L)) ).

Commentaire . L'OP dit que l'approche de @markus réduit déjà l'opération à quelques secondes, donc peut-être qu'une amélioration supplémentaire n'est pas nécessaire ... De plus, je ne suis pas vraiment sûr qu'il y ait des circonstances dans lesquelles mon approche serait plus rapide.


0 commentaires