Luego de de esta larga selección de información solucionamos este disgusto que tienen algunos lectores. Te compartimos la solución y deseamos serte de mucha apoyo.
Solución:
ACTUALIZAR: En su lugar, consulte esta respuesta actualizada.
ACTUALIZAR (eddi): Esto ahora se ha implementado en la versión 1.8.11 como un fill
argumento para rbind
. Por ejemplo:
DT1 = data.table(a = 1:2, b = 1:2)
DT2 = data.table(a = 3:4, c = 1:2)
rbind(DT1, DT2, fill = TRUE)
# a b c
#1: 1 1 NA
#2: 2 2 NA
#3: 3 NA 1
#4: 4 NA 2
FR # 4790 agregado ahora – rbind.fill (de plyr) como funcionalidad para fusionar la lista de data.frames / data.tables
Nota 1:
Esta solución utiliza data.table
‘s rbindlist
función para “rbind” lista de data.tables y para esto, asegúrese de usar la versión 1.8.9 debido a este error en las versiones <1.8.9.
Nota 2:
rbindlist
al vincular listas de data.frames / data.tables, a partir de ahora, conservará el tipo de datos de la primera columna. Es decir, si una columna en el primer data.frame es de carácter y la misma columna en el segundo data.frame es “factor”, entonces, rbindlist
dará como resultado que esta columna sea un personaje. Entonces, si su data.frame consistió en todas las columnas de caracteres, entonces, su solución con este método será idéntica al método plyr. De lo contrario, los valores seguirán siendo los mismos, pero algunas columnas serán caracteres en lugar de factores. Tendrá que convertir a “factorizar” usted mismo después. Con suerte, este comportamiento cambiará en el futuro.
Y ahora aquí está usando data.table
(y comparación de evaluación comparativa con rbind.fill
de plyr
):
require(data.table)
rbind.fill.DT <- function(ll)
# changed sapply to lapply to return a list always
all.names <- lapply(ll, names)
unq.names <- unique(unlist(all.names))
ll.m <- rbindlist(lapply(seq_along(ll), function(x)
tt <- ll[[x]]
setattr(tt, 'class', c('data.table', 'data.frame'))
data.table:::settruelength(tt, 0L)
invisible(alloc.col(tt))
tt[, c(unq.names[!unq.names %chin% all.names[[x]]]) := NA_character_]
setcolorder(tt, unq.names)
))
rbind.fill.PLYR <- function(ll)
rbind.fill(ll)
require(microbenchmark)
microbenchmark(t1 <- rbind.fill.DT(ll), t2 <- rbind.fill.PLYR(ll), times=10)
# Unit: seconds
# expr min lq median uq max neval
# t1 <- rbind.fill.DT(ll) 10.8943 11.02312 11.26374 11.34757 11.51488 10
# t2 <- rbind.fill.PLYR(ll) 121.9868 134.52107 136.41375 184.18071 347.74724 10
# for comparison change t2 to data.table
setattr(t2, 'class', c('data.table', 'data.frame'))
data.table:::settruelength(t2, 0L)
invisible(alloc.col(t2))
setcolorder(t2, unique(unlist(sapply(ll, names))))
identical(t1, t2) # [1] TRUE
se debe notar que plyr
's rbind.fill
bordes más allá de este particular data.table
solución hasta un tamaño de lista de aproximadamente 500.
Parcela de evaluación comparativa:
Aquí está el gráfico de las ejecuciones con la longitud de la lista de datos. seq(1000, 10000, by=1000)
. he usado microbenchmark
con 10 repeticiones en cada una de estas diferentes longitudes de lista.
Lo esencial de la evaluación comparativa:
Aquí está la esencia de la evaluación comparativa, en caso de que alguien quiera replicar los resultados.
Ahora eso rbindlist
(y rbind
) por data.table
ha mejorado la funcionalidad y la velocidad con los cambios / confirmaciones recientes en v1.9.3 (versión de desarrollo), y dplyr
tiene una versión más rápida de plyr
's rbind.fill
, llamado rbind_all
, esta respuesta mía parece un poco anticuada.
Aquí está la entrada de NOTICIAS relevante para rbindlist
:
o 'rbindlist' gains 'use.names' and 'fill' arguments and is now implemented entirely in C. Closes #5249
-> use.names by default is FALSE for backwards compatibility (doesn't bind by
names by default)
-> rbind(...) now just calls rbindlist() internally, except that 'use.names'
is TRUE by default, for compatibility with base (and backwards compatibility).
-> fill by default is FALSE. If fill is TRUE, use.names has to be TRUE.
-> At least one item of the input list has to have non-null column names.
-> Duplicate columns are bound in the order of occurrence, like base.
-> Attributes that might exist in individual items would be lost in the bound result.
-> Columns are coerced to the highest SEXPTYPE, if they are different, if/when possible.
-> And incredibly fast ;).
-> Documentation updated in much detail. Closes DR #5158.
Por lo tanto, he comparado las versiones más nuevas (y más rápidas) con datos relativamente más grandes a continuación.
Nuevo punto de referencia:
Crearemos un total de 10,000 tablas de datos con columnas que van de 200 a 300 con el número total de columnas después de la vinculación de 500.
Funciones para crear datos:
require(data.table) ## 1.9.3 commit 1267
require(dplyr) ## commit 1504 devel
set.seed(1L)
names = paste0("V", 1:500)
foo <- function()
cols = sample(200:300, 1)
data = setDT(lapply(1:cols, function(x) sample(10)))
setnames(data, sample(names)[1:cols])
n = 10e3L
ll = vector("list", n)
for (i in 1:n)
.Call("Csetlistelt", ll, i, foo())
Y aquí están los horarios:
## Updated timings on data.table v1.9.5 - three consecutive runs:
system.time(ans1 <- rbindlist(ll, fill=TRUE))
# user system elapsed
# 1.993 0.106 2.107
system.time(ans1 <- rbindlist(ll, fill=TRUE))
# user system elapsed
# 1.644 0.092 1.744
system.time(ans1 <- rbindlist(ll, fill=TRUE))
# user system elapsed
# 1.297 0.088 1.389
## dplyr's rbind_all - Timings for three consecutive runs
system.time(ans2 <- rbind_all(ll))
# user system elapsed
# 9.525 0.121 9.761
# user system elapsed
# 9.194 0.112 9.370
# user system elapsed
# 8.665 0.081 8.780
identical(ans1, setDT(ans2)) # [1] TRUE
Todavía hay algo que ganar si paraleliza ambos rbind.fill
y rbindlist
. Los resultados se hacen con data.table
la versión 1.8.8 como la versión 1.8.9 se bloqueó cuando la probé con la función paralelizada. Entonces los resultados no son idénticos entre data.table
y plyr
, pero son idénticos dentro data.table
o plyr
solución. Significado paralelo plyr
coincide con incomparable plyr
, y viceversa.
Aquí está el punto de referencia / scripts. los parallel.rbind.fill.DT
se ve horrible, pero ese es el más rápido que pude tirar.
require(plyr)
require(data.table)
require(ggplot2)
require(rbenchmark)
require(parallel)
# data.table::rbindlist solutions
rbind.fill.DT <- function(ll)
all.names <- lapply(ll, names)
unq.names <- unique(unlist(all.names))
rbindlist(lapply(seq_along(ll), function(x)
tt <- ll[[x]]
setattr(tt, 'class', c('data.table', 'data.frame'))
data.table:::settruelength(tt, 0L)
invisible(alloc.col(tt))
tt[, c(unq.names[!unq.names %chin% all.names[[x]]]) := NA_character_]
setcolorder(tt, unq.names)
))
parallel.rbind.fill.DT <- function(ll, cluster=NULL)
all.names <- lapply(ll, names)
unq.names <- unique(unlist(all.names))
if(is.null(cluster))
ll.m <- rbindlist(lapply(seq_along(ll), function(x)
tt <- ll[[x]]
setattr(tt, 'class', c('data.table', 'data.frame'))
data.table:::settruelength(tt, 0L)
invisible(alloc.col(tt))
tt[, c(unq.names[!unq.names %chin% all.names[[x]]]) := NA_character_]
setcolorder(tt, unq.names)
))
else
cores <- length(cluster)
sequ <- as.integer(seq(1, length(ll), length.out = cores+1))
Call <- paste(paste("list", seq(cores), sep=""), " = ll[", c(1, sequ[2:cores]+1), ":", sequ[2:(cores+1)], "]", sep="", collapse=", ")
ll <- eval(parse(text=paste("list(", Call, ")")))
rbindlist(clusterApply(cluster, ll, function(ll, unq.names)
rbindlist(lapply(seq_along(ll), function(x, ll, unq.names)
tt <- ll[[x]]
setattr(tt, 'class', c('data.table', 'data.frame'))
data.table:::settruelength(tt, 0L)
invisible(alloc.col(tt))
tt[, c(unq.names[!unq.names %chin% colnames(tt)]) := NA_character_]
setcolorder(tt, unq.names)
, ll=ll, unq.names=unq.names))
, unq.names=unq.names))
# plyr::rbind.fill solutions
rbind.fill.PLYR <- function(ll)
rbind.fill(ll)
parallel.rbind.fill.PLYR <- function(ll, cluster=NULL, magicConst=400)
if(is.null(cluster)
# Function to generate sample data of varying list length
set.seed(45)
sample.fun <- function()
nam <- sample(LETTERS, sample(5:15))
val <- data.frame(matrix(sample(letters, length(nam)*10,replace=TRUE),nrow=10))
setNames(val, nam)
ll <- replicate(10000, sample.fun())
cl <- makeCluster(4, type="SOCK")
clusterEvalQ(cl, library(data.table))
clusterEvalQ(cl, library(plyr))
benchmark(t1 <- rbind.fill.PLYR(ll),
t2 <- rbind.fill.DT(ll),
t3 <- parallel.rbind.fill.PLYR(ll, cluster=cl, 400),
t4 <- parallel.rbind.fill.DT(ll, cluster=cl),
replications=5)
stopCluster(cl)
# Results for rbinding 10000 dataframes
# done with 4 cores, i5 3570k and 16gb memory
# test reps elapsed relative
# rbind.fill.PLYR 5 321.80 16.682
# rbind.fill.DT 5 26.10 1.353
# parallel.rbind.fill.PLYR 5 28.00 1.452
# parallel.rbind.fill.DT 5 19.29 1.000
# checking are results equal
t1 <- as.matrix(t1)
t2 <- as.matrix(t2)
t3 <- as.matrix(t3)
t4 <- as.matrix(t4)
t1 <- t1[order(t1[, 1], t1[, 2]), ]
t2 <- t2[order(t2[, 1], t2[, 2]), ]
t3 <- t3[order(t3[, 1], t3[, 2]), ]
t4 <- t4[order(t4[, 1], t4[, 2]), ]
identical(t2, t4) # TRUE
identical(t1, t3) # TRUE
identical(t1, t2) # FALSE, mismatch between plyr and data.table
Como puede ver, paralizar rbind.fill
lo hizo comparable a data.table
, y podría obtener un aumento marginal de velocidad al paralizar data.table
incluso con este recuento de tramas de datos tan bajo.
Sección de Reseñas y Valoraciones
Puedes añadir valor a nuestro contenido informacional aportando tu experiencia en las ilustraciones.