Bir listeyi veri çerçevesine dönüştürme


513

İç içe veri listem var. Uzunluğu 132'dir ve her bir öğe 20 uzunluğunun bir listesidir. Bu yapıyı 132 satır ve 20 sütun veri içeren bir veri çerçevesine dönüştürmenin hızlı bir yolu var mı ?

Çalışmak için bazı örnek veriler:

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

Her liste öğesini data.frame'inizde bir veri satırı olarak mı istiyorsunuz?
Joshua Ulrich

2
@RichieCotton Doğru örnek değil. "her madde 20 uzunluk listesidir " ve her öğe 20 uzunluk vektörünün tek element listesidir .
Marek

1
Geç partiye, ama kimse söz görmedim bu ben (Yapmak aradığı şeyi için) çok kullanışlı olduğunu düşünmüş.
mflo-ByeSE


Yanıtlar:


390

Liste listenizin adı varsayarsak l:

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

Yukarıdaki tüm karakter sütunlarını faktörlere dönüştürecektir, bundan kaçınmak için data.frame () çağrısına bir parametre ekleyebilirsiniz:

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

109
Verileriniz aynı türde değilse buraya dikkat edin. Bir matristen geçmek, tüm verilerin ortak bir türe zorlanacağı anlamına gelir. Yani bir karakter verisi sütunu ve bir sayısal veri sütunu varsa, sayısal veriler matris () ile dizeye ve daha sonra da her ikisi de data.frame () ile çarpar.
Ian Sudbery

Listede eksik değerler olduğunda bunu yapmanın veya veri çerçevesine NA eklemenin en iyi yolu nedir?
Dave

1
@Dave: Benim için çalışıyor ... buraya bakın r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
nico

4
Karakter veri türünüz varsa da dikkatli olun - data.frame bunu faktörlere dönüştürür.
Alex Brown

4
@nico Liste öğelerinin adlarını df'de colnames veya rownames olarak tutmanın bir yolu var mı?
N.Varela

472

İle rbind

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

Düzenleme: Önceki sürüm dönüş data.frameait list(@IanSudbery Açıklamalarda belirttiği gibi) 'vektörlerin yerine s.


5
Neden çalışıyor ama rbind(your_list)1x32 liste matrisi döndürüyor?
eykanal

26
@eykanal do.callöğelerini your_listargüman olarak geçirir rbind. Eşdeğerdir rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]]).
Marek

2
Bu yöntem null durumdan muzdariptir.
Frank Wang

3
@FrankWANG Ancak bu yöntem boş durum için tasarlanmamıştır. your_listEşit boyutlu vektörler içermesi gerekir . NULLuzunluğu 0 olduğundan başarısız olması gerekir.
Marek

12
Bu yöntem doğru nesneyi döndürüyor gibi görünüyor, ancak nesneyi incelerken, sütunların vektörlerden ziyade listeler olduğunu göreceksiniz, bu da beklemiyorsanız satırda sorunlara yol açabilir.
Ian Sudbery

135

plyrPaketi kullanabilirsiniz . Örneğin, formun iç içe bir listesi

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

artık 4 uzunluğundadır ve içindeki her liste l3 uzunluğundaki başka bir listeyi içerir.

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

@Marek ve @nico cevabındakiyle aynı sonucu almalıdır.


8
Mükemmel cevap. Bunun nasıl çalıştığını biraz açıklayabilir misiniz? Her liste girişi için bir veri çerçevesi döndürüyor mu?
Michael Barton

13
Imho EN İYİ cevap. Dürüst bir data.frame döndürür. Tüm veri türleri (karakter, sayısal, vb.) Doğru bir şekilde dönüştürülür. Listenin farklı veri türleri varsa hepsi matrixyaklaşımla karaktere dönüştürülür .
Roah

1
burada sağlanan örnek soru tarafından verilen örnek değildir. orijinal veri kümesindeki bu yanıtın sonucu yanlıştır.
MySchizoBuddy

Benim için harika çalışıyor! Ve ortaya çıkan Veri Çerçevesindeki sütunların adları ayarlanır! Tx
bAN

Plyr çok çekirdekli midir? Yoksa mclapply ile kullanmak için lapply versiyonu var mı?
Garglesoap

103

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

sapplybunu bir matrise dönüştürür. data.framematrisi bir veri çerçevesine dönüştürür.


19
açık arayla en iyi cevap! Diğer çözümlerin hiçbiri türleri / sütun adlarını doğru almaz. TEŞEKKÜR EDERİM!
d_a_c321

1
cListedeki verilerin bir örneği olan burada hangi rolü oynamak istiyorsunuz ? Oh bekleyin, c bitiştir işlevi için değil mi? @ Mnel'in c. Ayrıca @dchandler ile hemfikirim, sütun adlarını doğru bir şekilde kullanmak kullanım durumumda değerli bir ihtiyaçtı. Mükemmel çözüm.
jxramos

bu sağ - standart c işlevi; itibaren ?c:Combine Values into a Vector or List
Alex Brown

1
soruda verilen örnek verilerle çalışmaz
MySchizoBuddy

3
Bu listelerin bir data.frame dosyasını oluşturmuyor mu?
Carl

69

listenizin çağrıldığını varsayalım L,

data.frame(Reduce(rbind, L))

2
Güzel bir! @Alex Brown'un çözümü ile sizinkine kıyasla bir fark var, rotanıza gitmek bazı nedenlerden dolayı aşağıdaki uyarı mesajını verdi: `Uyarı mesajı: data.row.names (row.names, rowsi, i): bazı row.names çoğaltıldı : 3,4 -> row.names NOT used '
jxramos

Çok iyi!! Burada benim için çalıştı: stackoverflow.com/questions/32996321/…
Anastasia Pupynina

2
Listede sadece bir öğe olmadığı sürece iyi çalışır: 2 satır, 1 sütun (1 satır 2 sütun beklediğim) data.frame(Reduce(rbind, list(c('col1','col2'))))içeren bir veri çerçevesi oluşturur
Red Pea

61

Paket data.table, rbindlistsüper hızlı bir uygulama işlevi vardır do.call(rbind, list(...)).

Bu bir listesini alabilir lists, data.framesya da data.tables girdi olarak.

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)

Bu, data.tableöğesinden devralma döndürür data.frame.

Eğer varsa gerçekten bir data.frame kullanımına geri dönüştürmek istediğinizas.data.frame(DT)


Son satırla ilgili olarak, setDFartık referans olarak data.frame öğesine dönmeye izin veriyor.
Frank

1
30k öğe içeren listem için, rbindlist ldply'den daha hızlı çalıştı
tallharish

35

tibblePaket bir işlevi vardır enframe()çözer iç içe zorlamak bu problem, listiç içe nesneleri tibble( "düzenli" veri çerçevesi) nesneleri. Veri Bilimi için R'den kısa bir örnek :

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

Eğer Listede birkaç yuva olduğundan, lkullanabileceğiniz unlist(recursive = FALSE)tek bir hiyerarşik listesini almak için gereksiz iç içe kaldırmak ve daha sonra geçmek enframe(). tidyr::unnest()Çıktıyı, iki sütun (grup nameiçin bir ve gruplarla gözlemler için) olan tek düzeyli bir "düzenli" veri çerçevesine dürüstleştirmek için kullanıyorum value. Genişleyen sütunlar istiyorsanız add_column(), yalnızca 132 kez değerlerin sırasını tekrarlayan bir sütun ekleyebilirsiniz . Sonra sadece spread()değerler.


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'den alıntı: "Bu yapıyı 132 satır ve 20 sütun veri içeren bir veri çerçevesine dönüştürmenin hızlı bir yolu var mı?" Yani belki bir formata ihtiyacınız var.
Frank

1
Ah evet, sadece yayılabilecek bir dizin sütunu olmalı. Yakında güncelleyeceğim.
Matt Dancho

17

Listelerinizin yapısına bağlı olarak, tidyverseeşit olmayan uzunluk listeleriyle iyi çalışan bazı seçenekler vardır:

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

Vektörleri ve veri çerçevelerini de karıştırabilirsiniz:

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

Bu dplyr :: bind_rows işlevi JSON kaynaklı listelerle çalışmak zor olsa bile iyi çalışır. JSON'dan şaşırtıcı derecede temiz bir veri çerçevesine. Güzel.
GGAnderson

@sbha df <- purrr :: map_df (l, ~ .x) kullanmaya çalıştım ama çalışmıyor gibi görünüyor, sahip olduğum hata mesajı Hata: Sütun X2tamsayı karaktere dönüştürülemez
Jolin

16

Reshape2 yukarıdaki plyr örneğiyle aynı çıktıyı verir:

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)

verim:

  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

Neredeyse piksel dışında olsaydınız , bunu 1 satır w / recast () ile yapabilirsiniz.


12

Bu yöntem bir tidyversepaket ( purrr ) kullanır .

Liste:

x <- as.list(mtcars)

Veri çerçevesine dönüştürmek ( tibbledaha spesifik olarak):

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

10

@ Marek'in cevabını genişletmek: Dizelerin faktörlere dönüştürülmesini önlemek istiyorsanız ve verimlilik endişe verici bir şey değildir

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

10

İç içe geçmiş bir JSON'dan elde edilenler gibi 3 veya daha fazla seviyeli derin iç içe listelerin genel durumu için :

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

melt()önce iç içe listeyi uzun bir biçime dönüştürme yaklaşımını göz önünde bulundurun :

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

ardından dcast()her değişkenin bir sütun oluşturduğu ve her gözlemin bir satır oluşturduğu düzenli bir veri kümesine yeniden genişletin:

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

Bu sorunun cevabındaki zamanlamalarla birlikte daha fazla cevap: Bir listeyi veri çerçevesi olarak yayınlamanın en etkili yolu nedir?

Sütunlar için vektörler yerine listeler içeren bir veri çerçevesi oluşturmayan en hızlı yol (Martin Morgan'ın cevabından) görünmektedir:

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

Bazen verileriniz aynı uzunlukta vektörlerin bir listesi olabilir.

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

(İç vektörler de listeler olabilir, ancak bunu okumayı kolaylaştırmak için basitleştiriyorum).

Ardından aşağıdaki değişikliği yapabilirsiniz. Bir kerede bir seviye listeleyebileceğinizi unutmayın:

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

Şimdi diğer cevaplarda bahsedilen en sevdiğiniz yöntemi kullanın:

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

Sonunda benim için işe yarayan buydu:

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


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

3

Çözüm purrrailesi kullanan paralel (çok çekirdekli, çok oturumlu vb.) Bir çözüm için şunları kullanın:

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

lListe nerede .

En verimli olanı karşılaştırmak için plan()şunları kullanabilirsiniz:

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

3

Aşağıdaki basit komut benim için çalıştı:

myDf <- as.data.frame(myList)

Referans ( Quora cevabı )

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

Ancak listenin veri çerçevesine nasıl dönüştürüleceği açık değilse, bu başarısız olacaktır:

> 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

Not : Cevap sorunun başlığına yöneliktir ve sorunun bazı ayrıntılarını atlayabilir


Sorudan gelen girdide bunun sadece bir çeşit işe yaradığını unutmayın. OP 132 satır ve 20 sütun ister, ancak bu 20 satır ve 132 sütun verir.
Gregor Thomas

Başarısız olduğu farklı uzunluktaki girişe sahip örneğin, istenen sonucun ne olacağı belli değil ...
Gregor Thomas

@Gregor Doğru, ancak soru başlığı "R - veri çerçevesi listesi" dir. Sorunun birçok ziyaretçisi ve oy kullananların OP sorunu tam olarak yok. Soru başlığına dayanarak, listeyi veri çerçevesine dönüştürmenin bir yolunu ararlar. Kendim de aynı sorunu yaşadım ve gönderdiğim çözüm sorunumu çözdü
Ahmad

Evet, sadece not et. Aşağı oy değil. Cevapta, diğer tüm cevapların hemen hemen hepsine benzer - ama açıkça farklı - bir şey yaptığını belirtmek güzel olabilir.
Gregor Thomas

1

Bunu yapmanın kısa (ama belki de en hızlı değil) yolu r tabanını kullanmak olacaktır, çünkü bir veri çerçevesi sadece eşit uzunlukta vektörlerin bir listesidir . Böylece giriş listeniz ile 30 x 132 veri çerçevesi arasındaki dönüşüm şöyle olur:

df <- data.frame(l)

Oradan 132 x 30 matrise aktarabilir ve veri çerçevesine geri dönüştürebiliriz:

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

Tek astar olarak:

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

Rownames bakmak oldukça sinir bozucu olacak, ancak bunları her zaman

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


2
Bu neden reddedildi? Bilmek istiyorum, bu yüzden yanlış bilgi yaymaya devam etmiyorum.
Will C

Bunu daha önce bir data.frame ve t kombinasyonu kullanarak yaptım! Sanırım küçülen insanlar, özellikle isimleri berbat etmeyenler için daha iyi yollar olduğunu düşünüyor.
Arthur Yip

1
Bu iyi bir nokta, sanırım listenizdeki isimleri korumak istiyorsanız bu da yanlış.
C

0

map_Bir fordöngü ile birlikte işlevi kullanmaya ne dersiniz ? İşte benim çözümüm:

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

burada map_dfrliste öğesinin her birini bir data.frame'e dönüştürün ve sonra rbindbunları bir araya getirin.

Senin durumunda, sanırım şöyle olurdu:

converted_list <- list_to_df(l)
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.