Konwertuj listę ramek danych na jedną ramkę danych

Mam kod, który w jednym miejscu kończy się listą ramek danych, które naprawdę chcę przekonwertować na pojedynczą dużą ramkę danych.

Dostałem kilka wskazówek z wcześniejszego pytania , które próbowało zrobić coś podobnego, ale bardziej złożonego.

Oto przykład tego, od czego zaczynam (jest to rażąco uproszczone dla ilustracji):

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

Obecnie używam tego:

  df <- do.call("rbind", listOfDataFrames)
Author: Community, 2010-05-17

9 answers

Inną opcją jest użycie funkcji plyr:

df <- ldply(listOfDataFrames, data.frame)

To jest trochę wolniejsze od oryginału:

> 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

Domyślam się, że użycie do.call("rbind", ...) będzie najszybszym podejściem, jakie znajdziesz, chyba że możesz zrobić coś w rodzaju (a) użyć macierzy zamiast danych.ramek i (b) prealokują ostateczną macierz i przypisują do niej zamiast ją powiększać.

Edytuj 1:

Na podstawie komentarza Hadleya, oto najnowsza wersja rbind.fill z CRAN:

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

Jest to łatwiejsze niż rbind i nieznacznie szybsze (te timingi utrzymują się na wielu biegach). I o ile to Rozumiem, Wersja plyr na GitHubie jest jeszcze szybsza.

 171
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-18 13:54:06

Dla kompletności, myślałem, że odpowiedzi na to pytanie wymaga aktualizacji. "Domyślam się, że użycie do.call("rbind", ...) będzie najszybszym podejściem, jakie znajdziesz..."Prawdopodobnie było to prawdą w maju 2010 roku i jakiś czas później, ale około września 2011 roku nowa funkcja rbindlist została wprowadzona w pakiecie data.table w wersji 1.8.2, z uwagą, że "robi to to samo co do.call("rbind",l), ale znacznie szybciej". O ile szybciej?

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
 89
Author: andrekos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 21:06:23

Istnieje również bind_rows(x, ...) w 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
 43
Author: TheVTM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-29 00:32:15

bind-plot

Kod:

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)

Sesja:

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 : Przekierowanie 31-Sty-2018. Działał na tym samym komputerze. Nowe wersje pakietów. Dodano nasiona dla miłośników nasion.

Tutaj wpisz opis obrazka

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’
 43
Author: rmf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 15:10:11

Użyj bind_rows () z pakietu dplyr:

bind_rows(list_of_dataframes, .id = "column_label")
 10
Author: joeklieg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-27 20:05:08

Jak to powinno być zrobione w tidyverse:

df.dplyr.purrr <- listOfDataFrames %>% map_df(bind_rows)
 9
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 13:27:19

Jest inny sposób, aby to zrobić (po prostu dodanie go do odpowiedzi, ponieważ reduce jest bardzo skutecznym narzędziem funkcjonalnym, które jest często pomijane jako zamiennik pętli. W tym konkretnym przypadku, żadne z nich nie są znacznie szybsze niż zrobić.call)

Używając bazy R:

df <- Reduce(rbind, listOfDataFrames)

Lub, używając tidyverse:

library(tidyverse) # or, library(dplyr); library(purrr)
df <- listOfDataFrames %>% reduce(bind_rows)
 9
Author: yeedle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-17 02:38:23

Jedyną rzeczą, której brakuje rozwiązaniom z data.table, jest kolumna identifier, z której ramki danych na liście pochodzą dane.

Coś takiego:

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

Parametr idcol dodaje kolumnę (.id) określającą pochodzenie ramki danych zawartej w liście. Wynik wyglądałby tak:

.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  
 5
Author: f0nzie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 15:30:44

Zaktualizowany obraz dla tych, którzy chcą porównać niektóre z ostatnich odpowiedzi (chciałem porównać purrr do rozwiązania dplyr). W zasadzie połączyłem odpowiedzi z @TheVTM i @RMF.

Tutaj wpisz opis obrazka

Kod:

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)

Informacje O Sesji:

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

Wersje Pakietu:

> packageVersion("tidyverse")
[1] ‘1.1.1’
> packageVersion("data.table")
[1] ‘1.10.0’
 4
Author: Nova,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 17:04:43