データフレームのリストを1つのデータフレームに変換する


336

ある場所で、実際に単一のビッグデータフレームに変換したいデータフレームのリストが作成されるコードがあります。

似たような、しかしもっと複雑なことをしようとしていた以前の質問から、いくつかの指針を得ました。

これは私が始めているものの例です(これは説明のために大幅に簡略化されています):

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)

また、この質問を参照してください。stackoverflow.com/questions/2209258/...
シェーン

27
do.call("rbind", list)イディオムは私も前に使用したものです。なぜイニシャルが必要なのunlistですか?
Dirk Eddelbuettel

5
誰かがdo.call( "rbind"、list)とrbind(list)の違いを私に説明できますか-出力が同じではないのはなぜですか?
user6571411

1
@ user6571411 do.call()は引数を1つずつ返すのではなく、リストを使用して関数の引数を保持するためです。https://www.stat.berkeley.edu/~s133/Docall.htmlを
Marjolein Fokkema 2018

回答:


130

dplyrパッケージのbind_rows()を使用します。

bind_rows(list_of_dataframes, .id = "column_label")

5
素晴らしい解決策。.id = "column_label"リスト要素名に基づいて一意の行名を追加します。
SIBO江

10
それdplyrは2018年であり、高速で使用するための確かなツールであるため、これを承認済みの回答に変更しました。年、彼らは飛んでいきます!
JD Long

186

もう1つのオプションは、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", ...)、(a)data.framesの代わりに行列を使用し、(b)最終的な行列を事前に割り当てて、それを拡張するのではなく割り当てる場合を除き、 。

編集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よりも簡単で、わずかに高速です(これらのタイミングは、複数の実行で保持されます)。そして、私が理解している限り、github のバージョンはplyrこれよりもさらに高速です。


28
最新バージョンのplyrのrbind.fillは、do.callやrbindよりもかなり高速です
ハドリー

1
面白い。私にとってはrbind.fillが最速でした。奇妙なことに、違いを見つけることができたとしても、do.call / rbindは同じTRUEを返しませんでした。他の2つは同等でしたが、プライが遅くなりました。
Matt Bannert、2010年

I()data.frameあなたのldply電話で置き換えることができます
baptiste 2013

4
またありますmelt.listリシェイプに(2)
バティスト

do.call(function(...) rbind(..., make.row.names=F), df)自動生成される一意の行名が不要な場合に便利です。
smci 2018年

111

完全を期すために、この質問への回答には更新が必要だと思いました。「私の推測では、使用するのdo.call("rbind", ...)が最も速いアプローチだと思います...」2010年5月以降はおそらくそうでしたが、2011年9月頃にパッケージバージョン1.8.2 rbindlistで新しい関数が導入されました。data.table、「これはと同じですが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

3
本当にありがとうございました-私の髪の毛を引き出していたのは、私のデータセットが大きくなりすぎてldply、溶けた長いデータフレームを束ねることができなかったからです。とにかく、私はあなたのrbindlist提案を使用して信じられないほどのスピードアップを得ました。
KarateSnowMachine 2013

11
もう1つ、完全dplyr::rbind_all(listOfDataFrames)を期すために、トリックも実行します。
andyteucher 14

2
同等のものはありrbindlistますが、列ごとにデータフレームを追加しますか?cbindlistのようなものですか?
rafa.pereira 2015

2
@ rafa.pereira最近の機能リクエストがあります: 関数cbindlistを追加します
Henrik

do.call()データフレームのリストを18時間実行していて、まだ終了していなかったので、髪も抜いていました。ありがとうございました!!!
Graeme Frost

74

バインドプロット

コード:

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

更新:2018年1月31日を再実行します。同じコンピュータで実行。パッケージの新しいバージョン。種子愛好家のための種子を追加しました。

ここに画像の説明を入力してください

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

更新:2019年8月6日を再実行します。

ここに画像の説明を入力してください

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
これは素晴らしい答えです。私は同じものを実行しましたが(同じOS、同じパッケージ、そうでないために異なるランダム化set.seed)、最悪の場合のパフォーマンスにいくつかの違いが見られました。 rbindlist私の結果では、実際に最高のワーストケースと最高の典型的なケースがありました
C8H10N4O2

48

にもbind_rows(x, ...)あり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

技術的に言えば、as.data.frameは必要ありません-table_df(deplyrから)とは対照的に、それだけでdata.frameになります
user1617979

14

これを行う別の方法を次に示します(回答に追加するだけなので、 reduceはです。これは非常に効果的な機能ツールであるため、ループの代わりとして見過ごされがちです。この特定のケースでは、どちらもdo.callよりも大幅に高速です)。

ベースRを使用:

df <- Reduce(rbind, listOfDataFrames)

または、tidyverseを使用します。

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

11

それがtidyverseでどのように行われるべきか:

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

3
データフレームのリストを取得できるmap場合、なぜ使用するのbind_rowsですか?
18

9

最近の回答のいくつかを比較したい人のための更新されたビジュアル(私はpurrrとdplyrのソリューションを比較したかった)。基本的に、@ TheVTMと@rmfの回答を組み合わせました。

ここに画像の説明を入力してください

コード:

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

7

ソリューションにdata.table欠けているのは、リストのどのデータフレームからデータが送信されているかを知るための識別子列だけです。

このようなもの:

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

この idcolパラメーターは.id、リストに含まれるデータフレームの起点を識別する列()を追加します。結果は次のようになります。

.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  
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.