web-dev-qa-db-fra.com

R - liste à trame de données

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

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

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

420
Marek

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.

123
mropa

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

sapply le convertit en matrice. data.frame convertit la matrice en une trame de données.

86
Alex Brown

supposons que votre liste s'appelle L,

data.frame(Reduce(rbind, L))
59
jdeng

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)

51
mnel

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>
29
Matt Dancho

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

16
Jack Ryan

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
11
sbha

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

É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))
9
laubbas

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
9
ecerulm

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]])))
9
Ian Sudbery

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
7
user36302
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
4
zhan2383

C'est ce qui a finalement fonctionné pour moi:

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

4
Amit Kohli

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)

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()
2
trevi

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
0
Ahmad