índice Converter uma lista de quadros de dados em um quadro de dados




numerar figuras automaticamente word 2010 (8)

Eu tenho código que em um lugar acaba com uma lista de quadros de dados que eu realmente quero converter em um único quadro grande de dados.

Eu tenho algumas dicas de uma pergunta anterior que estava tentando fazer algo semelhante, mas mais complexo.

Aqui está um exemplo do que estou começando (isto é simplificado para ilustração):

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

Atualmente estou usando isso:

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

Código:

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)

Sessão:

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

ATUALIZAÇÃO : Execute novamente 31-jan-2018. Correu no mesmo computador. Novas versões de pacotes. Adicionado sementes para os amantes de sementes.

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

Use bind_rows () do pacote dplyr:

bind_rows(list_of_dataframes, .id = "column_label")

Como isso deve ser feito no tidyverse:

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

Para o propósito de completude, achei que as respostas para essa pergunta exigiam uma atualização. "Meu palpite é que usando do.call("rbind", ...) vai ser a abordagem mais rápida que você vai encontrar ..." Provavelmente foi verdade para maio de 2010 e algum tempo depois, mas em cerca de setembro de 2011 uma nova função rbindlist foi introduzida no pacote data.table versão 1.8.2, com uma observação que "Isto faz o mesmo que do.call("rbind",l) , mas muito mais rápido". Quão rápido?

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

Há também bind_rows(x, ...) em 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

A única coisa que faltam nas soluções com data.table é a coluna do identificador para saber de qual dataframe na lista os dados estão vindo.

Algo assim:

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

O parâmetro idcol inclui uma coluna ( .id ) identificando a origem do dataframe contido na lista. O resultado seria algo como isto:

.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  

Aqui está outra maneira de fazer isso (apenas adicionando-o às respostas, porque reduce é uma ferramenta funcional muito eficaz que é frequentemente ignorada como substituta de loops. Neste caso em particular, nenhum deles é significativamente mais rápido que o do.call).

usando base R:

df <- Reduce(rbind, listOfDataFrames)

ou, usando o tidyverse:

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

Um visual atualizado para aqueles que querem comparar algumas das respostas recentes (eu queria comparar a solução purrr para dplyr). Basicamente eu combinei respostas de @TheVTM e @rmf.

Código:

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)

Informações da Sessão:

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

Versões do pacote:

> packageVersion("tidyverse")
[1]1.1.1> packageVersion("data.table")
[1]1.10.0




dataframe