J'ai une grande liste de plus de 30000 éléments. Il existe des vecteurs de longueurs différentes et je souhaite convertir la liste en un dataframe, où chaque vecteur représente une ligne et ses valeurs sont réparties sur plusieurs colonnes. Voici un exemple simulé de la liste:
# [,1] [,2] [,3] [,4] [,5] [,6] #a 1 2 3 4 5 6 #b 7 8 9 NA NA NA #c 10 11 NA NA NA NA
Ma sortie souhaitée ressemble à ceci:
lst <- list(a = c(1,2,4,5,6), c = c(7,8,9), c = c(10,11))
4 Réponses :
L'astuce consiste à créer des vecteurs de longueur égale. Il semble également que vous souhaitiez disposer d'une matrice en sortie.
lst <- list(a = c(1,2,4,5,6), c = c(7,8,9), c = c(10,11))
convert <-function(lst){
Reduce(function(x,y){
n <- max(length(x), length(y))
length(x) <- n
length(y) <- n
rbind(x,y,deparse.level = 0)
},
lst)
}
convert2 <- function(lst){
t(sapply(lst, "length<-", max(lengths(lst))))
}
convert3 <- function(lst){
t(as.data.frame(lapply(lst, "length<-", max(lengths(lst)))))
}
microbenchmark::microbenchmark(convert(lst),
convert2(lst),
convert3(lst))
#Unit: microseconds
# expr min lq mean median uq max neval
# convert(lst) 41.962 50.0725 106.47314 62.2375 68.408 4392.895 100
# convert2(lst) 28.209 33.6755 69.93855 40.7280 45.136 2298.002 100
# convert3(lst) 292.673 306.6005 381.59504 319.1180 333.399 2887.929 100
Sortie
# [,1] [,2] [,3] [,4] [,5] # [1,] 1 2 4 5 6 # [2,] 7 8 9 NA NA # [3,] 10 11 NA NA NA
Vous pouvez réinitialiser les noms des lignes à ce stade.
MISE À JOUR Horaires pour ceux qui sont intéressés:
Reduce(function(x,y){
n <- max(length(x), length(y))
length(x) <- n
length(y) <- n
rbind(x,y,deparse.level = 0)
},
list(a = c(1,2,4,5,6), c = c(7,8,9), c = c(10,11)))
Vous pouvez faire:
t(sapply(lst, "length<-", max(lengths(lst)))) # [,1] [,2] [,3] [,4] [,5] #a 1 2 4 5 6 #c 7 8 9 NA NA #c 10 11 NA NA NA
Ou comme @Andrew l'a souligné, vous pouvez faire:
t(as.data.frame(lapply(lst, "length<-", max(lengths(lst))))) # [,1] [,2] [,3] [,4] [,5] #a 1 2 4 5 6 #c 7 8 9 NA NA #c.1 10 11 NA NA NA
Pas besoin d'envelopper as.data.frame si vous le transposez après. Vous pouvez utiliser sapply en considérant que t se convertira de toute façon en matrice.
renvoie l'erreur: Erreur dans (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,: les arguments impliquent un nombre de lignes différent: 1, 0
Voici une option R de base:
# Create a vector for number of times an NA needs to be padded na_nums <- max(lengths(lst)) - lengths(lst) # Transpose results after patting NA's using mapply t(mapply(c, lst, sapply(na_nums, rep, x = NA))) [,1] [,2] [,3] [,4] [,5] a 1 2 4 5 6 c 7 8 9 NA NA c 10 11 NA NA NA
C'était ma première impulsion.
lst <- list(a = c(1,2,4,5,6), c = c(7,8,9), c = c(10,11))
# build out a list of 30,000 elements.
lst <- lst[sample(1:3, 30000, replace = TRUE)]
library(microbenchmark)
microbenchmark(
benjamin = {
max_len <- max(vapply(lst,
FUN = length,
FUN.VALUE = numeric(1)))
lst <- lapply(lst,
function(x, max_len) c(x, rep(NA, max_len - length(x))),
max_len)
# Form a matrix
do.call("rbind", lst)
},
slava = {
Reduce(function(x,y){
n <- max(length(x), length(y))
length(x) <- n
length(y) <- n
rbind(x,y,deparse.level = 0)
},
lst)
},
andrew = {
na_nums <- max(lengths(lst)) - lengths(lst)
# Transpose results after patting NA's using mapply
t(mapply(c, lst, sapply(na_nums, rep, x = NA)))
},
matt = {
t(as.data.frame(lapply(lst, "length<-", max(lengths(lst)))))
}
)
Unit: milliseconds
expr min lq mean median uq max neval
benjamin 77.08337 91.42793 117.9376 106.97656 122.53898 191.6612 5
slava 32383.10840 32962.57589 32976.6662 33071.40314 33180.70634 33285.5372 5
andrew 60.91803 66.74401 87.1645 71.92043 77.78805 158.4520 5
matt 1685.09158 1702.19796 1759.2741 1737.01949 1760.86237 1911.1993 5
C'est un peu verbeux, et certaines des autres solutions sont plutôt élégantes. Puisque vous dites que votre liste contient plus de 30 000 éléments, j'étais curieux de savoir comment ils fonctionneraient sur une liste de 30 000 éléments.
Si c'est quelque chose que vous devez faire souvent, vous voudrez peut-être adopter l'approche d'Andrew .
max_len <- max(vapply(lst,
FUN = length,
FUN.VALUE = numeric(1)))
lst <- lapply(lst,
function(x, max_len) c(x, rep(NA, max_len - length(x))),
max_len)
# Form a matrix
do.call("rbind", lst)
Merci pour les horaires avec un plus grand ensemble de données.
C'est bien de voir un exemple court et précis reproductible avec la sortie souhaitée!
J'ai l'impression que vous voulez vraiment une matrice plutôt qu'un data.frame - difficile à dire avec certitude sans en savoir plus sur ce que vous faites, mais gardez à l'esprit que, même dans R, les données tabulaires ne doivent pas nécessairement être dans des données frame s'il n'est pas orienté colonne.
J'ai ajouté des horaires