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


513

ネストされたデータのリストがあります。その長さは132で、各項目は長さ20のリストです。この構造を、132行と20列のデータを持つデータフレームにすばやく変換する方法はありますか?

処理するサンプルデータを次に示します。

l <- replicate(
  132,
  list(sample(letters, 20)),
  simplify = FALSE
)

では、各リスト要素をdata.frameのデータの行として必要ですか?
Joshua Ulrich

2
@RichieCotton正しい例ではありません。「各項目があり、リストの長さ20の」あなたは、各項目は以下のいずれかの要素のリストで得たベクトルの長さ20の
マレク

1
後期パーティーに、私は誰も言及見なかったこの私が(私が何を探していたもののために)非常に便利だと思いました。
mflo-ByeSE 2017年


回答:


390

リストのリストが呼び出されていると仮定しますl

df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=T))

上記はすべての文字列を因子に変換します。これを回避するには、data.frame()呼び出しにパラメーターを追加します。

df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)

109
データがすべて同じタイプではない場合は、ここで注意してください。マトリックスを通過するということは、すべてのデータが共通の型に強制変換されることを意味します。つまり、1列の文字データと1列の数値データがある場合、数値データは、matrix()によって文字列に強制変換され、両方がdata.frame()によって因数分解されます。
Ian Sudbery 2013年

リストに欠損値がある場合にこれを行う、またはデータフレームにNAを含めるための最良の方法は何ですか?
デイブ

1
@Dave:私の作品...こちらをご覧くださいr-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
ニコ

4
文字データ型がある場合も注意してください-data.frameはそれを因子に変換します。
アレックスブラウン

4
@nicoリスト要素の名前をdfのcolnamesまたはrownamesとして保持する方法はありますか?
N.Varela

472

rbind

do.call(rbind.data.frame, your_list)

編集:前のバージョンのリターンdata.framelist代わりにベクトルのさん(@IanSudberyはコメントで指摘したように)。


5
なぜこれは機能するがrbind(your_list)1x32リストマトリックスを返すのですか?
eykanal 2011

26
@eykanalはのdo.call要素をyour_listとして引数に渡しますrbind。と同等ですrbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]])
Marek、2011

2
この方法はnullの状況に悩まされています。
フランクワン

3
@FrankWANGしかし、このメソッドは状況をnullにするようには設計されていません。your_list同じサイズのベクターが含まれている必要があります。NULL長さが0なので、失敗するはずです。
Marek

12
このメソッドは正しいオブジェクトを返すようですが、オブジェクトを調べると、列がベクトルではなくリストであることがわかります。これは、予期しない場合に問題を引き起こす可能性があります。
Ian Sudbery 2013年

135

plyrパッケージを使用できます。たとえば、フォームのネストされたリスト

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
      , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
      , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
      , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
      )

長さは4になり、各リストにlは長さ3の別のリストが含まれています。

  library (plyr)
  df <- ldply (l, data.frame)

@Marekおよび@nicoと同じ結果が得られるはずです。


8
すばらしい答えです。それがどのように機能するかを少し説明できますか?リストのエントリごとにデータフレームを返すだけですか?
Michael Barton

13
最高の答えをイムホ。正直なdata.frameを返します。すべてのデータ型(文字、数値など)は正しく変換されます。リストに異なるデータ型がある場合、それらはすべてmatrixアプローチによって特性に変換されます。
Roah 2013

1
ここで提供されるサンプルは、質問によって提供されるものではありません。元のデータセットに対するこの回答の結果は正しくありません。
MySchizoBuddy 2015

私にとっては素晴らしい作品です!そして、結果のデータフレームの列の名前が設定されます!Tx
bAN 2016

プライアーはマルチコアですか?または、mclapplyで使用するラッププライバージョンはありますか?
Garglesoap

103

data.frame(t(sapply(mylistlist,c)))

sapplyそれを行列に変換します。 data.frameマトリックスをデータフレームに変換します。


19
正解です。他のソリューションでは、タイプ/列名が正しくなりません。ありがとうございました!
d_a_c321 2014年

1
cここで、リストのデータの1つのインスタンスとしてどのような役割を果たしますか?ああ待って、連結関数のcですよね?@mnelのcの使用法と混同される。また、@ dchandlerにも同意します。列名を正しく取得することは、私のユースケースでの貴重なニーズでした。素晴らしいソリューション。
jxramos 2014年

その権利-標準のc関数; から?cCombine Values into a Vector or List
アレックスブラウン

1
質問に提供されているサンプルデータでは動作しません
MySchizoBuddy

3
これはリストのdata.frameを生成しませんか?
カール・

69

あなたのリストがと呼ばれていると仮定しL

data.frame(Reduce(rbind, L))

2
良いですね!@Alex Brownのソリューションとあなたのソリューションとの違いが1つあります。ルートをたどると、何らかの理由で次の警告メッセージが表示されます。 :3,4-> row.names NOT used '
jxramos

とても良い!!ここでの私の仕事:stackoverflow.com/questions/32996321/...
アナスタシアPupynina

2
リストに要素が1つしかない場合を除き、適切に機能data.frame(Reduce(rbind, list(c('col1','col2'))))します。2行1列のデータフレームを生成します(1行2列が必要です)
レッドピース

61

パッケージには、の超高速実装であるdata.table関数rbindlistがありdo.call(rbind, list(...))ます。

のリスト listsdata.framesまたはdata.tables 入力として使用できます。

library(data.table)
ll <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
  , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
  , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
  , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
  )

DT <- rbindlist(ll)

これはdata.tableからの継承を返しますdata.frame

あなたが本当に data.frameに変換したい、as.data.frame(DT)


最後の行について、setDF参照によりdata.frameに戻ることができるようになりました。
フランク

1
30Kのアイテムとの私のリストについては、rbindlistはldplyよりも高速道を働いた
tallharish

35

tibbleパッケージには、機能を有しているenframe()ネストされた強制することでこの問題を解決しlist、入れ子にオブジェクトをtibble(「整頓」のデータフレームを)オブジェクト。以下は、Rからのデータサイエンスの簡単な例です。

x <- list(
    a = 1:5,
    b = 3:4, 
    c = 5:6
) 

df <- enframe(x)
df
#> # A tibble: 3 × 2
#>    name     value
#>   <chr>    <list>
#>    1     a <int [5]>
#>    2     b <int [2]>
#>    3     c <int [2]>

リストにはいくつかのネストlがあるため、unlist(recursive = FALSE)を使用して不要なネストを削除し、単一の階層リストを取得してに渡すことができenframe()ます。tidyr::unnest()2つの列(1つはグループ用、もう1つはグループnameでの観測用value)を持つ単一レベルの「整然とした」データフレームに出力をネスト解除するために使用します。幅の広い列が必要な場合add_column()は、値の順序を132回繰り返すだけで列を追加できます。次にspread()、値だけです。


library(tidyverse)

l <- replicate(
    132,
    list(sample(letters, 20)),
    simplify = FALSE
)

l_tib <- l %>% 
    unlist(recursive = FALSE) %>% 
    enframe() %>% 
    unnest()
l_tib
#> # A tibble: 2,640 x 2
#>     name value
#>    <int> <chr>
#> 1      1     d
#> 2      1     z
#> 3      1     l
#> 4      1     b
#> 5      1     i
#> 6      1     j
#> 7      1     g
#> 8      1     w
#> 9      1     r
#> 10     1     p
#> # ... with 2,630 more rows

l_tib_spread <- l_tib %>%
    add_column(index = rep(1:20, 132)) %>%
    spread(key = index, value = value)
l_tib_spread
#> # A tibble: 132 x 21
#>     name   `1`   `2`   `3`   `4`   `5`   `6`   `7`   `8`   `9`  `10`  `11`
#> *  <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1      1     d     z     l     b     i     j     g     w     r     p     y
#> 2      2     w     s     h     r     i     k     d     u     a     f     j
#> 3      3     r     v     q     s     m     u     j     p     f     a     i
#> 4      4     o     y     x     n     p     i     f     m     h     l     t
#> 5      5     p     w     v     d     k     a     l     r     j     q     n
#> 6      6     i     k     w     o     c     n     m     b     v     e     q
#> 7      7     c     d     m     i     u     o     e     z     v     g     p
#> 8      8     f     s     e     o     p     n     k     x     c     z     h
#> 9      9     d     g     o     h     x     i     c     y     t     f     j
#> 10    10     y     r     f     k     d     o     b     u     i     x     s
#> # ... with 122 more rows, and 9 more variables: `12` <chr>, `13` <chr>,
#> #   `14` <chr>, `15` <chr>, `16` <chr>, `17` <chr>, `18` <chr>,
#> #   `19` <chr>, `20` <chr>

OPの引用:「この構造を132行と20列のデータを持つデータフレームに変換する簡単な方法はありますか?」だから、多分あなたはスプレッドステップか何かが必要です。
フランク

1
ああ、そうです、分散できるインデックス列が必要です。まもなく更新します。
Matt Dancho 2017

17

リストの構造に応じて、tidyverse長さが等しくないリストでうまく機能するいくつかのオプションがあります。

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
        , b = list(var.1 = 4, var.2 = 5)
        , c = list(var.1 = 7, var.3 = 9)
        , d = list(var.1 = 10, var.2 = 11, var.3 = NA))

df <- dplyr::bind_rows(l)
df <- purrr::map_df(l, dplyr::bind_rows)
df <- purrr::map_df(l, ~.x)

# all create the same data frame:
# A tibble: 4 x 3
  var.1 var.2 var.3
  <dbl> <dbl> <dbl>
1     1     2     3
2     4     5    NA
3     7    NA     9
4    10    11    NA

ベクトルとデータフレームを混在させることもできます。

library(dplyr)
bind_rows(
  list(a = 1, b = 2),
  data_frame(a = 3:4, b = 5:6),
  c(a = 7)
)

# A tibble: 4 x 2
      a     b
  <dbl> <dbl>
1     1     2
2     3     5
3     4     6
4     7    NA

このdplyr :: bind_rows関数は、JSONで作成されたリストを処理するのが難しい場合でも、うまく機能します。JSONから驚くほどクリーンなデータフレームへ。いいね。
GGAnderson

@sbha df <-purrr :: map_df(l、〜.x)を使用しようとしましたが、機能していないようです。エラーメッセージ:エラー:列X2を整数から文字に変換できません
Jolin

16

Reshape2は、上記のplyrの例と同じ出力を生成します。

library(reshape2)
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
          , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
          , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
          , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
l <- melt(l)
dcast(l, L1 ~ L2)

収量:

  L1 var.1 var.2 var.3
1  a     1     2     3
2  b     4     5     6
3  c     7     8     9
4  d    10    11    12

ピクセルがほとんどなくなった場合は、recast()を使用してこれをすべて1行で実行できます。


12

この方法では、tidyverseパッケージ(purrr)を。

リスト:

x <- as.list(mtcars)

データフレームに変換する(tibbleより具体的に):

library(purrr)
map_df(x, ~.x)

10

@Marekの答えを拡張してください:文字列が因子に変換されるのを避けたいのであれば、効率は問題ではありません

do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))

10

ネストされたJSONから取得されたリストのような3つ以上のレベルを持つ深くネストされたリストの一般的な場合:

{
"2015": {
  "spain": {"population": 43, "GNP": 9},
  "sweden": {"population": 7, "GNP": 6}},
"2016": {
  "spain": {"population": 45, "GNP": 10},
  "sweden": {"population": 9, "GNP": 8}}
}

melt()最初にネストされたリストをトール形式に変換する方法を検討してください。

myjson <- jsonlite:fromJSON(file("test.json"))
tall <- reshape2::melt(myjson)[, c("L1", "L2", "L3", "value")]
    L1     L2         L3 value
1 2015  spain population    43
2 2015  spain        GNP     9
3 2015 sweden population     7
4 2015 sweden        GNP     6
5 2016  spain population    45
6 2016  spain        GNP    10
7 2016 sweden population     9
8 2016 sweden        GNP     8

続いdcast()て、整然としたデータセットに再び拡大し、各変数が列を形成し、各観測値が行を形成します。

wide <- reshape2::dcast(tall, L1+L2~L3) 
# left side of the formula defines the rows/observations and the 
# right side defines the variables/measurements
    L1     L2 GNP population
1 2015  spain   9         43
2 2015 sweden   6          7
3 2016  spain  10         45
4 2016 sweden   8          9

9

この質問への回答のタイミングとともに、その他の回答: リストをデータフレームとしてキャストする最も効率的な方法は何ですか?

最も速い方法は、列のベクトルではなくリストを持つデータフレームを生成しないことです(Martin Morganの回答から)。

l <- list(list(col1="a",col2=1),list(col1="b",col2=2))
f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE)
as.data.frame(Map(f(l), names(l[[1]])))

8

データが同じ長さのベクトルのリストのリストである場合があります。

lolov = list(list(c(1,2,3),c(4,5,6)), list(c(7,8,9),c(10,11,12),c(13,14,15)) )

(内部ベクトルはリストにすることもできますが、これを読みやすくするために単純化しています)。

その後、次の変更を行うことができます。一度に1つのレベルのリストを解除できることに注意してください。

lov = unlist(lolov, recursive = FALSE )
> lov
[[1]]
[1] 1 2 3

[[2]]
[1] 4 5 6

[[3]]
[1] 7 8 9

[[4]]
[1] 10 11 12

[[5]]
[1] 13 14 15

ここで、他の回答で言及されているお気に入りの方法を使用します。

library(plyr)
>ldply(lov)
  V1 V2 V3
1  1  2  3
2  4  5  6
3  7  8  9
4 10 11 12
5 13 14 15


4
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)

3

ソリューションpurrrファミリーを使用した並列(マルチコア、マルチセッションなど)ソリューションの場合は、以下を使用します。

library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)

lリストはどこですか。

plan()使用できる最も効率的なベンチマークを行うには:

library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()

3

次の簡単なコマンドでうまくいきました。

myDf <- as.data.frame(myList)

リファレンス(Quora回答

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6))
> myList
$a
[1] 1 2 3

$b
[1] 4 5 6

> myDf <- as.data.frame(myList)
  a b
1 1 4
2 2 5
3 3 6
> class(myDf)
[1] "data.frame"

しかし、リストをデータフレームに変換する方法が明らかでない場合、これは失敗します。

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6, 7))
> myDf <- as.data.frame(myList)
Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
  arguments imply differing number of rows: 3, 4

:回答は質問のタイトルに向けられており、質問の一部の詳細がスキップされる場合があります


質問からの入力では、これは一種の作品でしかないことに注意してください。OPは132行と20列を要求しますが、これは20行と132列を提供します。
Gregor Thomas

失敗するさまざまな長さの入力の例では、望ましい結果がどうなるかは明らかではありません...
グレゴールトーマス

@Gregor True、ただし質問のタイトルは「R-リストからデータフレーム」です。質問の多くの訪問者と投票した人々は、OPの正確な問題を抱えていません。質問のタイトルに基づいて、リストをデータフレームに変換する方法を探します。私自身も同じ問題を抱えており、私が投稿した解決策で問題が解決しました
Ahmad

うん、ただ注意してください。反対投票はしない。他のほとんどすべての回答とかなり似ていますが、明らかに異なります。
グレゴールトーマス

1

データフレームは等しい長さのベクトルのリストに過ぎないため、これを行う短い(ただし、おそらく最速ではない)方法は、ベースrを使用することです。したがって、入力リストと30 x 132のdata.frameの間の変換は次のようになります。

df <- data.frame(l)

そこから、132 x 30の行列に転置して、データフレームに戻すことができます。

new_df <- data.frame(t(df))

ワンライナーとして:

new_df <- data.frame(t(data.frame(l)))

行名は見るのがかなり面倒ですが、いつでも名前を変更できます

rownames(new_df) <- 1:nrow(new_df)


2
なぜこれは反対票が投じられたのですか?誤解を広め続けないように知りたいのですが。
ウィルC

data.frameとtの組み合わせを使用して、これを確実に以前に実行しました。反対票を投じた人たちは、もっと良い方法があると思います。特に、名前を台無しにしない方がいいと思います。
Arthur Yip

1
これは良い点です。リストの名前を保持したい場合も、これは正しくないと思います。
ウィルC

0

map_関数をforループと一緒に使用してみませんか?これが私の解決策です:

list_to_df <- function(list_to_convert) {
  tmp_data_frame <- data.frame()
  for (i in 1:length(list_to_convert)) {
    tmp <- map_dfr(list_to_convert[[i]], data.frame)
    tmp_data_frame <- rbind(tmp_data_frame, tmp)
  }
  print(tmp_data_frame)
}

map_dfr各リスト要素をdata.frame に変換してから、rbindそれらをすべて結合します。

あなたの場合、私はそれがそうなると思います:

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