Chuyển đổi danh sách các khung dữ liệu thành một khung dữ liệu


336

Tôi có mã ở một nơi kết thúc với một danh sách các khung dữ liệu mà tôi thực sự muốn chuyển đổi thành một khung dữ liệu lớn duy nhất.

Tôi đã nhận được một số gợi ý từ một câu hỏi trước đó đang cố gắng làm điều gì đó tương tự nhưng phức tạp hơn.

Đây là một ví dụ về những gì tôi đang bắt đầu (điều này được đơn giản hóa để minh họa):

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

Tôi hiện đang sử dụng này:

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


27
Thành do.call("rbind", list)ngữ là những gì tôi đã sử dụng trước đây là tốt. Tại sao bạn cần ban đầu unlist?
Dirk Eddelbuettel

5
ai đó có thể giải thích cho tôi sự khác biệt giữa do.call ("rbind", list) và rbind (list) - tại sao các đầu ra không giống nhau?
dùng6571411

1
@ user6571411 Vì do.call () không trả về từng đối số một mà sử dụng danh sách để giữ các đối số của hàm. Xem https
//www.stat.ber siêu.edu / ~ s133 / Docall.html

Câu trả lời:


130

Sử dụng bind_rows () từ gói dplyr:

bind_rows(list_of_dataframes, .id = "column_label")

5
Giải pháp tốt đẹp. .id = "column_label"thêm tên hàng duy nhất dựa trên tên thành phần danh sách.
Sibo Jiang

10
kể từ năm 2018 và dplyrvừa nhanh, vừa là công cụ vững chắc để sử dụng, tôi đã thay đổi câu trả lời này thành câu trả lời được chấp nhận. Năm tháng, họ bay qua!
JD Long

186

Một tùy chọn khác là sử dụng hàm plyr:

df <- ldply(listOfDataFrames, data.frame)

Đây là một chút chậm hơn so với bản gốc:

> 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

Tôi đoán là sử dụng do.call("rbind", ...)sẽ là cách tiếp cận nhanh nhất mà bạn sẽ tìm thấy trừ khi bạn có thể làm một cái gì đó như (a) sử dụng ma trận thay vì data.frames và (b) phân bổ ma trận cuối cùng và gán cho nó thay vì phát triển nó .

Chỉnh sửa 1 :

Dựa trên nhận xét của Hadley, đây là phiên bản mới nhất của rbind.fillCRAN:

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

Điều này dễ dàng hơn so với rbind, và nhanh hơn một chút (những thời gian này giữ nhiều lần chạy). Và theo như tôi hiểu thì phiên bản plyrtrên github thậm chí còn nhanh hơn thế này.


28
rbind.fill trong phiên bản mới nhất của plyr nhanh hơn đáng kể so với do.call và rbind
hadley

1
hấp dẫn. Đối với tôi rbind.fill là nhanh nhất. Thật kỳ lạ, do.call / rbind đã không trả lại TRUE giống hệt nhau, ngay cả khi tôi không thể tìm thấy sự khác biệt. Hai cái kia bằng nhau nhưng plyr chậm hơn.
Matt Bannert

I()có thể thay thế data.frametrong ldplycuộc gọi của bạn
baptiste

4
cũng có melt.listtrong việc định hình lại (2)
baptiste

do.call(function(...) rbind(..., make.row.names=F), df)là hữu ích nếu bạn không muốn các tên miền độc đáo được tạo tự động.
smci 16/03/18

111

Với mục đích hoàn thiện, tôi nghĩ rằng câu trả lời cho câu hỏi này cần phải cập nhật. "Tôi đoán là sử dụng do.call("rbind", ...)sẽ là cách tiếp cận nhanh nhất mà bạn sẽ tìm thấy ..." Có lẽ đúng vào tháng 5 năm 2010 và một thời gian sau, nhưng vào khoảng tháng 9 năm 2011, một chức năng mới rbindlistđã được giới thiệu trong data.tablephiên bản gói 1.8.2. , với một nhận xét rằng "Điều này không giống như do.call("rbind",l), nhưng nhanh hơn nhiều". Nhanh hơn bao nhiêu?

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

3
Cảm ơn bạn rất nhiều vì điều này - Tôi đã nhổ tóc vì bộ dữ liệu của tôi đang trở nên quá lớn để lấy ldplymột loạt các khung dữ liệu dài, nóng chảy. Dù sao, tôi đã tăng tốc đáng kinh ngạc bằng cách sử dụng rbindlistđề xuất của bạn .
KarateSnowMachine

11
Và một điều nữa cho sự hoàn chỉnh: dplyr::rbind_all(listOfDataFrames)cũng sẽ thực hiện các mẹo.
andyteucher

2
Có tương đương với rbindlistnhưng nối các khung dữ liệu theo cột không? một cái gì đó giống như một cbindlist?
rafa.pereira

2
@ rafa.pereira Có một yêu cầu tính năng gần đây: thêm chức năng cbindlist
Henrik

Tôi cũng đã nhổ tóc vì do.call()đã chạy trên một danh sách các khung dữ liệu trong 18 giờ, và vẫn chưa hoàn thành, cảm ơn bạn !!!
Graeme Frost

74

cốt truyện

Mã số:

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)

Phiên:

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

CẬP NHẬT : Chạy lại ngày 31 tháng 1 năm 2018. Ran trên cùng một máy tính. Phiên bản mới của gói. Thêm hạt giống cho những người yêu thích hạt giống.

nhập mô tả hình ảnh ở đây

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

CẬP NHẬT : Chạy lại 06 tháng 8 năm 2019.

nhập mô tả hình ảnh ở đây

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

2
Đây là một câu trả lời tuyệt vời. Tôi đã chạy cùng một thứ (cùng một hệ điều hành, cùng các gói, ngẫu nhiên khác nhau vì bạn không set.seed) nhưng thấy một số khác biệt về hiệu suất trong trường hợp xấu nhất. rbindlistthực sự có trường hợp xấu nhất cũng như trường hợp điển hình nhất trong kết quả của tôi
C8H10N4O2

48

Ngoài ra còn có bind_rows(x, ...)trong 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

về mặt kỹ thuật, bạn không cần as.data.frame - tất cả những gì nó làm cho nó chỉ là một data.frame, trái ngược với bảng_df (từ deplyr)
user1617979

14

Đây là một cách khác có thể được thực hiện (chỉ cần thêm nó vào câu trả lời vì reduce là một công cụ chức năng rất hiệu quả thường bị bỏ qua như là một thay thế cho các vòng lặp. Trong trường hợp cụ thể này, không có cách nào nhanh hơn đáng kể so với do.call)

sử dụng cơ sở R:

df <- Reduce(rbind, listOfDataFrames)

hoặc, sử dụng tidyverse:

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

11

Làm thế nào nó nên được thực hiện trong ngăn nắp:

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

3
Tại sao bạn sẽ sử dụng mapnếu bind_rowscó thể lấy một danh sách các dataframes?
xem

9

Một hình ảnh cập nhật cho những người muốn so sánh một số câu trả lời gần đây (tôi muốn so sánh purrr với giải pháp dplyr). Về cơ bản tôi đã kết hợp câu trả lời từ @TheVTM và @rmf.

nhập mô tả hình ảnh ở đây

Mã số:

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)

Thông tin phiên:

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

Phiên bản gói:

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

7

Điều duy nhất mà các giải pháp data.tablebị thiếu là cột định danh để biết từ đó khung dữ liệu trong danh sách mà dữ liệu đến từ đâu.

Một cái gì đó như thế này:

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

Các idcoltham số bổ sung thêm một cột ( .id) xác định nguồn gốc của dataframe chứa trong danh sách. Kết quả sẽ trông giống như thế này:

.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  
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.