2010-05-17 3 views
208

У меня есть код, который в одном месте заканчивается списком кадров данных, которые я действительно хочу преобразовать в один большой фрейм данных.Преобразование списка кадров данных в один фрейм данных

У меня есть указатели от earlier question, которые пытались сделать что-то подобное, но сложнее.

Вот пример того, что я начинаю с (это грубо упрощен для иллюстрации):

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

В настоящее время я использую это:

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

Смотрите также вопрос: http://stackoverflow.com/questions/2209258/merge-several-data-frames-into-one-data-frame-with-a-loop/2209371 – Shane

+12

Параметр 'делать .call ("rbind", list) 'idiom - это то, что я использовал и раньше. Зачем вам нужен начальный 'unlist'? –

+1

Шейн, я только что проделал то же самое испытание и поймал меня. Вы быстро;) –

ответ

157

Еще один вариант заключается в использовании plyr функция:

df <- ldply(listOfDataFrames, data.frame) 

Это немного медленнее, чем оригинал:

> 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 

Я думаю, что использование do.call("rbind", ...) будет самый быстрый подход, который вы найдете, если вы не можете сделать что-то подобное (а) используют матрицы, а не в data.frames и (б) предварительно выделить конечную матрицу и а не выращивать его.

Edit 1:

Основываясь на комментарий Хэдли, вот последняя версия rbind.fill от CRAN:

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

Это проще, чем rbind, и немного быстрее (эти тайминги держат в течение нескольких работает). И, насколько я понимаю, the version of plyr on github еще быстрее.

+21

rbind.fill в последней версии plyr значительно быстрее, чем do.call и rbind – hadley

+1

интересно. для меня rbind.fill был самым быстрым. Довольно странно, do.call/rbind не возвращал идентичный TRUE, даже если бы я мог найти разницу. Остальные два были равны, но plyr был медленнее. –

+0

'I()' может заменить 'data.frame' в вашем' ldply' вызове – baptiste

80

В целях полноты, я думал, что ответы на этот вопрос требуют обновления. «Я предполагаю, что использование do.call("rbind", ...) будет самым быстрым подходом, который вы найдете ...» Вероятно, это было в мае 2010 года и некоторое время спустя, но примерно в сентябре 2011 года появилась новая функция rbindlist в версии пакета data.table 1.8.2, с замечанием, что «Это делает то же самое, что и do.call("rbind",l), но намного быстрее». Насколько быстрее?

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 
+1

Большое вам спасибо за это - я вытягивал свои волосы, потому что мои данные наборы стали слишком большими для 'ldply'ing кучи длинных, расплавленных кадров данных.В любом случае, я получил невероятное ускорение, используя предложение 'rbindlist'. – KarateSnowMachine

+7

И еще один для полноты: 'dplyr :: rbind_all (listOfDataFrames)' также будет делать трюк. – andyteucher

+1

есть эквивалент 'rbindlist', но который добавляет кадры данных по столбцу? что-то вроде cbindlist? –

35

Существует также в dplyrbind_rows(x, ...).

> 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 
+0

технически говоря вам не нужен as.data.frame - все, что делает это, делает его исключительно data.frame, в отличие от table_df (от deplyr) – user1617979

30

bind-plot

Код:

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) 

Сессия:

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: Перекладка 31-Jan-2018. Выйти на том же компьютере. Новые версии пакетов. Добавлены семена для любителей семян.

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’ 
+1

Это отличный ответ. Я запускал то же самое (одна и та же ОС, одни и те же пакеты, различная рандомизация, потому что вы не «set.seed»), но видели некоторые отличия в худшем случае. 'rbindlist' на самом деле имел лучший худший случай, а также лучший типичный случай в моих результатах. – C8H10N4O2

6

Как это должно быть сделано в tidyverse:

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

' df_dplyr_purrr', если вы хотите быть tidyverse purist ... – yeedle

+0

@yeedle Спасибо - почти пусть это один промах;) – Nick

5

Вот еще один способ это можно сделать (просто добавить его к ответам, потому что reduce является очень эффективным функциональным инструментом которые часто игнорируются как замена петель. В этом конкретном случае ни один из них не намного быстрее, чем do.call)

с использованием базы R:

df <- Reduce(rbind, listOfDataFrames) 

или, используя tidyverse:

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

Единственное, что решения с data.table отсутствуют является столбец идентификатора, чтобы узнать, из которого dataframe в списке данные приходящий из.

Что-то вроде этого:

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

Параметр idcol добавляет столбец (.id), идентифицирующий происхождение dataframe, содержащихся в списке. Результат будет выглядеть примерно так:

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

Обновленный визуальный для тех, кто хочет, чтобы сравнить некоторые из последних ответов (я хотел сравнить purrr для dplyr решения). В основном я комбинировал ответы от @TheVTM и @rmf.

enter image description here

Код:

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) 

информация Сессия:

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 

Варианты упаковки:

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

Используйте bind_rows() из пакета dplyr:

bind_rows(list_of_dataframes, .id = "column_label") 
Смежные вопросы