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)
Utilisez bind_rows () à partir du package dplyr:
bind_rows(list_of_dataframes, .id = "column_label")
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.
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
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.
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.
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’
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
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)
Comment cela devrait-il être fait dans le sens inverse:
df.dplyr.purrr <- listOfDataFrames %>% map_df(bind_rows)
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.
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’
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