dataframes bind_rows performance r dataframe rbind

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.