Difficoltà nel convertire una lunga lista di data.frames (~ 1 milione) in single data.frame usando do.call e ldply

So che ci sono molte domande qui in SO su come convertire un elenco di data.frames in un singolo data.frame usando do.call o ldply, ma queste domande riguardano la comprensione del funzionamento interno di entrambi i metodi e il tentativo di capire perché Non riesco neanche a lavorare per concatenare una lista di quasi 1 milione di df della stessa struttura, stessi nomi di campi, ecc. In un singolo data.frame. Ogni data.frame è di una riga e 21 colonne.

I dati sono iniziati come file JSON, che ho convertito in liste usando fromJSON, quindi ho eseguito un altro lapply per estrarre parte dell’elenco e convertito in data.frame e ho terminato con un elenco di data.frames.

Ho provato:

df <- do.call("rbind", list) df <- ldply(list) 

ma ho dovuto uccidere il processo dopo averlo lasciato funzionare fino a 3 ore e non ottenere nulla indietro.

C’è un metodo più efficiente per farlo? Come posso risolvere ciò che sta accadendo e perché ci vuole così tanto tempo?

Cordiali saluti – Sto usando il server RStudio su un server quad-core da 72 GB con RHEL, quindi non credo che la memoria sia il problema. sessioneInformazioni di seguito:

 > sessionInfo() R version 2.14.1 (2011-12-22) Platform: x86_64-redhat-linux-gnu (64-bit) locale: [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 [7] LC_PAPER=C LC_NAME=C [9] LC_ADDRESS=C LC_TELEPHONE=C [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] multicore_0.1-7 plyr_1.7.1 rjson_0.2.6 loaded via a namespace (and not attached): [1] tools_2.14.1 > 

Dato che stai cercando prestazioni, sembra che dovrebbe essere suggerita una soluzione data.table .

C’è una funzione rbindlist che è la same ma molto più veloce di do.call(rbind, list)

 library(data.table) X <- replicate(50000, data.table(a=rnorm(5), b=1:5), simplify=FALSE) system.time(rbindlist.data.table <- rbindlist(X)) ## user system elapsed ## 0.00 0.01 0.02 

È anche molto veloce per un elenco di data.frame

 Xdf <- replicate(50000, data.frame(a=rnorm(5), b=1:5), simplify=FALSE) system.time(rbindlist.data.frame <- rbindlist(Xdf)) ## user system elapsed ## 0.03 0.00 0.03 

Per confronto

 system.time(docall <- do.call(rbind, Xdf)) ## user system elapsed ## 50.72 9.89 60.88 

E un buon benchmarking

 library(rbenchmark) benchmark(rbindlist.data.table = rbindlist(X), rbindlist.data.frame = rbindlist(Xdf), docall = do.call(rbind, Xdf), replications = 5) ## test replications elapsed relative user.self sys.self ## 3 docall 5 276.61 3073.444445 264.08 11.4 ## 2 rbindlist.data.frame 5 0.11 1.222222 0.11 0.0 ## 1 rbindlist.data.table 5 0.09 1.000000 0.09 0.0 

e contro le soluzioni di @JoshuaUlrich

 benchmark(use.rbl.dt = rbl.dt(X), use.rbl.ju = rbl.ju (Xdf), use.rbindlist =rbindlist(X) , replications = 5) ## test replications elapsed relative user.self ## 3 use.rbindlist 5 0.10 1.0 0.09 ## 1 use.rbl.dt 5 0.10 1.0 0.09 ## 2 use.rbl.ju 5 0.33 3.3 0.31 

Non sono sicuro che tu abbia davvero bisogno di usare as.data.frame , perché un data.table eredita la class data.frame

rbind.data.frame esegue molti controlli non necessari. Questa dovrebbe essere una trasformazione piuttosto veloce se fai esattamente ciò che vuoi.

 # Use data from Josh O'Brien's post. set.seed(21) X <- replicate(50000, data.frame(a=rnorm(5), b=1:5), simplify=FALSE) system.time({ Names <- names(X[[1]]) # Get data.frame names from first list element. # For each name, extract its values from each data.frame in the list. # This provides a list with an element for each name. Xb <- lapply(Names, function(x) unlist(lapply(X, `[[`, x))) names(Xb) <- Names # Give Xb the correct names. Xb.df <- as.data.frame(Xb) # Convert Xb to a data.frame. }) # user system elapsed # 3.356 0.024 3.388 system.time(X1 <- do.call(rbind, X)) # user system elapsed # 169.627 6.680 179.675 identical(X1,Xb.df) # [1] TRUE 

Ispirato alla risposta data.table, ho deciso di provare a renderlo ancora più veloce. Ecco la mia soluzione aggiornata, per cercare di mantenere il segno di spunta. 😉

 # My "rbind list" function rbl.ju <- function(x) { u <- unlist(x, recursive=FALSE) n <- names(u) un <- unique(n) l <- lapply(un, function(N) unlist(u[N==n], FALSE, FALSE)) names(l) <- un d <- as.data.frame(l) } # simple wrapper to rbindlist that returns a data.frame rbl.dt <- function(x) { as.data.frame(rbindlist(x)) } library(data.table) if(packageVersion("data.table") >= '1.8.2') { system.time(dt <- rbl.dt(X)) # rbindlist only exists in recent versions } # user system elapsed # 0.02 0.00 0.02 system.time(ju <- rbl.ju(X)) # user system elapsed # 0.05 0.00 0.05 identical(dt,ju) # [1] TRUE 

La tua osservazione del fatto che il tempo impiegato aumenta in modo esponenziale con il numero di dati. Frames suggerisce che spezzare la rbind in due fasi potrebbe accelerare le cose.

Questo semplice esperimento sembra confermare che si tratta di un percorso molto fruttuoso da intraprendere:

 ## Make a list of 50,000 data.frames X <- replicate(50000, data.frame(a=rnorm(5), b=1:5), simplify=FALSE) ## First, rbind together all 50,000 data.frames in a single step system.time({ X1 <- do.call(rbind, X) }) # user system elapsed # 137.08 57.98 200.08 ## Doing it in two stages cuts the processing time by >95% ## - In Stage 1, 100 groups of 500 data.frames are rbind'ed together ## - In Stage 2, the resultant 100 data.frames are rbind'ed system.time({ X2 <- lapply(1:100, function(i) do.call(rbind, X[((i*500)-499):(i*500)])) X3 <- do.call(rbind, X2) }) # user system elapsed # 6.14 0.05 6.21 ## Checking that the results are the same identical(X1, X3) # [1] TRUE 

Hai una lista di data.frames che hanno ciascuno una singola riga. Se è ansible convertire ciascuno di questi in un vettore, penso che accelererebbe molto le cose.

Tuttavia, supponendo che debbano essere data.frames, creerò una funzione con il codice preso in prestito dalla risposta di Dominik su Can rbind essere parallelizzato in R?

 do.call.rbind <- function (lst) { while (length(lst) > 1) { idxlst <- seq(from = 1, to = length(lst), by = 2) lst <- lapply(idxlst, function(i) { if (i == length(lst)) { return(lst[[i]]) } return(rbind(lst[[i]], lst[[i + 1]])) }) } lst[[1]] } 

Ho usato questa funzione per diversi mesi, e ho trovato che fosse più veloce e utilizza meno memoria di do.call(rbind, ...) [la dichiarazione di non responsabilità è che ho praticamente usato solo su oggetti xts ]

Più file ha ogni data.frame e più elementi contiene l'elenco, più utile sarà questa funzione.

Se hai una lista di 100.000 vettori numerici, do.call(rbind, ...) sarà migliore. Se hai una lista di lunghezza di un miliardo, sarà meglio.

 > df <- lapply(1:10000, function(x) data.frame(x = sample(21, 21))) > library(rbenchmark) > benchmark(a=do.call(rbind, df), b=do.call.rbind(df)) test replications elapsed relative user.self sys.self user.child sys.child 1 a 100 327.728 1.755965 248.620 79.099 0 0 2 b 100 186.637 1.000000 181.874 4.751 0 0 

La velocità relativa aumenterà esponenzialmente meglio man mano che aumenti la lunghezza della lista.