33 votes

La méthode la plus efficace pour convertir une liste en data.frame ?

Je viens d'avoir une conversation avec des collègues à ce sujet, et nous avons pensé que cela vaudrait la peine de voir ce que les gens du pays des SO avaient à dire. Supposons que j'ai une liste de N éléments, où chaque élément est un vecteur de longueur X. Supposons maintenant que je veuille transformer cette liste en un data.frame. Comme pour la plupart des choses dans R, il y a plusieurs façons d'écorcher le chat proverbial, telles que as.dataframe en utilisant le paquet plyr, le comboing do.call con cbind , la pré-affectation du DF et son remplissage, et autres.

Le problème présenté était de savoir ce qui se passe lorsque N ou X (dans notre cas, c'est X) devient extrêmement grand. Existe-t-il une méthode de dépeçage des chats qui soit nettement supérieure lorsque l'efficacité (notamment en termes de mémoire) est primordiale ?

28voto

Joshua Ulrich Points 68776

Puisqu'un data.frame est déjà une liste et que vous savez que chaque élément de la liste a la même longueur (X), la solution la plus rapide serait probablement de mettre à jour le fichier class y row.names attributs :

set.seed(21)
n <- 1e6
x <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
x <- c(x,x,x,x,x,x)

system.time(a <- as.data.frame(x))
system.time(b <- do.call(data.frame,x))
system.time({
  d <- x  # Skip 'c' so Joris doesn't down-vote me! ;-)
  class(d) <- "data.frame"
  rownames(d) <- 1:n
  names(d) <- make.unique(names(d))
})

identical(a, b)  # TRUE
identical(b, d)  # TRUE

Mise à jour - ce qui est ~2x plus rapide que de créer d :

system.time({
  e <- x
  attr(e, "row.names") <- c(NA_integer_,n)
  attr(e, "class") <- "data.frame"
  attr(e, "names") <- make.names(names(e), unique=TRUE)
})

identical(d, e)  # TRUE

Mise à jour 2 - J'ai oublié la consommation de mémoire. La dernière mise à jour fait deux copies de e . L'utilisation du attributes réduit cela à une seule copie.

set.seed(21)
f <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
f <- c(f,f,f,f,f,f)
tracemem(f)
system.time({  # makes 2 copies
  attr(f, "row.names") <- c(NA_integer_,n)
  attr(f, "class") <- "data.frame"
  attr(f, "names") <- make.names(names(f), unique=TRUE)
})

set.seed(21)
g <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
g <- c(g,g,g,g,g,g)
tracemem(g)
system.time({  # only makes 1 copy
  attributes(g) <- list(row.names=c(NA_integer_,n),
    class="data.frame", names=make.names(names(g), unique=TRUE))
})

identical(f,g)  # TRUE

10voto

mnel Points 48160

Cela semble nécessiter un data.table suggestion étant donné que l'efficacité pour les grands ensembles de données est requise. Notamment setattr établit par référence et ne copie pas

library(data.table)
set.seed(21)
n <- 1e6
h <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
h <- c(h,h,h,h,h,h)
tracemem(h)

system.time({h <- as.data.table(h)
            setattr(h, 'names', make.names(names(h), unique=T))})

as.data.table mais il en fait une copie.


Editer - version sans copie

En utilisant la suggestion de @MatthewDowle setattr(h,'class','data.frame') qui sera converti en data.frame par référence ( aucun exemplaire )

set.seed(21)
n <- 1e6
i <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
i <- c(i,i,i,i,i,i)
tracemem(i)

system.time({  
  setattr(i, 'class', 'data.frame')
  setattr(i, "row.names", c(NA_integer_,n))

  setattr(i, "names", make.names(names(i), unique=TRUE))

})

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X