R-list to data frame


Tengo una lista anidada de datos. Su longitud es 132 y cada elemento es una lista de longitud 20. ¿Hay una forma rápida de convertir esta estructura en un marco de datos que tenga 132 filas y 20 columnas de datos?

Aquí hay algunos datos de muestra para trabajar con:

l <- replicate(
  132,
  list(sample(letters, 20)),
  simplify = FALSE
)
Author: Philipp HB, 2010-11-19

17 answers

Asumiendo que tu lista de listas se llama l:

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

Lo anterior convertirá todas las columnas de caracteres en factores, para evitar esto puede agregar un parámetro a los datos.llamada a frame ():

df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)
 277
Author: nico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-17 08:36:28

Con rbind

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

Editar: La versión anterior devuelve data.frame de list's en lugar de vectores (como @IanSudbery señaló en los comentarios).

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

Puede usar el paquete plyr. Por ejemplo, una lista anidada de la forma

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

Tiene ahora una longitud de 4 y cada lista en l contiene otra lista de la longitud 3. Ahora puedes ejecutar

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

Y debería obtener el mismo resultado que en la respuesta @Marek y @nico.

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

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

sapply lo convierte en una matriz. data.frame convierte la matriz en un marco de datos.

 77
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-19 17:19:33

Supongamos que su lista se llama L,

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

El paquete data.table tiene la función rbindlist que es una implementación superrápida de do.call(rbind, list(...)).

Puede tomar una lista de lists, data.frames o data.tables como entrada.

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)

Esto devuelve un data.table heredado de data.frame.

Si realmente desea convertir de nuevo a un dato.uso del marco as.data.frame(DT)

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

El paquete tibble tiene una función enframe() que resuelve este problema coaccionando objetos anidados list a objetos anidados tibble ("tidy" data frame). Aquí hay un breve ejemplo de R para la Ciencia de datos :

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

Dado que tiene varios nidos en su lista, l, puede usar unlist(recursive = FALSE) para eliminar el anidamiento innecesario para obtener una sola lista jerárquica y luego pasar a enframe(). Utilizo tidyr::unnest() para desmontar la salida en un marco de datos "ordenado" de un solo nivel, que tiene sus dos columnas (una para el grupo name y uno para las observaciones con los grupos value). Si desea columnas que sean amplias, puede agregar una columna usando add_column() que simplemente repita el orden de los valores 132 veces. Entonces solo spread() los valores.


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>
 17
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-10 20:15:21

Reshape2 produce la misma salida que el ejemplo plyr anterior:

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)

Rinde:

  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

Si estuviera casi sin píxeles, podría hacer todo esto en 1 línea con recast().

 15
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-16 19:26:14

Más respuestas, junto con los tiempos en la respuesta a esta pregunta: ¿Cuál es la forma más eficiente de convertir una lista como un marco de datos?

La forma más rápida, que no produce un dataframe con listas en lugar de vectores para columnas parece ser (de la respuesta de 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]])))
 8
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:33

Para el caso general de listas profundamente anidadas con 3 o más niveles como las que se obtienen de un JSON anidado:

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

Considere el enfoque de melt() para convertir la lista anidada a un formato alto primero:

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

Seguido de dcast() luego de nuevo a ancho en un conjunto de datos ordenado donde cada variable forma una columna y cada observación forma una fila:

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
 8
Author: ecerulm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-06 12:37:07

Extendiendo la respuesta de @ Marek: si quieres evitar que las cadenas se conviertan en factores y la eficiencia no es una preocupación, intenta

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

A veces sus datos pueden ser una lista de listas de vectores de la misma longitud.

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

(Los vectores internos también podrían ser listas, pero estoy simplificando para hacer esto más fácil de leer).

Entonces puede hacer la siguiente modificación. Recuerda que puedes dejar de listar un nivel a la vez:

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

Ahora usa tu método favorito mencionado en las otras respuestas:

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
 6
Author: user36302,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-27 18:34:41

Esto es lo que finalmente funcionó para mí:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 11:15:24

Dependiendo de la estructura de sus listas, hay algunas opciones tidyverse que funcionan bien con listas de longitud desigual:

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

También puede mezclar vectores y marcos de datos:

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
 3
Author: sbha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-11 02:07:45
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
 2
Author: zhan2383,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-20 17:48:48

Este método utiliza un paquete tidyverse ( purrr).

La lista:

x <- as.list(mtcars)

Convertirlo en un marco de datos (a tibble más específicamente):

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

Test1

Test2

As.datos.marco(test2) a b c 1 a b c 2 d e f

Test3

As.datos.marco(test3) a b c var2 var3 Fila 1 a b c
Fila 2 d e f

 -3
Author: dileep balineni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-29 18:43:40