Veri çerçeveleri listesini tek bir veri çerçevesine dönüştürme


336

Bir yerde gerçekten tek bir büyük veri çerçevesine dönüştürmek istediğiniz veri çerçeveleri listesi ile biter kodu var.

Daha önceki bir sorudan benzer ama daha karmaşık bir şey yapmaya çalışan bazı işaretçiler aldım .

İşte ne ile başlıyorum bir örnek (bu gösterim için son derece basitleştirilmiş):

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

Şu anda kullanıyorum:

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

Ayrıca bu soruya bakın: stackoverflow.com/questions/2209258/…
Shane

27
do.call("rbind", list)Deyim Ben de daha önce kullanmış budur. İlkine neden ihtiyacınız var unlist?
Dirk Eddelbuettel

5
Birisi bana do.call ("rbind", list) ve rbind (list) arasındaki farkı açıklayabilir mi - çıktılar neden aynı değil?
user6571411

1
@ user6571411 Çünkü do.call (), bağımsız değişkenleri tek tek döndürmez, ancak işlevin bağımsız değişkenlerini tutmak için bir liste kullanır. Bkz. Https://www.stat.berkeley.edu/~s133/Docall.html
Marjolein Fokkema

Yanıtlar:


131

Dplyr paketinden bind_rows () kullanın:

bind_rows(list_of_dataframes, .id = "column_label")

5
Güzel çözüm. .id = "column_label"liste öğesi adlarını temel alarak benzersiz satır adlarını ekler.
Sibo Jiang

10
2018 olduğundan ve dplyrhem hızlı hem de kullanımı sağlam bir araç olduğundan, bunu kabul edilen cevaba değiştirdim. Yıllar geçiyorlar!
JD Long

186

Başka bir seçenek de bir plyr işlevi kullanmaktır:

df <- ldply(listOfDataFrames, data.frame)

Bu orijinalden biraz daha yavaş:

> 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

Benim tahminim, do.call("rbind", ...)(a) bir veri yerine bir matris kullanmak ve (b) son matrisi önceden konumlandırmak ve büyütmek yerine atamak gibi bir şey yapmadıkça , kullanmanın , bulabileceğiniz en hızlı yaklaşım olacağıdır. .

Düzenleme 1 :

Hadley'nin yorumuna dayanarak, CRAN'ın en son sürümü rbind.fill:

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

Bu, geri sarmaktan daha kolay ve marjinal olarak daha hızlıdır (bu zamanlamalar birden fazla çalıştırmada geçerlidir). Ve anladığım kadarıyla, github'ın versiyonuplyr bundan daha hızlı.


28
plyr'in en son sürümündeki rbind.fill, do.call ve rbind'den oldukça hızlıdır
hadley

1
ilginç. benim için rbind.fill en hızlı oldu. Ne kadar garip, do.call / rbind, ne bir fark bulabilirsem bile, aynı DOĞRU döndürmedi. Diğer ikisi eşitti ama plyr daha yavaştı.
Matt Bannert

I()yerini alabilir data.framesizin de ldplyçağrı
baptiste

4
orada da melt.listyeniden şekillendirme (2)
baptiste

do.call(function(...) rbind(..., make.row.names=F), df)otomatik olarak oluşturulan benzersiz rownames'i istemiyorsanız yararlıdır.
smci

111

Tamlık amacıyla, bu sorunun cevaplarının bir güncelleme gerektirdiğini düşündüm. "Tahminimce kullanmak do.call("rbind", ...), bulacağınız en hızlı yaklaşım olacak ..." Muhtemelen Mayıs 2010 ve bir süre sonra doğruydu, ancak Eylül 2011'de paket sürümü 1.8.2'de yeni bir işlev rbindlisttanıtıldı data.table, "Bu aynı do.call("rbind",l), ama çok daha hızlı" yapar. Ne kadar hızlı?

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
Bunun için çok teşekkür ederim - saçlarımı dışarı çekiyordum çünkü veri kümelerim ldplyuzun, erimiş veri çerçeveleri oluşturmak için çok büyük hale geliyordu . Her neyse, rbindlistönerinizi kullanarak inanılmaz bir hız kazandım .
KarateSnowMachine

11
Ve tamlık için bir tane daha: dplyr::rbind_all(listOfDataFrames)hile de yapacak.
andyteucher

2
eşdeğeri var rbindlistancak veri karelerini sütuna göre ekliyor mu? cbindlist gibi bir şey mi?
rafa.pereira

2
@ rafa.pereira Yeni bir özellik isteği var: fonksiyon ekleme cbindlist
Henrik

Ben de saçlarımı çekiyordum çünkü do.call()18 saat boyunca veri çerçeveleri listesinde çalışıyordum ve hala bitmemiştim, teşekkür ederim !!!
Graeme Frost

74

bağlama-arsa

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)

Oturum, toplantı, celse:

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

GÜNCELLEME : 31 Ocak 2018'i tekrar çalıştırın. Aynı bilgisayarda koştu. Yeni paket sürümleri. Tohum sevenler için tohum eklendi.

resim açıklamasını buraya girin

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

GÜNCELLEME : 06-Ağu-2019'u tekrar çalıştır.

resim açıklamasını buraya girin

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
Bu harika bir cevap. Aynı şeyi çalıştırdım (aynı işletim sistemi, aynı paketler, farklı rasgeleleştirme çünkü yapmıyorsunuz set.seed) ama en kötü performansta bazı farklılıklar gördüm. rbindlistaslında sonuçlarımda en kötü durumun yanı sıra en iyi tipik durum da vardı
C8H10N4O2

48

İçinde de bind_rows(x, ...)var 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

teknik olarak konuşursanız as.data.frame'e ihtiyacınız yoktur - tüm bunları tek başına bir data.frame yapar, aksine bir table_df (deplyr'den) yerine
user1617979

14

İşte bunun başka bir yolu var (sadece cevaplara eklemek çünkü reduce döngüler yerine genellikle göz ardı edilen çok etkili bir işlevsel araçtır. Bu özel durumda, bunların hiçbiri do.call'dan önemli ölçüde daha hızlı değildir)

taban R kullanarak:

df <- Reduce(rbind, listOfDataFrames)

veya tidyverse kullanarak:

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

11

Düzenli olarak nasıl yapılmalı:

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

3
Neden kullanmak istiyorsunuz mapeğer bind_rowsdataframes bir listesini alabilir?
see24

9

Son cevapların bazılarını karşılaştırmak isteyenler için güncellenmiş bir görsel (Purrr-dplyr çözümünü karşılaştırmak istedim). Temelde @TheVTM ve @rmf'den cevapları birleştirdim.

resim açıklamasını buraya girin

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)

Oturum Bilgisi:

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

Paket Sürümleri:

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

7

Çözüm olan tek şey data.table eksik olduğu , listedeki verilerin hangi veri çerçevesinden geldiğini bilmek için tanımlayıcı sütundur.

Bunun gibi bir şey:

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

idcolParametresi, bir kolonu (ekler .idlistede yer alan dataframe menşeini tanımlayan). Sonuç böyle bir şeye benzeyecektir:

.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  
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.