De acuerdo con Creating an R dataframe row-by-row, no es ideal para anexar a data.frame
usando rbind
, ya que crea una copia del data.frame entero cada vez. ¿Cómo acumulo datos en R
dando como resultado un data.frame
sin incurrir en esta penalización? El formato intermedio no necesita ser un data.frame
.Crecimiento de un data.frame de manera eficiente de memoria
Respuesta
Primera aproximación
intenté acceder a cada elemento de una hoja.de.datos preasignados:
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)
}
Pero tracemem se vuelve loco (por ejemplo, el hoja.de.datos está siendo copiado a una nueva dirección cada vez).
enfoque alternativo (no funciona bien)
Un enfoque (no estoy seguro que es más rápido ya que no he referenciado aún) es crear una lista de data.frames, entonces stack
a todos juntos:
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)
Desafortunadamente al crear la lista creo que será difícil preasignar. Por ejemplo:
> tracemem(res)
[1] "<0x79b98b0>"
> res[[2]] <- data.frame()
tracemem[0x79b98b0 -> 0x71da500]:
En otras palabras, la sustitución de un elemento de la lista hace que la lista se copie. Supongo que toda la lista, pero es posible que sea solo ese elemento de la lista. No estoy íntimamente familiarizado con los detalles de la administración de memoria de R.
Probablemente el mejor enfoque
Al igual que con muchos de velocidad o los procesos de memoria limitada en estos días, el mejor enfoque bien puede ser el uso de data.table
en lugar de un data.frame
. Desde data.table
tiene la :=
asignar por el operador de referencia, se puede actualizar sin volver a copiar:
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
Pero, como señala @MatthewDowle, set()
es la forma apropiada de hacer esto dentro de un bucle. Si lo hace, lo hace más rápido aún:
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)
(resultados mostrados abajo)
Benchmarking
con la carrera del bucle de 10.000 veces, tabla de datos es casi un orden total de magnitud más rápido:
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
y comparación de :=
con 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
Tenga en cuenta que aquí es n
10^6 no 10^5 como en los puntos de referencia trazada anteriormente.Entonces hay un orden de magnitud más de trabajo, y el resultado se mide en milisegundos en lugar de segundos. Impresionante de hecho.
Por lo que puedo decir, su último ejemplo no hace crecer el data.table. Simplemente sobrescribe la primera fila 1,000 veces. – Andrie
@Andrie. Oops. Solucionado eso. Gracias por mencionarlo. –
Eso está bien, pero ¿has visto el ejemplo de velocidad en la parte inferior de '?": = "' Comparando ': =' dentro de un bucle con 'set()' dentro de un bucle. ': =' tiene sobrecarga (p.verificando la existencia y el tipo de argumentos pasados a '[.data.table'], que es el motivo por el cual' set() 'se proporciona para el uso dentro de loops. –
Me gusta RSQLite
para el caso: dbWriteTable(...,append=TRUE)
declaraciones durante la recopilación, y dbReadTable
declaración al final.
Si los datos son lo suficientemente pequeños, se puede usar el archivo ": memoria:", si es grande, el disco duro.
Por supuesto, no puede competir en términos de velocidad:
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
Pero podría ser mejor si los data.frame
s tienen más de una fila. Y no necesita saber el número de filas por adelantado.
La idea es genial, pero [está lejos de ser eficiente] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r-data-frame/38052208#38052208). Lo puse en una prueba en otro hilo. –
También podría tener un objeto de la lista vacía donde los elementos se llenan con marcos de datos; luego recoge los resultados al final con sapply o similar. Se puede encontrar un ejemplo here. Esto no incurrirá en las penalidades de hacer crecer un objeto.
Bien, estoy muy sorprendido de que nadie mencionó la conversión a una matriz sin embargo ...
Comparando con el dt.colon y dt.set funciones definidas por Ari B. Friedman, la conversión a una matriz tiene el mejor tiempo de ejecución (un poco más rápido que dt.colon). Todas las afectaciones dentro de una matriz se realizan por referencia, por lo que no se realiza una copia de memoria innecesaria en este código.
CÓDIGO:
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)
RESULTADO:
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
Pros de la utilización de una matriz:
- este es el método más rápido hasta ahora
- que no tiene que aprender/usar objetos data.table
Con de la utilización de una matriz:
- sólo se puede manejar un tipo de datos en una matriz (en particular, si tuviera tipos mixtos en las columnas de su hoja.de.datos, entonces todos serán convertidos a carácter por la línea: estera = as.matrix (dt) # convertir a la matriz)
- 1. cURL bucle de crecimiento de la memoria
- 2. Localizar de manera eficiente las columnas constantes de grupos en un data.frame
- 3. R: cómo convertir datos de data.table() a matriz de manera eficiente (velocidad y memoria)
- 4. Sin memoria cuando se modifica un gran R data.frame
- 5. Procesador XSLT de memoria eficiente
- 6. ¿La lista más eficiente para el método data.frame?
- 7. Crecimiento de ByteBuffer
- 8. ¿La manera más eficiente de crear miniaturas?
- 9. Manera eficiente de resolver Cryptarithms
- 10. Manera eficiente de probar la conexión ODBC
- 11. Repetir filas de un data.frame
- 12. Algoritmo de potencia de memoria eficiente
- 13. Manera eficiente de dibujar en OpenGL ES
- 14. manera eficiente de implementar paginación
- 15. ¿Cómo implementaría la cola de manera eficiente?
- 16. ¿Cómo encoger y ajustar un std :: vector de una manera eficiente con la memoria?
- 17. memoria multivaluemap eficiente
- 18. Common Lisp: Anexar un plist anidado de manera eficiente
- 19. Manera eficiente de almacenar imágenes en android
- 20. Orden de crecimiento
- 21. Manera eficiente de la memoria de insertar una matriz de objetos con Core Data
- 22. R: ¿Cómo reemplazar elementos de un data.frame?
- 23. Uso de múltiples asignadores de manera eficiente
- 24. Cadenas de memoria eficiente en Haskell
- 25. Java: ByteArrayOutputStream eficiente en memoria
- 26. ¿Manera eficiente de encontrar órdenes de par?
- 27. Python: encontrar un duplicado en un recipiente de manera eficiente
- 28. ¿La manera más eficiente de nombrar un avatar de usuario?
- 29. La manera más eficiente de hacer un registro de actividad
- 30. Genere de manera eficiente un enrejado de puntos en python
Editado para dejar claro lo que estoy bastante seguro de que quería decir. Por favor revertir si me equivoqué. –
Si todavía está interesado, [aquí hay otro punto de referencia de otro conjunto de formas diferentes de crecer data.frame] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r -data-frame/38052208 # 38052208) cuando no se conoce el tamaño por adelantado. –