Selon Creating an R dataframe row-by-row, il n'est pas idéal d'ajouter à un data.frame
en utilisant rbind
, car il crée une copie de l'ensemble data.frame à chaque fois. Comment puis-je accumuler des données au R
pour obtenir un data.frame
sans encourir cette pénalité? Le format intermédiaire n'a pas besoin d'être un data.frame
.Augmenter un data.frame d'une manière efficace en mémoire
Répondre
Première approche
J'essayé d'accéder à chaque élément d'une data.frame pré-alloué:
res <- data.frame(x=rep(NA,1000), y=rep(NA,1000))
tracemem(res)
for(i in 1:1000) {
res[i,"x"] <- runif(1)
res[i,"y"] <- rnorm(1)
}
Mais tracemem devient fou (par exemple, la data.frame est en cours de copie à une nouvelle adresse chaque fois).
approche alternative (ne fonctionne pas non plus)
Une approche (pas sûr qu'il est plus rapide que je ne l'ai pas encore benchmarkée) est de créer une liste de data.frames, puis les stack
tous ensemble:
makeRow <- function() data.frame(x=runif(1),y=rnorm(1))
res <- replicate(1000, makeRow(), simplify=FALSE) # returns a list of data.frames
library(taRifx)
res.df <- stack(res)
Malheureusement, lors de la création de la liste, je pense que vous aurez du mal à pré-allouer. Par exemple:
> tracemem(res)
[1] "<0x79b98b0>"
> res[[2]] <- data.frame()
tracemem[0x79b98b0 -> 0x71da500]:
En d'autres termes, le remplacement d'un élément de la liste entraîne la copie de la liste. Je suppose que toute la liste, mais il est possible que ce soit seulement cet élément de la liste. Je ne suis pas intimement familier avec les détails de la gestion de la mémoire de R.
Probablement la meilleure approche
Comme beaucoup de vitesse ou les processus de mémoire limitée ces jours-ci, la meilleure approche pourrait bien être d'utiliser data.table
au lieu d'un data.frame
. Depuis data.table
a le :=
assign par l'opérateur de référence, il peut mettre à jour sans re-copie:
library(data.table)
dt <- data.table(x=rep(0,1000), y=rep(0,1000))
tracemem(dt)
for(i in 1:1000) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
# note no message from tracemem
Mais comme le souligne @MatthewDowle sur, set()
est la manière appropriée de le faire dans une boucle. Cela rend encore plus vite:
library(data.table)
n <- 10^6
dt <- data.table(x=rep(0,n), y=rep(0,n))
dt.colon <- function(dt) {
for(i in 1:n) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
}
dt.set <- function(dt) {
for(i in 1:n) {
set(dt,i,1L, runif(1))
set(dt,i,2L, rnorm(1))
}
}
library(microbenchmark)
m <- microbenchmark(dt.colon(dt), dt.set(dt),times=2)
(résultats affichés ci-dessous)
Analyse comparative
Avec la course en boucle 10.000 fois, une table de données est presque un ordre plein de grandeur plus rapide:
Unit: seconds
expr min lq median uq max
1 test.df() 523.49057 523.49057 524.52408 525.55759 525.55759
2 test.dt() 62.06398 62.06398 62.98622 63.90845 63.90845
3 test.stack() 1196.30135 1196.30135 1258.79879 1321.29622 1321.29622
et la comparaison des :=
avec set()
:
> m
Unit: milliseconds
expr min lq median uq max
1 dt.colon(dt) 654.54996 654.54996 656.43429 658.3186 658.3186
2 dt.set(dt) 13.29612 13.29612 15.02891 16.7617 16.7617
Notez que n
ici est 10^10^6 pas 5 comme dans les repères tracés ci-dessus.Il y a donc un ordre de grandeur de travail supplémentaire, et le résultat est mesuré en millisecondes et non en secondes. Impressionnant en effet.
Pour autant que je sache, votre dernier exemple ne développe pas la table data.table. Vous écrasez simplement la première rangée 1000 fois. – Andrie
@Andrie. Oops. Corrigé cela. Merci de l'avoir signalé. –
C'est bien mais avez-vous vu l'exemple de vitesse en bas de '?": = "' Comparer ': =' dans une boucle à 'set()' dans une boucle. ': =' a des frais généraux (par ex.vérifier l'existence et le type des arguments passés à '[.data.table'], c'est pourquoi' set() 'est prévu pour être utilisé dans les boucles. –
J'aime RSQLite
d'ailleurs: dbWriteTable(...,append=TRUE)
déclarations lors de la collecte, et dbReadTable
déclaration à la fin.
Si les données sont assez petites, on peut utiliser le fichier ": memory:", s'il est grand, le disque dur.
Bien sûr, il ne peut rivaliser en termes de vitesse:
makeRow <- function() data.frame(x=runif(1),y=rnorm(1))
library(RSQLite)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
collect1 <- function(n) {
for (i in 1:n) dbWriteTable(con, "test", makeRow(), append=TRUE)
dbReadTable(con, "test", row.names=NULL)
}
collect2 <- function(n) {
res <- data.frame(x=rep(NA, n), y=rep(NA, n))
for(i in 1:n) res[i,] <- makeRow()[1,]
res
}
> system.time(collect1(1000))
User System verstrichen
7.01 0.00 7.05
> system.time(collect2(1000))
User System verstrichen
0.80 0.01 0.81
Mais il pourrait mieux si les data.frame
s ont plus d'une ligne. Et vous n'avez pas besoin de connaître le nombre de lignes à l'avance.
L'idée est cool, mais il [est loin d'être efficace] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r-data-frame/38052208#38052208). Je l'ai mis sur un test sur un autre fil. –
Vous pouvez également avoir un objet de liste vide où les éléments sont remplis de données; puis recueillir les résultats à la fin avec sapply ou similaire. Un exemple peut être trouvé here. Cela n'encourra pas les pénalités de croissance d'un objet.
Eh bien, je suis très surpris que personne n'a mentionné la conversion à une matrice encore ...
comparaison avec le dt.colon et dt.set fonctions définies par Ari B. Friedman, la conversion à une matrice a le meilleur temps de fonctionnement (légèrement plus rapide que dt.colon). Toutes les affectations à l'intérieur d'une matrice sont effectuées par référence, donc il n'y a pas de copie de mémoire inutile effectuée dans ce code.
CODE:
library(data.table)
n <- 10^4
dt <- data.table(x=rep(0,n), y=rep(0,n))
use.matrix <- function(dt) {
mat = as.matrix(dt) # converting to matrix
for(i in 1:n) {
mat[i,1] = runif(1)
mat[i,2] = rnorm(1)
}
return(as.data.frame(mat)) # converting back to a data.frame
}
dt.colon <- function(dt) { # same as Ari's function
for(i in 1:n) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
}
dt.set <- function(dt) { # same as Ari's function
for(i in 1:n) {
set(dt,i,1L, runif(1))
set(dt,i,2L, rnorm(1))
}
}
library(microbenchmark)
microbenchmark(dt.colon(dt), dt.set(dt), use.matrix(dt),times=10)
RÉSULTAT:
Unit: milliseconds
expr min lq median uq max neval
dt.colon(dt) 7107.68494 7193.54792 7262.76720 7277.24841 7472.41726 10
dt.set(dt) 93.25954 94.10291 95.07181 97.09725 99.18583 10
use.matrix(dt) 48.15595 51.71100 52.39375 54.59252 55.04192 10
Avantages de l'utilisation d'une matrice:
- c'est la méthode la plus rapide jusqu'à présent
- vous n'avez pas apprendre/utiliser les objets de données.table
Con d'utiliser une matrice:
- vous ne pouvez gérer un type de données dans une matrice (en particulier, si vous aviez des types mixtes dans les colonnes de votre data.frame, ils seront tous convertis à caractère par la ligne: mat = as.matrix (dt) # conversion à matrice)
- 1. Java quoi de plus efficace en mémoire?
- 2. Augmenter la mémoire mysql
- 3. Groupby en mémoire efficace en Python
- 4. Calculer un percentile de colonne de données de manière efficace
- 5. Manière efficace de lire un fichier
- 6. Conversion d'une grande liste en un data.frame
- 7. Augmenter la mémoire pour la mémoire partagée
- 8. manière efficace d'imprimer mon format
- 9. manière efficace et correcte pour charger tableau avec un halo de global à mémoire partagée
- 10. manière efficace de faire une application multiview?
- 11. glDrawArray + VBO augmenter l'empreinte mémoire
- 12. Méthode efficace d'échange d'octets dans un fichier mappé en mémoire
- 13. Pourquoi ifelse convertit un data.frame en une liste: ifelse (TRUE, data.frame (1), 0))! = Data.frame (1)?
- 14. Comment augmenter la mémoire de org.eclipse.ant.core.antRunner
- 15. Manière efficace d'ajouter un paramètre shared_ptr à un conteneur?
- 16. Une implémentation SHA1 à mémoire efficace
- 17. double match un data.frame
- 18. data.frame en R
- 19. Augmenter la mémoire du projet opencascade
- 20. Augmenter l'allocation de mémoire de Cygwin
- 21. Manière efficace d'imprimer MIPS Int Array
- 22. manière efficace d'écrire plusieurs fonctions jquery
- 23. manière efficace d'exécuter le script dans Java
- 24. Manière efficace de modifier 100GB table
- 25. Parcourez NSMutableArray d'objets personnalisés de la manière la plus efficace sur le plan de la mémoire.
- 26. Manière plus efficace d'écrire ceci si/sinon
- 27. Manière plus efficace d'écrire ce script
- 28. Appel d'une méthode en Python de manière plus efficace
- 29. Manière efficace pour gérer les rôles d'utilisateur
- 30. Manière efficace d'effectuer plusieurs recherches dans un contrôle personnalisé
modifié pour préciser ce que je suis sûr que vous vouliez dire. S'il vous plaît revenir si j'ai foiré. –
Si vous êtes toujours intéressé, [voici une autre référence d'autre ensemble de manière différente de développer data.frame] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r -data-frame/38052208 # 38052208) lorsque vous ne connaissez pas la taille à l'avance. –