tabla - R-lista al marco de datos
tabla contenido arcgis (17)
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
)
test1 <- lista (c (a = ''a'', b = ''b'', c = ''c''), c (a = ''d'', b = ''e'', c = ''f'')) as.data .frame (test1) abc 1 abc 2 def
test2 <- lista (c (''a'', ''b'', ''c''), c (a = ''d'', b = ''e'', c = ''f''))
as.data.frame (test2) abc 1 abc 2 def
test3 <- lista (''Fila1'' = c (a = ''a'', b = ''b'', c = ''c''), ''Fila2'' = c (a = ''d'', var2 = ''e'', var3 = ''F''))
as.data.frame (test3) abc var2 var3 Fila1 abc
Fila2 def
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 que sea más fácil de leer).
Entonces puedes hacer la siguiente modificación. Recuerda que puedes anular 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
Con rbind
do.call(rbind.data.frame, your_list)
Edición: la versión anterior devuelve data.frame
de la list
en lugar de vectores (como @IanSudbery señaló en los comentarios).
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 puedes 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
El paquete data.table
tiene la función rbindlist
que es una implementación ultrarrá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 una data.table
de data.frame
.
Si realmente desea volver a convertir a un data.frame use as.data.frame(DT)
El paquete tibble
tiene una función enframe()
que resuelve este problema al coaccionar objetos de list
anidadas en objetos anidados tibble
(marco de datos "ordenado"). Aquí hay un breve ejemplo de R para 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]>
Como tiene varios nidos en su lista, l
, puede usar unlist(recursive = FALSE)
para eliminar los anidamientos innecesarios para obtener una sola lista jerárquica y luego pasar a enframe()
. Uso tidyr::unnest()
para anular la salida en un marco de datos "tidy" de un solo nivel, que tiene sus dos columnas (una para el name
del grupo y otra para las observaciones con el value
los grupos). Si desea que las columnas se add_column()
, puede agregar una columna usando add_column()
que simplemente repite el orden de los valores 132 veces. Entonces solo se 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>
Este método utiliza un paquete tidyverse
( purrr ).
La lista:
x <- as.list(mtcars)
Convertirlo en un marco de datos (un tibble
más específicamente):
library(purrr)
map_df(x, ~.x)
Esto es lo que finalmente me funcionó:
do.call("rbind", lapply(S1, as.data.frame))
Extenderse en la respuesta de @ Marek: si quiere evitar que las cadenas se conviertan en factores y eficiencia no es una preocupación intentar
{
"2015": {
"spain": {"population": 43, "GNP": 9},
"sweden": {"population": 7, "GNP": 6}},
"2016": {
"spain": {"population": 45, "GNP": 10},
"sweden": {"population": 9, "GNP": 8}}
}
Más respuestas, junto con los tiempos en la respuesta a esta pregunta: ¿Cuál es la forma más eficiente de emitir una lista como marco de datos?
La forma más rápida, que no produce un marco de datos con listas en lugar de vectores para columnas, parece ser (según 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]])))
Para el caso general de listas profundamente anidadas con 3 o más niveles como los obtenidos de un JSON anidado:
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
Considere el enfoque de melt()
para convertir primero la lista anidada a un formato alto:
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
seguido por dcast()
luego a lo ancho nuevamente en un conjunto de datos ordenado donde cada variable forma una columna y cada observación forma una fila:
do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))
Puedes usar el paquete plyr
. Por ejemplo una lista anidada del formulario.
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)
)
ahora tiene una longitud de 4 y cada lista en l
contiene otra lista de la longitud 3. Ahora puede ejecutar
library (plyr)
df <- ldply (l, data.frame)
y debería obtener el mismo resultado que en la respuesta @Marek y @nico.
Reshape2 produce el mismo resultado que el ejemplo de 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)
rendimientos
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 w / recast ().
Supongamos que su lista se llama L
,
data.frame(Reduce(rbind, L))
Suponiendo que su 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 la llamada data.frame ():
df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)
data.frame(t(sapply(mylistlist,c)))
sapply
convierte en una matriz. data.frame
convierte la matriz en un marco de datos.
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)