performance - bind_rows - list of dataframes python
Rendimiento de rbind.data.frame (5)
Tengo una lista de marcos de datos para los que estoy seguro de que todos contienen al menos una fila (de hecho, algunos contienen solo una fila, y otros contienen un número dado de filas), y todos tienen las mismas columnas (nombres y tipos). En caso de que importe, también estoy seguro de que no hay NA en ninguna parte de las filas.
La situación se puede simular así:
#create one row
onerowdfr<-do.call(data.frame, c(list(), rnorm(100) , lapply(sample(letters[1:2], 100, replace=TRUE), function(x){factor(x, levels=letters[1:2])})))
colnames(onerowdfr)<-c(paste("cnt", 1:100, sep=""), paste("cat", 1:100, sep=""))
#reuse it in a list
someParts<-lapply(rbinom(200, 1, 14/200)*6+1, function(reps){onerowdfr[rep(1, reps),]})
He establecido los parámetros (de la aleatorización) para que se aproximen a mi verdadera situación.
Ahora, quiero unir todos estos dataframes en un marco de datos. Pensé que usar rbind haría el truco, así:
system.time(
result<-do.call(rbind, someParts)
)
Ahora, en mi sistema (que no es particularmente lento), y con la configuración anterior, esto toma la salida del sistema.tiempo:
user system elapsed
5.61 0.00 5.62
¿Casi 6 segundos para recopilar 254 (en mi caso) filas de 200 variables? Seguramente tiene que haber una manera de mejorar el rendimiento aquí? En mi código, tengo que hacer cosas similares muy a menudo (es una imputación múltiple), así que necesito que esto sea lo más rápido posible.
Esto es ~ 25% más rápido, pero tiene que haber una mejor manera ...
system.time({
N <- do.call(sum, lapply(someParts, nrow))
SP <- as.data.frame(lapply(someParts[[1]], function(x) rep(x,N)))
k <- 0
for(i in 1:length(someParts)) {
j <- k+1
k <- k + nrow(someParts[[i]])
SP[j:k,] <- someParts[[i]]
}
})
No es un gran estímulo, pero el intercambio de rbind
para rbind.fill
del paquete plyr
aproximadamente el 10% del tiempo de ejecución (con el conjunto de datos de muestra, en mi máquina).
¿Puedes construir tus matrices con variables numéricas solamente y convertir a un factor al final? rbind
es mucho más rápido en matrices numéricas.
En mi sistema, usando marcos de datos:
> system.time(result<-do.call(rbind, someParts))
user system elapsed
2.628 0.000 2.636
Construyendo la lista con todas las matrices numéricas en su lugar:
onerowdfr2 <- matrix(as.numeric(onerowdfr), nrow=1)
someParts2<-lapply(rbinom(200, 1, 14/200)*6+1,
function(reps){onerowdfr2[rep(1, reps),]})
resulta en un rbind
mucho más rbind
.
> system.time(result2<-do.call(rbind, someParts2))
user system elapsed
0.001 0.000 0.001
EDITAR: Aquí hay otra posibilidad; simplemente combina cada columna por turno.
> system.time({
+ n <- 1:ncol(someParts[[1]])
+ names(n) <- names(someParts[[1]])
+ result <- as.data.frame(lapply(n, function(i)
+ unlist(lapply(someParts, `[[`, i))))
+ })
user system elapsed
0.810 0.000 0.813
Sin embargo, aún no es tan rápido como el uso de matrices.
EDICION 2:
Si solo tiene números y factores, no es tan difícil convertir todo en numérico, rbind
y convertir las columnas necesarias en factores. Esto supone que todos los factores tienen exactamente los mismos niveles. La conversión a un factor a partir de un número entero también es más rápida que a partir de un valor numérico, por lo que forzo al número entero primero.
someParts2 <- lapply(someParts, function(x)
matrix(unlist(x), ncol=ncol(x)))
result<-as.data.frame(do.call(rbind, someParts2))
a <- someParts[[1]]
f <- which(sapply(a, class)=="factor")
for(i in f) {
lev <- levels(a[[i]])
result[[i]] <- factor(as.integer(result[[i]]), levels=seq_along(lev), labels=lev)
}
El tiempo en mi sistema es:
user system elapsed
0.090 0.00 0.091
Asegúrate de que estás uniendo el marco de datos al marco de datos. Se topó con una gran degradación de perf cuando se vincula la lista al marco de datos.
Si realmente quiere manipular su data.frame
es más rápido, le sugiero que use el paquete data.table
y la función rbindlist()
. No realicé pruebas exhaustivas, pero para mi conjunto de datos (3000 marcos de datos, 1000 filas x 40 columnas cada uno) rbindlist()
toma solo 20 segundos.