Konwertuj listę na ramkę danych


513

Mam zagnieżdżoną listę danych. Jego długość wynosi 132, a każdy element jest listą o długości 20. Czy istnieje szybki sposób na przekształcenie tej struktury w ramkę danych, która ma 132 wiersze i 20 kolumn danych?

Oto kilka przykładowych danych do pracy:

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

Czy chcesz, aby każdy element listy był wierszem danych w ramce data.frame?
Joshua Ulrich,

2
@RichieCotton To nie jest dobry przykład. „każdy element jest lista o długości 20” i masz każdy element jest lista jednym z elementów wektora o długości 20
Marek

1
Późno na imprezę, ale nikt nie wspominał o tym , co moim zdaniem było bardzo przydatne (do tego, co chciałem zrobić).
mflo-ByeSE


Odpowiedzi:


390

Zakładając, że twoja lista list nazywa się l:

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

Powyższe przekształci wszystkie kolumny znaków w czynniki, aby tego uniknąć, możesz dodać parametr do wywołania data.frame ():

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

109
Uważaj tutaj, jeśli Twoje dane nie są tego samego typu. Przejście przez matrycę oznacza, że ​​wszystkie dane zostaną wymuszone na wspólny typ. To znaczy, jeśli masz jedną kolumnę danych znakowych i jedną kolumnę danych liczbowych, dane liczbowe zostaną wymuszone na ciąg znaków przez matrix (), a następnie oba na czynniki według data.frame ().
Ian Sudbery,

Jaki jest najlepszy sposób, aby to zrobić, gdy na liście brakuje wartości, lub dołączyć NA do ramki danych?
Dave

1
@Dave: Działa dla mnie ... patrz tutaj r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
nico

4
Uważaj również, jeśli masz typ danych znakowych - data.frame przekonwertuje go na czynniki.
Alex Brown

4
@nico Czy istnieje sposób, aby zachować nazwy elementów listy jako nazwy coln lub rownames w pliku df?
N.Varela,

472

Z rbind

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

Edit: Poprzednia wersja powrót data.framez list„s zamiast wektorów (jak @IanSudbery zauważył w komentarzach).


5
Dlaczego to działa, ale rbind(your_list)zwraca macierz list 1x32?
eykanal

26
@eykanal do.callprzekazuje elementy your_listargumentów jako rbind. Jest to odpowiednik rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]]).
Marek

2
Ta metoda cierpi na sytuację zerową.
Frank Wang

3
@FrankWANG Ale ta metoda nie jest przeznaczona do zerowania sytuacji. Wymagane jest, aby your_listzawierały wektory o jednakowych rozmiarach. NULLma długość 0, więc powinno się nie powieść.
Marek

12
Ta metoda wydaje się zwracać poprawny obiekt, ale podczas sprawdzania obiektu okazuje się, że kolumny są listami, a nie wektorami, co może prowadzić do problemów wzdłuż linii, jeśli się tego nie spodziewasz.
Ian Sudbery,

135

Możesz skorzystać z plyrpakietu. Na przykład zagnieżdżona lista formularza

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

ma teraz długość 4, a każda lista lzawiera kolejną listę o długości 3. Teraz możesz uruchomić

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

i powinien uzyskać taki sam wynik jak w odpowiedzi @Marek i @nico.


8
Świetna odpowiedź. Czy mógłbym ci trochę wyjaśnić, jak to działa? Zwraca po prostu ramkę danych dla każdej pozycji listy?
Michael Barton

13
Imho NAJLEPSZA odpowiedź. Zwraca uczciwą ramkę danych. Wszystkie typy danych (znakowe, numeryczne itp.) Są poprawnie transformowane. Jeśli lista ma różne typy danych, wszystkie zostaną przekształcone w postaci z matrixpodejściem.
Roah

1
próbka podana tutaj nie jest tą podaną w pytaniu. wynik tej odpowiedzi w oryginalnym zestawie danych jest niepoprawny.
MySchizoBuddy

Działa świetnie dla mnie! Nazwy kolumn w wynikowej ramce danych są ustawione! Tx
BAN

Czy plyr jest wielordzeniowy? Czy jest wersja lapply do użycia z mclapply?
Garglesoap

103

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

sapplykonwertuje go na macierz. data.framekonwertuje macierz na ramkę danych.


19
jak dotąd najlepsza odpowiedź! Żadne z pozostałych rozwiązań nie poprawia nazw typów / kolumn. DZIĘKUJĘ CI!
d_a_c321

1
Jaką rolę zamierzasz ctutaj odegrać, jedno wystąpienie danych z listy? Och, czekaj, c dla funkcji konkatenacji, prawda? Zagubienie się przy użyciu c. @ Mnel Zgadzam się również z @dchandler, poprawienie nazw kolumn było cenną potrzebą w moim przypadku użycia. Genialne rozwiązanie.
jxramos

to prawo - standardowa funkcja c; od ?c:Combine Values into a Vector or List
Alex Brown

1
nie działa z przykładowymi danymi podanymi w pytaniu
MySchizoBuddy

3
Czy to nie generuje ramki danych z listami?
Carl

69

Załóżmy, że twoja lista się nazywa L,

data.frame(Reduce(rbind, L))

2
Niezłe! Jest jedna różnica w stosunku do rozwiązania @Alex Brown w porównaniu z twoim, podróż z trasy przyniosła z jakiegoś powodu następujący komunikat ostrzegawczy: `Komunikat ostrzegawczy: W data.row.names (row.names, Rowi, i): niektóre duplikaty row.names : 3,4 -> row.names NOT used '
jxramos

Bardzo dobre!! Pracowałem dla mnie tutaj: stackoverflow.com/questions/32996321/...
Anastasia Pupynina

2
Działa dobrze, chyba że lista zawiera tylko jeden element: data.frame(Reduce(rbind, list(c('col1','col2'))))tworzy ramkę danych z 2 wierszami, 1 kolumną (spodziewałem się 1 rzędu 2 kolumn)
Czerwony groszek

61

Pakiet data.tablema funkcję rbindlistsuperszybkiej implementacji do.call(rbind, list(...)).

To może trwać listę lists, data.frameslub data.tables jako wejście.

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)

Zwraca data.tabledziedziczenie z data.frame.

Jeśli naprawdę chcesz przekonwertować z powrotem na data.frame użyjas.data.frame(DT)


Jeśli chodzi o ostatnią linię, setDFteraz pozwala powrócić do data.frame przez odniesienie.
Frank

1
W przypadku mojej listy zawierającej 30 000 pozycji rbindlist działał znacznie szybciej niż ldply
highharish

35

tibbleOpakowanie ma funkcję enframe(), która rozwiązuje ten problem przez zmuszanie zagnieżdżonych listobiektów zagnieżdżony tibble( „uporządkowane” ramki danych) obiektów. Oto krótki przykład z R dla Data Science :

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

Ponieważ na liście znajduje się kilka gniazd l, możesz użyć przycisku unlist(recursive = FALSE)do usunięcia niepotrzebnego zagnieżdżenia, aby uzyskać tylko jedną hierarchiczną listę, a następnie przejść do niej enframe(). Używam tidyr::unnest()do odczekania danych wyjściowych w jednopoziomowej „uporządkowanej” ramce danych, która zawiera dwie kolumny (jedną dla grupy namei jedną dla obserwacji z grupami value). Jeśli chcesz, aby kolumny były szerokie, możesz dodać kolumnę, add_column()która po prostu powtarza kolejność wartości 132 razy. Następnie tylko spread()wartości.


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>

Cytując OP: „Czy istnieje szybki sposób na przekształcenie tej struktury w ramkę danych, która ma 132 wiersze i 20 kolumn danych?” Więc może potrzebujesz kroku lub czegoś takiego.
Frank

1
Ach tak, po prostu musi istnieć kolumna indeksu, którą można rozłożyć. Niedługo zaktualizuję.
Matt Dancho,

17

W zależności od struktury list istnieje kilka tidyverseopcji, które działają dobrze z listami o nierównej długości:

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

Możesz także mieszać wektory i ramki danych:

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

Ta funkcja dplyr :: bind_rows działa dobrze, nawet przy trudnej pracy z listami pochodzącymi z JSON. Od JSON po zaskakująco czystą ramkę danych. Miły.
GGAnderson

@sbha Próbowałem użyć df <- purrr :: map_df (l, ~ .x), ale wygląda na to, że nie działa, komunikat o błędzie, który mam, to błąd: kolumny X2nie można przekonwertować z liczby całkowitej na znak
Jolin

16

Reshape2 daje taką samą wydajność jak w powyższym przykładzie 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)

daje:

  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

Jeśli były niemal z pikseli ty mógł to wszystko zrobić w 1 linii w / przekształcenia ().


12

Ta metoda używa tidyversepakietu ( mruczenie ).

Lista:

x <- as.list(mtcars)

Przekształcanie go w ramkę danych (a tibbledokładniej):

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

10

Rozszerzając odpowiedź @ Marka: jeśli chcesz uniknąć zamiany łańcuchów na czynniki, a wydajność nie stanowi problemu, spróbuj

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

10

Dla ogólnego przypadku głęboko zagnieżdżonych list z 3 lub więcej poziomami, takimi jak te uzyskane z zagnieżdżonego JSON:

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

rozważ melt()najpierw podejście do konwersji zagnieżdżonej listy do wysokiego formatu:

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

po dcast()czym następuje przejście do uporządkowanego zestawu danych, w którym każda zmienna tworzy kolumnę, a każda obserwacja tworzy wiersz:

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

Więcej odpowiedzi wraz z harmonogramem w odpowiedzi na to pytanie: Jaki jest najskuteczniejszy sposób na rzutowanie listy jako ramki danych?

Najszybszym sposobem, który nie tworzy ramki danych z listami zamiast wektorów dla kolumn wydaje się być (z odpowiedzi Martina Morgana):

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

Czasami twoje dane mogą być listą wektorów o tej samej długości.

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

(Wektory wewnętrzne mogą być również listami, ale upraszczam, aby ułatwić czytanie).

Następnie możesz wprowadzić następującą modyfikację. Pamiętaj, że możesz wylistować jeden poziom na raz:

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

Teraz użyj swojej ulubionej metody wymienionej w innych odpowiedziach:

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

Oto, co w końcu dla mnie zadziałało:

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

W przypadku rozwiązania równoległego (wielordzeniowego, wielosesyjnego itp.) Używającego purrrrodziny rozwiązań użyj:

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

Gdzie ljest lista.

Aby przetestować najbardziej efektywne plan(), możesz użyć:

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

3

Dla mnie zadziałało następujące proste polecenie:

myDf <- as.data.frame(myList)

Odniesienie ( odpowiedź 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"

Ale to się nie powiedzie, jeśli nie jest oczywiste, jak przekonwertować listę na ramkę danych:

> 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

Uwaga : odpowiedź dotyczy tytułu pytania i może pomijać niektóre szczegóły pytania


Zauważ, że na wejściu pytania pytanie działa tylko w ten sposób. OP prosi o 132 wiersze i 20 kolumn, ale daje to 20 wierszy i 132 kolumny.
Gregor Thomas

Dla twojego przykładu z wejściem o różnej długości, gdzie zawodzi, nie jest jasne, jaki byłby pożądany rezultat ...
Gregor Thomas

@Gregor Prawda, ale tytuł pytania brzmi „R - lista do ramki danych”. Wielu odwiedzających pytanie i osoby, które je głosowały, nie mają dokładnego problemu z OP. Na podstawie tytułu pytania szukają sposobu na konwersję listy do ramki danych. Ja sam miałem ten sam problem, a rozwiązanie, które opublikowałem, rozwiązało mój problem
Ahmad

Tak, tylko zauważam. Nie oddawanie głosu. Przyjemnie byłoby zauważyć w odpowiedzi, że robi coś podobnego - ale wyraźnie odmiennego niż - prawie wszystkie inne odpowiedzi.
Gregor Thomas

1

Krótkim (ale być może nie najszybszym) sposobem na zrobienie tego byłoby użycie bazy r, ponieważ ramka danych jest tylko listą wektorów o jednakowej długości . Tak więc konwersja między twoją listą wejściową a 30 x 132 danymi. Ramka byłaby:

df <- data.frame(l)

Stamtąd możemy przetransponować go na matrycę 132 x 30 i przekonwertować z powrotem na ramkę danych:

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

Jako jedna linijka:

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

Nazwy nazw będą dość denerwujące, ale zawsze możesz zmienić ich nazwy

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


2
Dlaczego zostało to zanegowane? Chciałbym wiedzieć, aby nie rozpowszechniać dezinformacji.
Czy C

Zrobiłem to już wcześniej, używając kombinacji data.frame it! Myślę, że ludzie, którzy głosowali z góry, uważają, że istnieją lepsze sposoby, szczególnie ci, którzy nie psują nazwisk.
Arthur Yip

1
To dobra uwaga, myślę, że jest to również niepoprawne, jeśli chcesz zachować nazwy na liście.
Czy C

0

Co powiesz na używanie map_funkcji razem z forpętlą? Oto moje rozwiązanie:

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

gdzie map_dfrprzekonwertuj każdy element listy na data.frame, a następnie połącz rbindje całkowicie.

W twoim przypadku myślę, że byłoby to:

converted_list <- list_to_df(l)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.