Konversi daftar bingkai data menjadi satu bingkai data


336

Saya memiliki kode yang di satu tempat berakhir dengan daftar bingkai data yang saya benar-benar ingin konversi menjadi satu bingkai data besar.

Saya mendapat beberapa petunjuk dari pertanyaan sebelumnya yang mencoba melakukan sesuatu yang serupa tetapi lebih kompleks.

Berikut adalah contoh dari apa yang saya mulai dengan (ini sangat disederhanakan untuk ilustrasi):

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

Saya sedang menggunakan ini:

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

Lihat juga pertanyaan ini: stackoverflow.com/questions/2209258/…
Shane

27
The do.call("rbind", list)idiom adalah apa yang telah saya gunakan sebelumnya juga. Mengapa Anda membutuhkan inisial unlist?
Dirk Eddelbuettel

5
dapatkah seseorang menjelaskan kepada saya perbedaan antara do.call ("rbind", list) dan rbind (list) - mengapa outputnya tidak sama?
user6571411

1
@ user6571411 Karena do.call () tidak mengembalikan argumen satu per satu, tetapi menggunakan daftar untuk menyimpan argumen fungsi. Lihat https://www.stat.berkeley.edu/~s133/Docall.html
Marjolein Fokkema

Jawaban:


131

Gunakan bind_rows () dari paket dplyr:

bind_rows(list_of_dataframes, .id = "column_label")

5
Solusi bagus .id = "column_label"menambahkan nama baris unik berdasarkan nama elemen daftar.
Sibo Jiang

10
karena ini tahun 2018 dan dplyrmerupakan alat yang cepat dan kuat untuk digunakan, saya telah mengubah ini menjadi jawaban yang diterima. Tahun-tahun berlalu, mereka terbang!
JD Long

186

Satu opsi lain adalah menggunakan fungsi plyr:

df <- ldply(listOfDataFrames, data.frame)

Ini sedikit lebih lambat dari aslinya:

> 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

Dugaan saya adalah bahwa menggunakan do.call("rbind", ...)akan menjadi pendekatan tercepat yang akan Anda temukan kecuali Anda dapat melakukan sesuatu seperti (a) menggunakan matriks, bukan data.frame dan (b) mengalokasikan awal matriks akhir dan menetapkannya daripada menumbuhkannya .

Edit 1 :

Berdasarkan komentar Hadley, inilah versi terbaru rbind.filldari CRAN:

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

Ini lebih mudah daripada rbind, dan sedikit lebih cepat (timing ini bertahan lebih dari beberapa kali). Dan sejauh yang saya mengerti, versi plyron github bahkan lebih cepat dari ini.


28
rbind.fill dalam versi terbaru plyr jauh lebih cepat daripada do.call dan rbind
hadley

1
menarik. bagi saya rbind.fill adalah yang tercepat. Cukup aneh, do.call / rbind tidak mengembalikan TRUE yang identik, bahkan jika saya tidak dapat menemukan perbedaan. Dua lainnya sama tetapi plyr lebih lambat.
Matt Bannert

I()dapat menggantikan panggilan data.frameAndaldply
baptiste

4
ada juga yang melt.listmembentuk kembali (2)
baptiste

do.call(function(...) rbind(..., make.row.names=F), df)berguna jika Anda tidak ingin nama unik unik yang dihasilkan secara otomatis.
smci

111

Untuk tujuan kelengkapan, saya pikir jawaban untuk pertanyaan ini memerlukan pembaruan. "Dugaan saya adalah bahwa menggunakan do.call("rbind", ...)akan menjadi pendekatan tercepat yang akan Anda temukan ..." Itu mungkin benar untuk Mei 2010 dan beberapa waktu setelahnya, tetapi pada sekitar Sep 2011 fungsi baru rbindlistdiperkenalkan dalam data.tablepaket versi 1.8.2 , dengan komentar bahwa "Ini melakukan hal yang sama do.call("rbind",l), tetapi jauh lebih cepat". Seberapa cepat?

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
Terima kasih banyak untuk ini - saya mencabut rambut saya karena set data saya menjadi terlalu besar untuk ldplysekelompok frame data yang panjang dan cair. Bagaimanapun, saya mendapat speedup yang luar biasa dengan menggunakan rbindlistsaran Anda .
KarateSnowMachine

11
Dan satu lagi untuk kelengkapan: dplyr::rbind_all(listOfDataFrames)akan melakukan trik juga.
andyteucher

2
apakah ada yang setara dengan rbindlisttetapi yang menambahkan frame data dengan kolom? sesuatu seperti cbindlist?
rafa.pereira

2
@ rafa.pereira Ada permintaan fitur terbaru: add function cbindlist
Henrik

Saya juga mencabut rambut saya karena do.call()sudah berjalan di daftar bingkai data selama 18 jam, dan masih belum selesai, terima kasih !!!
Graeme Frost

74

bind-plot

Kode:

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)

Sidang:

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 : Jalankan kembali 31-Jan-2018. Berlari di komputer yang sama. Versi paket baru. Menambahkan benih untuk pecinta benih.

masukkan deskripsi gambar di sini

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 : Jalankan kembali 06-Agustus-2019.

masukkan deskripsi gambar di sini

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
Ini jawaban yang bagus. Saya menjalankan hal yang sama (OS yang sama, paket yang sama, pengacakan berbeda karena Anda tidak set.seed) tetapi melihat beberapa perbedaan dalam kinerja kasus terburuk. rbindlistbenar-benar memiliki kasus terburuk terbaik serta kasus khas terbaik dalam hasil saya
C8H10N4O2

48

Ada juga bind_rows(x, ...)di 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

secara teknis Anda tidak perlu as.data.frame - semua yang dilakukan membuatnya menjadi data.frame secara eksklusif, tidak seperti table_df (dari deplyr)
user1617979

14

Inilah cara lain yang dapat dilakukan (hanya menambahkannya ke jawaban karena reducemerupakan alat fungsional yang sangat efektif yang sering diabaikan sebagai pengganti loop. Dalam kasus khusus ini, tak satu pun dari ini secara signifikan lebih cepat daripada do.call)

menggunakan basis R:

df <- Reduce(rbind, listOfDataFrames)

atau, menggunakan tidyverse:

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

11

Bagaimana seharusnya dilakukan di rapi:

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

3
Mengapa Anda menggunakan mapjika bind_rowsbisa mengambil daftar dataframe?
lihat24

9

Visual yang diperbarui untuk mereka yang ingin membandingkan beberapa jawaban terakhir (saya ingin membandingkan solusi purrr dengan dplyr). Pada dasarnya saya menggabungkan jawaban dari @TheVTM dan @rmf.

masukkan deskripsi gambar di sini

Kode:

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)

Info Sesi:

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

Versi Paket:

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

7

Satu-satunya hal yang data.tablehilang dengan solusi adalah kolom pengidentifikasi untuk mengetahui dari mana dataframe dalam daftar data berasal.

Sesuatu seperti ini:

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

The idcolparameter menambahkan kolom ( .id) mengidentifikasi asal dataframe yang terkandung dalam daftar. Hasilnya akan terlihat seperti ini:

.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  
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.