2010-05-17 20 views
208

Tengo un código que en un lugar termina con una lista de marcos de datos que realmente quiero convertir a un solo marco de datos grande.Convierta una lista de marcos de datos en un marco de datos

Recibí algunos consejos de un earlier question que intentaba hacer algo similar pero era más complejo.

He aquí un ejemplo de lo que estoy empezando con (esto se simplifica enormemente para la ilustración):

listOfDataFrames <- vector(mode = "list", length = 100) 

for (i in 1:100) { 
    listOfDataFrames[[i]] <- data.frame(a=sample(letters, 500, rep=T), 
          b=rnorm(500), c=rnorm(500)) 
} 

Actualmente estoy usando esto:

df <- do.call("rbind", listOfDataFrames) 
+0

Véase también esta pregunta: http://stackoverflow.com/questions/2209258/merge-several-data-frames-into-one-data-frame-with-a-loop/2209371 – Shane

+12

El 'hacer .call ("rbind", list) 'idioma es lo que he usado antes también. ¿Por qué necesitas la 'lista' inicial? –

+1

Shane, yo acababa de hacer exactamente la misma prueba y entendí mi error. Eres rápido;) –

Respuesta

157

Otra opción es utilizar un plyr función:

df <- ldply(listOfDataFrames, data.frame) 

Esto es un poco más lento que el original:

> system.time({ df <- do.call("rbind", listOfDataFrames) }) 
    user system elapsed 
    0.25 0.00 0.25 
> system.time({ df2 <- ldply(listOfDataFrames, data.frame) }) 
    user system elapsed 
    0.30 0.00 0.29 
> identical(df, df2) 
[1] TRUE 

Mi conjetura es que el uso de do.call("rbind", ...) va a ser el enfoque más rápido que va a encontrar a menos que puede hacer algo como (a) utilizar un matrices en lugar de un data.frames y (b) asignar previamente la matriz final y asignarle en lugar de hacerlo crecer.

Editar 1:

Basado en el comentario de Hadley, aquí está la versión más reciente de rbind.fill desde CRAN:

> system.time({ df3 <- rbind.fill(listOfDataFrames) }) 
    user system elapsed 
    0.24 0.00 0.23 
> identical(df, df3) 
[1] TRUE 

Esto es más fácil que rbind, y marginalmente más rápido (estos tiempos se sostienen a través de múltiples carreras). Y hasta donde yo lo entiendo, the version of plyr on github es incluso más rápido que esto.

+21

rbind.fill en la última versión de plyr es considerablemente más rápido que do.call y rbind – hadley

+1

interesante. para mí rbind.fill fue el más rápido. Lo suficientemente extraño, do.call/rbind no devolvió idéntico verdadero, incluso si no podría encontrar la diferencia. Los otros dos eran iguales pero plyr era más lento. –

+0

'I()' podría reemplazar 'data.frame' en su llamada' ldply' – baptiste

80

Para completar, creo que las respuestas a esta pregunta requieren una actualización. "Creo que usar do.call("rbind", ...) va a ser el enfoque más rápido que encontrarás ..." Probablemente fue cierto para mayo de 2010 y algún tiempo después, pero en septiembre de 2011 se introdujo una nueva función rbindlist en la versión del paquete data.table 1.8.2, con una observación que "Esto hace lo mismo que do.call("rbind",l), pero mucho más rápido". ¿Cuanto más rápido?

library(rbenchmark) 
benchmark(
    do.call = do.call("rbind", listOfDataFrames), 
    plyr_rbind.fill = plyr::rbind.fill(listOfDataFrames), 
    plyr_ldply = plyr::ldply(listOfDataFrames, data.frame), 
    data.table_rbindlist = as.data.frame(data.table::rbindlist(listOfDataFrames)), 
    replications = 100, order = "relative", 
    columns=c('test','replications', 'elapsed','relative') 
) 

    test replications elapsed relative 
4 data.table_rbindlist   100 0.11 1.000 
1    do.call   100 9.39 85.364 
2  plyr_rbind.fill   100 12.08 109.818 
3   plyr_ldply   100 15.14 137.636 
+1

Muchas gracias por esto - me estaba tirando de los pelos porque mis datos los conjuntos se estaban volviendo demasiado grandes para 'completar' un montón de marcos de datos largos y fundidos.De todos modos, obtuve una aceleración increíble usando su sugerencia 'rbindlist'. – KarateSnowMachine

+7

Y uno más para completar: 'dplyr :: rbind_all (listOfDataFrames)' también hará el truco. – andyteucher

+1

¿existe un equivalente a 'rbindlist' pero que añada los marcos de datos por columna? algo así como una lista cbindlist? –

35

Existe también bind_rows(x, ...) en dplyr.

> system.time({ df.Base <- do.call("rbind", listOfDataFrames) }) 
    user system elapsed 
    0.08 0.00 0.07 
> 
> system.time({ df.dplyr <- as.data.frame(bind_rows(listOfDataFrames)) }) 
    user system elapsed 
    0.01 0.00 0.02 
> 
> identical(df.Base, df.dplyr) 
[1] TRUE 
+0

técnicamente hablando, no necesita el archivo .data - todo lo que hace es exclusivamente un data.frame, a diferencia de también un table_df (from deplyr) – user1617979

30

bind-plot

Código:

library(microbenchmark) 

dflist <- vector(length=10,mode="list") 
for(i in 1:100) 
{ 
    dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260), 
          c=rep(LETTERS,10),d=rep(LETTERS,10)) 
} 


mb <- microbenchmark(
plyr::rbind.fill(dflist), 
dplyr::bind_rows(dflist), 
data.table::rbindlist(dflist), 
plyr::ldply(dflist,data.frame), 
do.call("rbind",dflist), 
times=1000) 

ggplot2::autoplot(mb) 

Sesión:

R version 3.3.0 (2016-05-