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.