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 stringsAsFactors
is nu default.stringsAsFactors()
wat op zijn beurt FALSE
als standaard oplevert.
Ervan uitgaande dat uw lijst met lijsten l
heet:
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.frame
van list
‘s in plaats van vectoren (zoals @IanSudbery opmerkte in opmerkingen).
Antwoord 3, autoriteit 32%
U kunt het pakket plyr
gebruiken.
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 l
bevat 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)))
sapply
converteert het naar een matrix.
data.frame
Converteert de matrix naar een gegevensframe.
resulterend in:
5, Autoriteit 17%
Neem aan dat uw lijst l
wordt 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 tibble
meer 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 l
de 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 list
dezelfde length
. Ik moest een list
converteren naar een data.frame
wanneer de length
van de objecten in de lijst list
waren van ongelijke length
. Hieronder staat de basis R
oplossing 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_dfr
elk van de lijstelementen omzet in een data.frame en vervolgens rbind
ze 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")