web-dev-qa-db-fra.com

Convertir une liste de trames de données en un seul

J'ai un code qui, à un endroit donné, finit avec une liste de trames de données que je souhaite vraiment convertir en une seule grande trame de données.

J'ai eu quelques indications d'un question précédente qui essayait de faire quelque chose de similaire mais plus complexe.

Voici un exemple de ce que je commence avec (ceci est grossièrement simplifié pour illustration):

listOfDataFrames <- vector(mode = "list", length = 100)

for (i in 1:100) {
    listOfDataFrames[[i]] <- data.frame(a=sample(letters, 500, rep=T),
                             b=rnorm(500), c=rnorm(500))
}

J'utilise actuellement ceci:

  df <- do.call("rbind", listOfDataFrames)
289
JD Long

Utilisez bind_rows () à partir du package dplyr:

bind_rows(list_of_dataframes, .id = "column_label")
70
joeklieg

Une autre option consiste à utiliser une fonction plyr:

df <- ldply(listOfDataFrames, data.frame)

C'est un peu plus lent que l'original:

> system.time({ df <- do.call("rbind", listOfDataFrames) })
   user  system elapsed 
   0.25    0.00    0.25 
> system.time({ df2 <- ldply(listOfDataFrames, data.frame) })
   user  system elapsed 
   0.30    0.00    0.29
> identical(df, df2)
[1] TRUE

Je suppose que l’utilisation de do.call("rbind", ...) sera l’approche la plus rapide que vous puissiez trouver, à moins que vous ne puissiez faire quelque chose comme (a) utiliser une matrice au lieu d’une image.frames et (b) préallouer la matrice finale et l’affecter à plutôt que de le faire pousser.

Éditez 1 :

D'après le commentaire de Hadley, voici la dernière version de rbind.fill de CRAN:

> system.time({ df3 <- rbind.fill(listOfDataFrames) })
   user  system elapsed 
   0.24    0.00    0.23 
> identical(df, df3)
[1] TRUE

C'est plus facile que rbind, et légèrement plus rapide (ces timings durent plusieurs fois). Et pour autant que je sache, la version de plyr sur github est encore plus rapide que cela.

180
Shane

Par souci d’exhaustivité, j’ai pensé que les réponses à cette question nécessitaient une mise à jour. "Je suppose que l'utilisation de do.call("rbind", ...) sera l'approche la plus rapide que vous trouverez ..." C'était probablement le cas pour mai 2010 et après, mais vers septembre 2011, une nouvelle fonction rbindlist a été introduit dans le package data.table version 1.8.2, avec la remarque suivante: "Ceci a le même effet que do.call("rbind",l), mais beaucoup plus rapidement". Combien plus vite?

library(rbenchmark)
benchmark(
  do.call = do.call("rbind", listOfDataFrames),
  plyr_rbind.fill = plyr::rbind.fill(listOfDataFrames), 
  plyr_ldply = plyr::ldply(listOfDataFrames, data.frame),
  data.table_rbindlist = as.data.frame(data.table::rbindlist(listOfDataFrames)),
  replications = 100, order = "relative", 
  columns=c('test','replications', 'elapsed','relative')
  ) 

                  test replications elapsed relative
4 data.table_rbindlist          100    0.11    1.000
1              do.call          100    9.39   85.364
2      plyr_rbind.fill          100   12.08  109.818
3           plyr_ldply          100   15.14  137.636
103
andrekos

bind-plot

Code:

library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
plyr::rbind.fill(dflist),
dplyr::bind_rows(dflist),
data.table::rbindlist(dflist),
plyr::ldply(dflist,data.frame),
do.call("rbind",dflist),
times=1000)

ggplot2::autoplot(mb)

Session:

R version 3.3.0 (2016-05-03)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

> packageVersion("plyr")
[1] ‘1.8.4’
> packageVersion("dplyr")
[1] ‘0.5.0’
> packageVersion("data.table")
[1] ‘1.9.6’

UPDATE: exécutez à nouveau le 31 janvier 2018. Couru sur le même ordinateur. Nouvelles versions de packages. Ajout de semences pour les amateurs de semences.

enter image description here

set.seed(21)
library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  plyr::rbind.fill(dflist),
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  plyr::ldply(dflist,data.frame),
  do.call("rbind",dflist),
  times=1000)

ggplot2::autoplot(mb)+theme_bw()


R version 3.4.0 (2017-04-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

> packageVersion("plyr")
[1] ‘1.8.4’
> packageVersion("dplyr")
[1] ‘0.7.2’
> packageVersion("data.table")
[1] ‘1.10.4’

UPDATE: Réexécutez le 6 août 2019.

enter image description here

set.seed(21)
library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  plyr::rbind.fill(dflist),
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  plyr::ldply(dflist,data.frame),
  do.call("rbind",dflist),
  purrr::map_df(dflist,dplyr::bind_rows),
  times=1000)

ggplot2::autoplot(mb)+theme_bw()

R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so

packageVersion("plyr")
packageVersion("dplyr")
packageVersion("data.table")
packageVersion("purrr")

>> packageVersion("plyr")
[1] ‘1.8.4’
>> packageVersion("dplyr")
[1] ‘0.8.3’
>> packageVersion("data.table")
[1] ‘1.12.2’
>> packageVersion("purrr")
[1] ‘0.3.2’
62
rmf

Il y a aussi bind_rows(x, ...) dans dplyr.

> system.time({ df.Base <- do.call("rbind", listOfDataFrames) })
   user  system elapsed 
   0.08    0.00    0.07 
> 
> system.time({ df.dplyr <- as.data.frame(bind_rows(listOfDataFrames)) })
   user  system elapsed 
   0.01    0.00    0.02 
> 
> identical(df.Base, df.dplyr)
[1] TRUE
47
TheVTM

Voici une autre façon de procéder (en l’ajoutant simplement aux réponses, car reduce est un outil fonctionnel très efficace qui est souvent négligé pour remplacer les boucles. Dans ce cas particulier, ni l’une ni l’autre ne sont nettement plus rapides qu’elles. appel)

en utilisant la base R:

df <- Reduce(rbind, listOfDataFrames)

ou, en utilisant le tidyverse:

library(tidyverse) # or, library(dplyr); library(purrr)
df <- listOfDataFrames %>% reduce(bind_rows)
13
yeedle

Comment cela devrait-il être fait dans le sens inverse:

df.dplyr.purrr <- listOfDataFrames %>% map_df(bind_rows)
11
Nick

Un visuel mis à jour pour ceux qui veulent comparer certaines des réponses récentes (je voulais comparer la solution purrr à dplyr). Fondamentalement, j'ai combiné les réponses de @TheVTM et @rmf.

enter image description here

Code:

library(microbenchmark)
library(data.table)
library(tidyverse)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  purrr::map_df(dflist, bind_rows),
  do.call("rbind",dflist),
  times=500)

ggplot2::autoplot(mb)

Informations de session:

sessionInfo()
R version 3.4.1 (2017-06-30)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Versions du paquet:

> packageVersion("tidyverse")
[1] ‘1.1.1’
> packageVersion("data.table")
[1] ‘1.10.0’
8
Nova

La seule chose qui manque aux solutions avec data.table est la colonne d'identifiant permettant de connaître le cadre de données dans la liste d'où proviennent les données.

Quelque chose comme ça:

df_id <- data.table::rbindlist(listOfDataFrames, idcol = TRUE)

Le paramètre idcol ajoute une colonne (.id) identifiant l'origine du cadre de données contenu dans la liste. Le résultat ressemblerait à quelque chose comme ceci:

.id a         b           c
1   u   -0.05315128 -1.31975849 
1   b   -1.00404849 1.15257952  
1   y   1.17478229  -0.91043925 
1   q   -1.65488899 0.05846295  
1   c   -1.43730524 0.95245909  
1   b   0.56434313  0.93813197  
7
f0nzie