Een lijst converteren naar een dataframe

Ik heb een geneste lijst met gegevens. De lengte is 132 en elk item is een lijst met lengte 20. Is er een snellemanier om deze structuur om te zetten in een gegevensframe met 132 rijen en 20 kolommen met gegevens?

Hier zijn enkele voorbeeldgegevens om mee te werken:

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

Antwoord 1, autoriteit 100%

Update juli 2020:

De standaard voor de parameter stringsAsFactorsis nu default.stringsAsFactors()wat op zijn beurt FALSEals standaard oplevert.


Ervan uitgaande dat uw lijst met lijsten lheet:

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

Het bovenstaande converteert alle karakterkolommen naar factoren, om dit te voorkomen kunt u een parameter toevoegen aan de data.frame()-aanroep:

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

Antwoord 2, autoriteit 95%

Met rbind

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

Bewerken: vorige versie retourneert data.framevan list‘s in plaats van vectoren (zoals @IanSudbery opmerkte in opmerkingen).


Antwoord 3, autoriteit 32%

U kunt het pakket plyrgebruiken.
Bijvoorbeeld een geneste lijst van het formulier

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

heeft nu een lengte van 4 en elke lijst in lbevat een andere lijst met de lengte 3.
Nu kunt u

uitvoeren

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

en zou hetzelfde resultaat moeten krijgen als in het antwoord @marek en @nico.


4, Autoriteit 27%

De voorbeeldgegevens vaststellen, zodat deze overeenkomt met de originele beschrijving ‘Elk item is een lijst met lengte 20’

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

We kunnen het converteren naar een gegevensframe zoals deze:

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

sapplyconverteert het naar een matrix.
data.frameConverteert de matrix naar een gegevensframe.

resulterend in:


5, Autoriteit 17%

Neem aan dat uw lijst lwordt genoemd,

data.frame(Reduce(rbind, L))

Antwoord 6, autoriteit 6%

Afhankelijk van de structuur van uw lijsten zijn er enkele tidyverse-opties die goed werken met lijsten van ongelijke lengte:

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

Je kunt ook vectoren en dataframes combineren:

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

Antwoord 7, autoriteit 4%

Deze methode gebruikt een tidyverse-pakket (purrr).

De lijst:

x <- as.list(mtcars)

Het converteren naar een dataframe (een tibblemeer specifiek):

library(purrr)
map_df(x, ~.x)

BEWERK: 30 mei 2021

Dit kan worden bereikt met de functie bind_rows()in dplyr.

x <- as.list(mtcars)
dplyr::bind_rows(x)
 A tibble: 32 x 11
     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
 2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
 3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
 4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
 5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
 6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
 7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
 8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
 9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
# ... with 22 more rows

Antwoord 8, autoriteit 4%

Reshape2 levert dezelfde output op als het plyr-voorbeeld hierboven:

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)

opbrengst:

 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

Als je bijna geen pixels meer had, zoudit allemaal in 1 regel kunnen worden gedaan met recast().


Antwoord 9, autoriteit 2%

Meer antwoorden, samen met tijdstippen in het antwoord op deze vraag:
Wat is de meest efficiënte manier om een lijst als dataframe te casten?

De snelste manier om geen dataframe te produceren met lijsten in plaats van vectoren voor kolommen lijkt te zijn (uit het antwoord van 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]])))

Antwoord 10, autoriteit 2%

Uitbreiden op het antwoord van @Marek: als je wilt voorkomen dat strings worden omgezet in factoren en efficiëntie geen probleem is, probeer dan

do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))

Antwoord 11, autoriteit 2%

Voor het algemene geval van diep geneste lijsten met 3 of meer niveauszoals die verkregen uit een geneste JSON:

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

overweeg de aanpak van melt()om de geneste lijst eerst naar een groot formaat te converteren:

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

gevolgd door dcast()en dan weer breed in een overzichtelijke dataset waarbij elke variabele een kolom vormt en elke waarneming een rij:

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

Antwoord 12, autoriteit 2%

Soms kunnen uw gegevens een lijst zijn van lijsten met vectoren van dezelfde lengte.

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

(De binnenste vectoren kunnen ook lijsten zijn, maar ik vereenvoudig het om het leesbaarder te maken).

Vervolgens kunt u de volgende wijziging aanbrengen. Onthoud dat je één niveau tegelijk kunt verwijderen:

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

Gebruik nu je favoriete methode die in de andere antwoorden wordt genoemd:

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

Antwoord 13

Dit is wat uiteindelijk voor mij werkte:

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


Antwoord 14

Voor een parallelle (multicore, multisessie, enz.) oplossing die gebruikmaakt van de purrr-familie van oplossingen, gebruikt u:

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

Waar lde lijst is.

Om het meest efficiënte plan()te benchmarken, kunt u het volgende gebruiken:

library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()

Antwoord 15

De volgende eenvoudige opdracht werkte voor mij:

myDf <- as.data.frame(myList)

Referentie (Quora-antwoord)

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

Maar dit zal mislukken als het niet duidelijk is hoe de lijst naar een dataframe moet worden geconverteerd:

> 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

Opmerking: het antwoord staat in de richting van de titel van de vraag en kan enkele details van de vraag overslaan


Antwoord 16

l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)

Antwoord 17

Een korte (maar misschien niet de snelste) manier om dit te doen, zou zijn om Base R te gebruiken, aangezien een gegevensframe slechts een Lijst met gelijke lengte vectoren . Dus de conversie tussen uw invoerlijst en een gegevens van 30 x 132.Frame zou zijn:

df <- data.frame(l)

Vanaf daar kunnen we deze omzetten naar een 132 x 30 matrix en omzetten naar een dataframe:

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

Als een-voering:

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

De Rownames zullen behoorlijk vervelend zijn om naar te kijken, maar je kunt die altijd hernoemen met

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


18

Elke oplossing die ik heb gevonden lijkt alleen toe te passen wanneer elk object in een listdezelfde length. Ik moest een listconverteren naar een data.framewanneer de lengthvan de objecten in de lijst listwaren van ongelijke length. Hieronder staat de basis Roplossing waar ik mee kwam. Het is ongetwijfeld erg inefficiënt, maar het lijkt erop te werken.

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

Antwoord 19

Hoe zit het met het gebruik van de functie map_samen met een for-lus? Hier is mijn oplossing:

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

waar map_dfrelk van de lijstelementen omzet in een data.frame en vervolgens rbindze samenvoegt.

In jouw geval, denk ik dat het zou zijn:

converted_list <- list_to_df(l)

Antwoord 20

Probeer collapse::unlist2d(afkorting voor ‘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

Antwoord 21

Of je zou het tibble-pakket kunnen gebruiken (van netjesvers):

#create examplelist
l <- replicate(
  132,
  as.list(sample(letters, 20)),
  simplify = FALSE
)
#package tidyverse
library(tidyverse)
#make a dataframe (or use as_tibble)
df <- as_data_frame(l,.name_repair = "unique")

Other episodes