J'ai une liste de données imbriquée. Sa longueur est de 132 et chaque élément est une liste de longueur 20. Existe-t-il un moyen rapide de convertir cette structure en un cadre de données de 132 lignes et 20 colonnes de données?
Voici quelques exemples de données avec lesquels travailler:
l <- replicate(
132,
list(sample(letters, 20)),
simplify = FALSE
)
En supposant que votre liste de listes s'appelle l
:
df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=T))
Ce qui précède convertira toutes les colonnes de caractères en facteurs. Pour éviter cela, vous pouvez ajouter un paramètre à l’appel data.frame ():
df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)
Avec rbind
do.call(rbind.data.frame, your_list)
Edit: La version précédente renvoie data.frame
de list
'au lieu de vecteurs (comme l'a souligné @IanSudbery dans les commentaires).
Vous pouvez utiliser le package plyr
. Par exemple une liste imbriquée du formulaire
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)
)
a maintenant une longueur de 4 et chaque liste dans l
contient une autre liste de la longueur 3. Maintenant, vous pouvez exécuter
library (plyr)
df <- ldply (l, data.frame)
et devrait obtenir le même résultat que dans la réponse @Marek et @nico.
data.frame(t(sapply(mylistlist,c)))
sapply
le convertit en matrice. data.frame
convertit la matrice en une trame de données.
supposons que votre liste s'appelle L
,
data.frame(Reduce(rbind, L))
Le paquetage data.table
a la fonction rbindlist
qui est une implémentation ultra-rapide de do.call(rbind, list(...))
.
Il peut prendre une liste de lists
, data.frames
ou data.tables
en entrée.
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)
Cela retourne un data.table
héritant de data.frame
.
Si vous vraiment voulez reconvertir en data.frame utilisez as.data.frame(DT)
Le package tibble
a une fonction enframe()
qui résout ce problème en forçant des objets _ imbriqués list
à des objets imbriqués tibble
(cadre de données "rangé"). Voici un bref exemple de R pour 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]>
Puisque vous avez plusieurs nids dans votre liste, l
, vous pouvez utiliser la fonction unlist(recursive = FALSE)
pour supprimer les imbrications inutiles afin d'obtenir une seule liste hiérarchique, puis passer à enframe()
. J'utilise tidyr::unnest()
pour annuler la sortie dans un cadre de données "ordonné" à un seul niveau, qui contient vos deux colonnes (une pour le groupe name
et une pour les observations avec les groupes value
) . Si vous voulez des colonnes larges, vous pouvez ajouter une colonne en utilisant add_column()
qui ne fait que répéter l'ordre des valeurs 132 fois. Alors juste spread()
les valeurs.
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>
Reshape2 produit le même résultat que l'exemple plyr ci-dessus:
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)
rendements:
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 vous étiez presque à court de pixels, vous pouvez faites tout cela en 1 ligne avec refonte ().
Selon la structure de vos listes, certaines options tidyverse
fonctionnent bien avec des listes de longueurs inégales:
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
Vous pouvez également mélanger des vecteurs et des trames de données:
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
Cette méthode utilise un package tidyverse
(purrr).
La liste:
x <- as.list(mtcars)
Le convertir en un cadre de données (un tibble
plus précisément):
library(purrr)
map_df(x, ~.x)
Étendre la réponse de @ Marek: si vous voulez éviter que les chaînes ne soient transformées en facteurs et que l'efficacité ne soit pas un problème, essayez
do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))
Pour le cas général des listes profondément imbriquées avec 3 niveaux ou plus comme celles obtenues à partir d'un JSON imbriqué:
{
"2015": {
"spain": {"population": 43, "GNP": 9},
"sweden": {"population": 7, "GNP": 6}},
"2016": {
"spain": {"population": 45, "GNP": 10},
"sweden": {"population": 9, "GNP": 8}}
}
considérez l'approche de melt()
pour convertir la liste imbriquée en un grand 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
suivi de dcast()
puis à nouveau dans un jeu de données ordonné où chaque variable forme une colonne et chaque observation forme une ligne:
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
Plus de réponses, ainsi que les délais dans la réponse à cette question: Quel est le moyen le plus efficace de transformer une liste en un bloc de données?
Le moyen le plus rapide, qui ne produit pas de cadre de données avec des listes plutôt que des vecteurs pour les colonnes, semble être (d'après la réponse 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]])))
Parfois, vos données peuvent être une liste de listes de vecteurs de même longueur.
lolov = list(list(c(1,2,3),c(4,5,6)), list(c(7,8,9),c(10,11,12),c(13,14,15)) )
(Les vecteurs internes pourraient également être des listes, mais je simplifie pour rendre la lecture plus facile).
Ensuite, vous pouvez faire la modification suivante. Rappelez-vous que vous pouvez annuler la liste un niveau à la fois:
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
Maintenant, utilisez votre méthode préférée mentionnée dans les autres réponses:
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
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
C'est ce qui a finalement fonctionné pour moi:
do.call("rbind", lapply(S1, as.data.frame))
Pour une solution en parallèle (multicœurs, multisession, etc.) utilisant la famille de solutions purrr
, utilisez:
library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)
Où l
est la liste.
Pour évaluer le plus efficace plan()
, vous pouvez utiliser:
library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()
La commande simple suivante a fonctionné pour moi:
myDf <- as.data.frame(myList)
Référence ( 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"
Mais cela échouera s’il n’est pas évident de convertir la liste en trame de données:
> 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