Konversi daftar menjadi bingkai data

513

Saya memiliki daftar data bersarang. Panjangnya adalah 132 dan setiap item adalah daftar panjangnya 20. Apakah ada cara cepat untuk mengubah struktur ini menjadi kerangka data yang memiliki 132 baris dan 20 kolom data?

Berikut ini beberapa contoh data untuk digunakan:

l <- replicate(
  132,
  list(sample(letters, 20)),
  simplify = FALSE
)
Btibert3
sumber
Jadi Anda ingin setiap elemen daftar sebagai deretan data di data.frame Anda?
Joshua Ulrich
2
@ RichieCotton Ini bukan contoh yang tepat. "setiap item adalah daftar panjang 20" dan Anda mendapatkan setiap item adalah satu daftar elemen vektor panjang 20.
Marek
1
Terlambat ke pesta, tetapi saya tidak melihat ada yang menyebutkan ini , yang saya pikir sangat berguna (untuk apa yang ingin saya lakukan).
mflo-ByeSE

Jawaban:

390

Dengan asumsi daftar daftar Anda disebut l:

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

Di atas akan mengkonversi semua kolom karakter ke faktor, untuk menghindari ini, Anda dapat menambahkan parameter ke panggilan data.frame ():

df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)
nico
sumber
109
Hati-hati di sini jika data Anda tidak semuanya bertipe sama. Melewati matriks berarti bahwa semua data akan dipaksa menjadi tipe umum. Yaitu jika Anda memiliki satu kolom data karakter dan satu kolom data numerik, data numerik akan dipaksa ke string oleh matriks () dan kemudian keduanya menjadi faktor dengan data.frame ().
Ian Sudbery
Apa cara terbaik untuk melakukan ini di mana daftar tersebut memiliki nilai yang hilang, atau untuk memasukkan NA dalam bingkai data?
Dave
1
@Dave: Bekerja untuk saya ... lihat di sini r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
nico
4
Juga berhati-hatilah jika Anda memiliki tipe data karakter - data.frame akan mengubahnya menjadi faktor.
Alex Brown
4
@nico Apakah ada cara untuk menjaga nama elemen daftar sebagai nama atau rownames di df?
N.Varela
472

Dengan rbind

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

Edit: Sebelumnya versi kembali data.framedari list's bukan vektor (seperti @IanSudbery ditunjukkan dalam komentar).

Marek
sumber
5
Mengapa ini berfungsi tetapi rbind(your_list)mengembalikan matriks daftar 1x32?
eykanal
26
@eykanal do.callmeneruskan elemen your_listsebagai argumen ke rbind. Ini setara dengan rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]]).
Marek
2
Metode ini menderita situasi nol.
Frank Wang
3
@ FrankWANG Tetapi metode ini tidak dirancang untuk situasi nol. Diperlukan yang your_listmengandung vektor berukuran sama. NULLmemiliki panjang 0 sehingga harus gagal.
Marek
12
Metode ini tampaknya mengembalikan objek yang benar, tetapi saat memeriksa objek, Anda akan menemukan bahwa kolom adalah daftar daripada vektor, yang dapat menyebabkan masalah di telepon jika Anda tidak mengharapkannya.
Ian Sudbery
135

Anda bisa menggunakan plyrpaket. Misalnya daftar formulir yang bersarang

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

sekarang memiliki panjang 4 dan masing-masing daftar di lberisi daftar panjang lainnya 3. Sekarang Anda dapat menjalankan

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

dan harus mendapatkan hasil yang sama seperti pada jawaban @Marek dan @nico.

mropa
sumber
8
Jawaban yang bagus Bisakah Anda jelaskan sedikit cara kerjanya? Ini hanya mengembalikan bingkai data untuk setiap entri daftar?
Michael Barton
13
Imho jawaban TERBAIK. Ini mengembalikan data.frame yang jujur. Semua tipe data (karakter, angka, dll) ditransformasikan dengan benar. Jika daftar memiliki tipe data yang berbeda mereka semua akan ditransformasikan menjadi karakter dengan matrixpendekatan.
Roah
1
sampel yang disediakan di sini bukan yang disediakan oleh pertanyaan. hasil jawaban ini pada dataset asli salah.
MySchizoBuddy
Bekerja sangat bagus untuk saya! Dan nama-nama kolom dalam Frame Data yang dihasilkan diatur! Tx
larangan
Apakah plyr multicore? Atau ada versi lapply untuk digunakan dengan mclapply?
Garglesoap
103

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

sapplymengubahnya menjadi sebuah matriks. data.framemengkonversi matriks ke bingkai data.

Alex Brown
sumber
19
jawaban terbaik sejauh ini! Tidak ada solusi lain yang mendapatkan tipe / nama kolom yang benar. TERIMA KASIH!
d_a_c321
1
Peran apa yang ingin Anda cmainkan di sini, satu contoh dari data daftar? Oh, tunggu, c untuk fungsi gabungan, kan? Menjadi bingung dengan penggunaan @ mnel tentang c. Saya juga setuju dengan @dchandler, mendapatkan nama kolom yang benar adalah kebutuhan yang berharga dalam kasus penggunaan saya. Solusi brilian.
jxramos
hak itu - fungsi standar c; dari ?c:Combine Values into a Vector or List
Alex Brown
1
tidak bekerja dengan data sampel yang disediakan dalam pertanyaan
MySchizoBuddy
3
Tidakkah ini menghasilkan data.frame daftar?
Carl
69

anggap daftar Anda dipanggil L,

data.frame(Reduce(rbind, L))
jeng
sumber
2
Yang bagus! Ada satu perbedaan dengan solusi @Alex Brown dibandingkan dengan solusi Anda, jika rute Anda menghasilkan pesan peringatan berikut karena beberapa alasan: `Pesan peringatan: Dalam data.row.names (row.names, rowsi, i): beberapa row.names digandakan : 3,4 -> row.names NOT used '
jxramos
Baik sekali!! Bekerja untuk saya di sini: stackoverflow.com/questions/32996321/…
Anastasia Pupynina
2
Berfungsi dengan baik kecuali jika daftar hanya memiliki satu elemen di dalamnya: data.frame(Reduce(rbind, list(c('col1','col2'))))menghasilkan bingkai data dengan 2 baris, 1 kolom (saya harapkan 1 baris 2 kolom)
The Red Pea
61

Paket ini data.tablememiliki fungsi rbindlistyang merupakan implementasi supercepat dari do.call(rbind, list(...)).

Dapat mengambil daftar lists, data.framesatau data.tables sebagai masukan.

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)

Ini mengembalikan data.tablewarisan dari data.frame.

Jika Anda benar - benar ingin mengonversi kembali ke penggunaan data.frameas.data.frame(DT)

mnel
sumber
Mengenai baris terakhir, setDFsekarang memungkinkan untuk kembali ke data.frame dengan referensi.
Frank
1
Untuk daftar saya dengan 30k item, rbindlist bekerja lebih cepat daripada ldply
tallharish
35

The tibblepaket memiliki fungsi enframe()yang memecahkan masalah ini dengan memaksa bersarang listobjek untuk bersarang tibble( "rapi" frame data) obyek. Berikut adalah contoh singkat dari R untuk Ilmu Data :

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]>

Karena Anda memiliki beberapa sarang dalam daftar, lAnda dapat menggunakan unlist(recursive = FALSE)untuk menghapus sarang yang tidak perlu untuk mendapatkan hanya satu daftar hierarkis dan kemudian lolos enframe(). Saya gunakan tidyr::unnest()untuk mem-undest output menjadi kerangka data "rapi" tingkat tunggal, yang memiliki dua kolom Anda (satu untuk grup namedan satu untuk pengamatan dengan grup value). Jika Anda ingin kolom yang lebar, Anda bisa menambahkan kolom menggunakan add_column()yang hanya mengulangi urutan nilai 132 kali. Kemudian hanya spread()nilainya.


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>
Matt Dancho
sumber
Mengutip OP: "Apakah ada cara cepat untuk mengubah struktur ini menjadi kerangka data yang memiliki 132 baris dan 20 kolom data?" Jadi mungkin Anda perlu langkah penyebaran atau sesuatu.
Frank
1
Ah ya, hanya perlu ada kolom indeks yang bisa disebarkan. Saya akan segera memperbarui.
Matt Dancho
17

Bergantung pada struktur daftar Anda, ada beberapa tidyverseopsi yang berfungsi baik dengan daftar panjang yang tidak sama:

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

Anda juga dapat mencampur vektor dan bingkai data:

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
sbha
sumber
Fungsi dplyr :: bind_rows ini berfungsi dengan baik, bahkan dengan susah bekerja dengan daftar yang berasal dari JSON. Dari JSON ke kerangka data bersih yang mengejutkan. Bagus.
GGAnderson
@sbha Saya mencoba menggunakan df <- purrr :: map_df (l, ~ .x) tetapi sepertinya itu tidak berfungsi, pesan kesalahan yang saya miliki adalah Kesalahan: Kolom X2tidak dapat dikonversi dari integer ke karakter
Jolin
16

Membentuk ulang2 menghasilkan output yang sama seperti contoh plyr di atas:

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)

hasil:

  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

Jika Anda hampir kehabisan piksel, Anda dapat melakukan ini semua dalam 1 baris dengan pemasangan ulang ().

Jack Ryan
sumber
12

Metode ini menggunakan tidyversepaket ( purrr ).

Daftar:

x <- as.list(mtcars)

Mengubahnya menjadi bingkai data ( tibblelebih khusus):

library(purrr)
map_df(x, ~.x)
SavedByJESUS
sumber
10

Memperluas pada jawaban @ Marek: jika Anda ingin menghindari string yang akan berubah menjadi faktor dan efisiensi bukan masalah coba

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

Untuk kasus umum daftar bersarang dalam dengan 3 level atau lebih seperti yang diperoleh dari JSON bersarang:

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

pertimbangkan pendekatan melt()untuk mengonversi daftar bersarang ke format tinggi terlebih dahulu:

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

diikuti dcast()kemudian untuk melebar lagi ke dataset rapi di mana setiap variabel membentuk kolom dan setiap pengamatan membentuk satu baris:

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
RubenLaguna
sumber
9

Lebih banyak jawaban, bersama dengan waktu dalam jawaban untuk pertanyaan ini: Apa cara paling efisien untuk melemparkan daftar sebagai kerangka data?

Cara tercepat, yang tidak menghasilkan kerangka data dengan daftar daripada vektor untuk kolom tampaknya (dari jawaban 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]])))
Ian Sudbery
sumber
8

Terkadang data Anda mungkin merupakan daftar daftar vektor dengan panjang yang sama.

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

(Vektor dalam juga bisa berupa daftar, tapi saya menyederhanakan untuk membuatnya lebih mudah dibaca).

Kemudian Anda dapat melakukan modifikasi berikut. Ingatlah bahwa Anda dapat membatalkan pendaftaran satu tingkat pada satu waktu:

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

Sekarang gunakan metode favorit Anda yang disebutkan dalam jawaban lain:

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
pengguna36302
sumber
4

Inilah yang akhirnya berhasil untuk saya:

do.call("rbind", lapply(S1, as.data.frame))

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

Untuk solusi paralel (multicore, multisession, dll) menggunakan purrrkeluarga solusi, gunakan:

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

Dimana ldaftarnya.

Untuk benchmark yang paling efisien, plan()Anda dapat menggunakan:

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

Perintah sederhana berikut ini bekerja untuk saya:

myDf <- as.data.frame(myList)

Referensi ( Jawaban 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"

Tetapi ini akan gagal jika tidak jelas cara mengonversi daftar menjadi bingkai data:

> 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

Catatan : Jawabannya mengarah ke judul pertanyaan dan dapat melompati beberapa detail pertanyaan

Ahmad
sumber
Catatan bahwa pada input dari pertanyaan ini hanya berfungsi. OP meminta 132 baris dan 20 kolom, tetapi ini memberikan 20 baris dan 132 kolom.
Gregor Thomas
Sebagai contoh Anda dengan input panjang berbeda di mana gagal, tidak jelas apa hasil yang diinginkan ...
Gregor Thomas
@ Gregor Benar, tetapi judul pertanyaannya adalah "R - daftar ke bingkai data". Banyak pengunjung pertanyaan dan mereka yang memilihnya tidak memiliki masalah OP yang pasti. Berdasarkan judul pertanyaan, mereka hanya mencari cara untuk mengubah daftar menjadi bingkai data. Saya sendiri memiliki masalah yang sama dan solusi yang saya posting memecahkan masalah saya
Ahmad
Yup, hanya mencatat. Tidak downvoting. Mungkin menyenangkan untuk dicatat dalam jawaban bahwa ia melakukan sesuatu yang serupa - tetapi jelas berbeda dari - hampir semua jawaban lainnya.
Gregor Thomas
1

Cara pendek (tapi mungkin bukan yang tercepat) untuk melakukan ini adalah dengan menggunakan basis r, karena bingkai data hanyalah daftar vektor dengan panjang yang sama . Dengan demikian konversi antara daftar input Anda dan data.frame 30 x 132 adalah:

df <- data.frame(l)

Dari sana kita dapat memindahkannya ke matriks 132 x 30, dan mengubahnya kembali menjadi kerangka data:

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

Sebagai one-liner:

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

Rownames akan sangat mengganggu untuk dilihat, tetapi Anda selalu bisa mengganti nama dengan

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

Will C
sumber
2
Mengapa ini diturunkan? Saya ingin tahu sehingga saya tidak terus menyebarkan informasi yang salah.
Will C
Saya pasti pernah melakukan ini sebelumnya, menggunakan kombinasi data.frame dan t! Saya kira orang-orang yang turun jabatan merasa ada cara yang lebih baik, terutama mereka yang tidak mengacaukan nama.
Arthur Yip
1
Itu poin yang bagus, saya kira ini juga salah jika Anda ingin menyimpan nama dalam daftar Anda.
Will C
0

Bagaimana kalau menggunakan map_fungsi bersamaan dengan satu forloop? Inilah solusi saya:

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

di mana map_dfrmengkonversi masing-masing elemen daftar ke dalam data.frame dan kemudian persatukan rbindsemuanya.

Dalam kasus Anda, saya kira itu akan:

converted_list <- list_to_df(l)
Bầo Trần
sumber