Konwertuj listę na ramkę danych

Mam zagnieżdżoną listę danych. Jego długość wynosi 132, a każdy element jest listą 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 przykładowe dane do pracy:

l <- replicate(
  132,
  as.list(sample(letters, 20)),
  simplify = FALSE
)
Author: Alex Brown, 2010-11-19

22 answers

Aktualizacja Lipiec 2020:

Wartością domyślną dla parametru stringsAsFactors jest teraz default.stringsAsFactors(), co z kolei daje FALSE jako wartość domyślną.


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

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

Powyższe konwertuje wszystkie kolumny znaków na czynniki, aby tego uniknąć, możesz dodać parametr do danych.frame() wywołanie:

df <- data.frame(matrix(unlist(l), nrow=132, byrow=TRUE),stringsAsFactors=FALSE)
 429
Author: nico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-05 13:46:42

Z rbind

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

Edit: poprzednia wersja zwraca data.frame z list zamiast wektorów (jak zauważył @IanSudbery w komentarzach).

 510
Author: Marek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-29 16:13:28

Możesz użyć pakietu plyr. 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 w l zawiera kolejną listę o długości 3. Teraz możesz uruchomić

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

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

 140
Author: mropa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-19 17:07:46

Ustalanie przykładowych danych tak, aby pasowały do oryginalnego opisu "każdy element jest listą długości 20"

mylistlist <- replicate(
  132,
  as.list(sample(letters, 20)),
  simplify = FALSE
)

Możemy przekonwertować go do ramki danych w następujący sposób:

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

sapply przekształca go w matrycę. data.frame konwertuje matrycę na ramkę danych.

W wyniku:

Tutaj wpisz opis obrazka

 114
Author: Alex Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-11 20:01:23

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

data.frame(Reduce(rbind, L))
 73
Author: jdeng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-24 14:49:15

Pakiet data.table posiada funkcję rbindlist, która jest superszybką implementacją do.call(rbind, list(...)).

Może mieć listę lists, data.frames lub 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.table dziedziczenie z data.frame.

Jeśli naprawdę. chcesz przekonwertować z powrotem na dane.użycie ramki as.data.frame(DT)

 62
Author: mnel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-25 21:59:44

Pakiet tibble posiada funkcję enframe(), która rozwiązuje ten problem poprzez wymuszenie zagnieżdżonych obiektów list do zagnieżdżonych obiektów tibble ("tidy" ramka danych). 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ż masz kilka gniazd na liście, l, możesz użyć unlist(recursive = FALSE), aby usunąć niepotrzebne zagnieżdżanie, aby uzyskać tylko jedną hierarchiczną listę, a następnie przejść do enframe(). Używam tidyr::unnest(), aby unnest wyjście do jednego poziomu" tidy " ramki danych, która ma dwie kolumny (jedna dla grupy name i jeden dla obserwacji z grupami value). Jeśli chcesz, aby kolumny były szerokie, możesz dodać kolumnę za pomocą add_column(), która po prostu powtarza kolejność wartości 132 razy. Następnie po prostu 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>
 38
Author: Matt Dancho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-10 20:15:21

W zależności od struktury twoich list istnieje kilka opcji tidyverse, które dobrze działają 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żna również 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
 23
Author: sbha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-11 02:07:45

Reshape2 daje takie samo wyjście jak powyższy przykład 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

Jeśli prawie zabrakło pikseli, mógłbyś zrobić to wszystko w jednej linii z funkcją przekształcania ().

 16
Author: Jack Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-16 19:26:14

Ta metoda wykorzystuje pakiet tidyverse (purrr).

Lista:

x <- as.list(mtcars)

Przekształcenie go w ramkę danych (a tibble dokładniej):

library(purrr)
map_df(x, ~.x)
 13
Author: SavedByJESUS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-30 02:00:12

Więcej odpowiedzi, wraz z czasem odpowiedzi na to pytanie: Jaki jest najskuteczniejszy sposób tworzenia listy jako ramki danych?

Najszybszy sposób, który nie tworzy ramki danych z listami, a nie wektorami 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]])))
 10
Author: Ian Sudbery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:33

ODPOWIEDŹ @Marek: jeśli chcesz uniknąć przerobienia ciągów na czynniki, a wydajność nie jest problemem spróbuj

do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))
 10
Author: laubbas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-28 10:31:40

Dla ogólnego przypadku głęboko zagnieżdżonych list z 3 lub więcej poziomami 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ż podejście melt(), Aby przekonwertować zagnieżdżoną listę na wysoki format:

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

Następnie dcast() Następnie ponownie do szerokiego zbioru danych, gdzie każda zmienna tworzy kolumnę a, 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
 10
Author: RubenLaguna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-06 12:37:07

Czasami dane mogą być listą 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)) )

(wewnętrzne wektory 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 nie wyświetlać jednego poziomu 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
 7
Author: user36302,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-27 18:34:41

To w końcu mi się udało:

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

 4
Author: Amit Kohli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-11 11:15:24

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

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

Gdzie l jest listą.

Aby porównać najefektywniejsze 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()
 4
Author: trevi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-23 10:42:06

Zadziałało dla mnie następujące proste polecenie:

myDf <- as.data.frame(myList)

Reference (Quora answer )

> 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ź jest w kierunku tytułu pytania i może pominąć niektóre szczegóły pytania

 4
Author: Ahmad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-15 04:53:59
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
 3
Author: zhan2383,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-20 17:48:48

Krótkim (ale być może nie najszybszym) sposobem na to byłoby użycie bazy r, ponieważ ramka danych jest tylko listą wektorów o równej długości . W ten sposób konwersja między listą wejściową a danymi 30 x 132.ramka będzie:

df <- data.frame(l)

Stamtąd możemy transponować go do matrycy 132 x 30 i konwertować z powrotem do ramki danych:

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

Jako jednoliterowy:

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

Nazwy wierszy będą dość irytujące, ale zawsze można je zmienić z

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

 2
Author: Will C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-02 21:36:23

Każde znalezione rozwiązanie wydaje się mieć zastosowanie tylko wtedy, gdy każdy obiekt w list ma to samo length. Musiałem przekonwertować list na data.frame, Gdy length obiektów w list były nierówne length. Poniżej znajduje się baza R rozwiązanie, które wymyśliłem. Bez wątpienia jest to bardzo nieefektywne, ale wydaje się działać.

x1 <- c(2, 13)
x2 <- c(2, 4, 6, 9, 11, 13)
x3 <- c(1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13)
my.results <- list(x1, x2, x3)

# identify length of each list
my.lengths <- unlist(lapply(my.results, function (x) { length(unlist(x))}))
my.lengths
#[1]  2  6 20

# create a vector of values in all lists
my.values <- as.numeric(unlist(c(do.call(rbind, lapply(my.results, as.data.frame)))))
my.values
#[1]  2 13  2  4  6  9 11 13  1  1  2  3  3  4  5  5  6  7  7  8  9  9 10 11 11 12 13 13

my.matrix <- matrix(NA, nrow = max(my.lengths), ncol = length(my.lengths))

my.cumsum <- cumsum(my.lengths)

mm <- 1

for(i in 1:length(my.lengths)) {

     my.matrix[1:my.lengths[i],i] <- my.values[mm:my.cumsum[i]]

     mm <- my.cumsum[i]+1

}

my.df <- as.data.frame(my.matrix)
my.df
#   V1 V2 V3
#1   2  2  1
#2  13  4  1
#3  NA  6  2
#4  NA  9  3
#5  NA 11  3
#6  NA 13  4
#7  NA NA  5
#8  NA NA  5
#9  NA NA  6
#10 NA NA  7
#11 NA NA  7
#12 NA NA  8
#13 NA NA  9
#14 NA NA  9
#15 NA NA 10
#16 NA NA 11
#17 NA NA 11
#18 NA NA 12
#19 NA NA 13
#20 NA NA 13
 1
Author: Mark Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-30 04:36:03

Może użyjesz funkcji map_ razem z pętlą for? 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)
  }
  return(tmp_data_frame)
}

Gdzie map_dfr konwertuje każdy element listy na dane.ramki, a następnie rbind połącz je w całość.

W Twoim przypadku chyba byłoby:

converted_list <- list_to_df(l)
 0
Author: Bảo Trần,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-29 07:25:50

Try collapse::unlist2d (skrót od 'unlist to data.frame'):

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

library(collapse)
head(unlist2d(l))
  .id.1 .id.2 V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
1     1     1  e  x  b  d  s  p  a  c  k   z   q   m   u   l   h   n   r   t   o   y
2     2     1  r  t  i  k  m  b  h  n  s   e   p   f   o   c   x   l   g   v   a   j
3     3     1  t  r  v  z  a  u  c  o  w   f   m   b   d   g   p   q   y   e   n   k
4     4     1  x  i  e  p  f  d  q  k  h   b   j   s   z   a   t   v   y   l   m   n
5     5     1  d  z  k  y  a  p  b  h  c   v   f   m   u   l   n   q   e   i   w   j
6     6     1  l  f  s  u  o  v  p  z  q   e   r   c   h   n   a   t   m   k   y   x

head(unlist2d(l, idcols = FALSE))
  V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
1  e  x  b  d  s  p  a  c  k   z   q   m   u   l   h   n   r   t   o   y
2  r  t  i  k  m  b  h  n  s   e   p   f   o   c   x   l   g   v   a   j
3  t  r  v  z  a  u  c  o  w   f   m   b   d   g   p   q   y   e   n   k
4  x  i  e  p  f  d  q  k  h   b   j   s   z   a   t   v   y   l   m   n
5  d  z  k  y  a  p  b  h  c   v   f   m   u   l   n   q   e   i   w   j
6  l  f  s  u  o  v  p  z  q   e   r   c   h   n   a   t   m   k   y   x
 0
Author: Sebastian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-01 22:05:14