2013-10-31 13 views
12

Devo scaricare una tabella dal server MS-SQL.Come velocizzare rbind?

Il numero di righe è superiore a 6 milioni. Il server non può restituire interi dati in una sola volta.

Quindi, ho scritto un codice che scarica 10.000 righe alla volta. e, lega le file nel loop.

Si supponga che la funzione getData() restituisca un frame di dati contenente 10000 righe alla volta. (Pseudo codice)

Il problema è che diventa più lento col passare del tempo.

Penso che usare rbind in questo modo non sia una buona idea.

Qualsiasi consiglio sarà molto utile. Grazie in anticipo.

+3

Se sai quanto grande sarà il tuo eventuale frame di dati, sarà molto più veloce pre-allocarlo e riempire i valori man mano che vai. Sebbene sia probabile che una soluzione più veloce utilizzi strutture di dati diverse che qualcuno pubblicherà. – David

+0

wannagetValue <- rbind (wannagetValue, getData()); . Potrebbe accelerare un po 'il tuo codice ... ma la domanda principale è: perché tanti dati? – Hackerman

+0

Grazie, David! Mi hai salvato ... Robert // Non ne ho idea ~~ tutto quello che so è che ci sono dati di dimensioni superiori a 500 GB. –

risposta

26

Qui ci sono alcune opzioni che sono sicuro che c'è di meglio:

library(data.table) 
library(microbenchmark) 

#function to generate your data 
getData <- function(){ 
    data.frame(x=rnorm(10000),y=rnorm(10000),z=rnorm(10000)) 
} 

#using data table's rbindlist each iteration 
fDT1 <- function(n){ 
    dat <- getData() 
    for(i in 1:n){ 
    dat <- rbindlist(list(dat,getData())) 
    } 
    return(data.frame(dat)) 
} 

#using data table's rbindlist all at once 
fDT2 <- function(n){ 
    return(data.frame(rbindlist(lapply(1:n,function(x) getData())))) 
} 

#pre-allocating a data frame 
fPre <- function(n){ 
    dat <- data.frame(x=rep(0,n*10000),y=rep(0,n*10000),z=rep(0,n*10000)) 
    j <- 1 
    for(i in 1:n){ 
    dat[j:(j+10000-1),] <- getData() 
    j <- j + 10000 
    } 
    return(dat) 
} 

#standard do.call rbind 
f2 <- function(n){ 
    return(do.call(rbind,lapply(1:n,function(x) getData()))) 
} 

#current approach 
f <- function(n){ 
    dat <- getData() 
    for(i in 1:n){ 
    dat <- rbind(dat,getData()) 
    } 
    return(dat) 
} 

Come si può vedere usando data.table s' rbindlist() è un grande miglioramento rispetto base di R rbind() e non v'è un grande vantaggio in accodando righe tutte in una volta anziché in interazioni, tuttavia ciò potrebbe non essere possibile se ci sono problemi di memoria. Si può anche notare che i miglioramenti di velocità non sono affatto vicini alla dimensione dei dati.

> microbenchmark(fDT2(5),fDT1(5),fPre(5),f2(5),f(5), 
+    fDT2(25),fDT1(25),fPre(25),f2(25),f(25), 
+    fDT2(75),fDT1(75),fPre(75),f2(75),f(75), 
+    times=10) 
Unit: milliseconds 
    expr  min   lq  median   uq   max neval 
    fDT2(5) 18.31207 18.63969 24.09943 25.45590 72.01725 10 
    fDT1(5) 27.65459 29.25147 36.34158 77.79446 88.82556 10 
    fPre(5) 34.96257 39.39723 41.24445 43.30319 68.75897 10 
    f2(5) 30.85883 33.00292 36.29100 43.53619 93.15869 10 
    f(5) 87.40869 97.97500 134.50600 138.65354 147.67676 10 
fDT2(25) 89.42274 99.39819 103.90944 146.44160 156.01653 10 
fDT1(25) 224.65745 229.78129 261.52388 280.85499 300.93488 10 
fPre(25) 371.12569 412.79876 431.80571 485.37727 1046.96923 10 
    f2(25) 221.03669 252.08998 265.17357 271.82414 281.47096 10 
    f(25) 1446.32145 1481.01998 1491.59203 1634.99936 1849.00590 10 
fDT2(75) 326.66743 334.15669 367.83848 467.85480 520.27142 10 
fDT1(75) 1749.83842 1882.27091 2066.95241 2278.55589 2419.07205 10 
fPre(75) 3701.16220 3968.64643 4162.70585 4234.39716 4356.09462 10 
    f2(75) 1174.47546 1183.98860 1314.64585 1421.09483 1537.42903 10 
    f(75) 9139.36935 9349.24412 9510.90888 9977.24621 10861.51206 10 
+0

+1 - per favore aggiungi 'do.call (rbind, lapply (1: 25, function (...) getData()))' – flodel

+0

Buona idea, grazie. – David

+0

Grazie mille per le informazioni dettagliate. ~ Mi aiuta molto. –

4

come è stato sottolineato in precedenza, negozi R tutti i suoi oggetti nella RAM di default, quindi con quella quantità di dati, che si sta per incorrere in alcuni problemi.

Due cose che vorrei aggiungere: 1) In genere, se non si desidera utilizzare data.table, è possibile utilizzare la funzione rbind.fill nel pacchetto di Hadley plyr, che è abbastanza veloce, troppo. Mai usa rbind come hai fatto sopra, in un ciclo 'for', aggiungendo ogni riga separatamente. Forza R a creare una copia dell'oggetto frame di dati ogni volta che si aggiunge una riga, e ciò è lento.

2) Per lavorare con dati RAM larger-than-R, uno sguardo alla sezione Grosse memorie e out-of-memoria dati a http://cran.r-project.org/web/views/HighPerformanceComputing.html, forse il pacchetto bigmemory è quello che ti serve.

+1

I dati grezzi interi supereranno i 500 GB, ma saranno 5 ~ 10 GB totali se vengono archiviati come dati binari. Inoltre, ogni tabella contiene 1 ~ 2 GB. Posso occuparmene separatamente. Fortunatamente, il mio computer ha 64 GB di RAM DDR3 ECC. Non penso che non sia abbastanza. A proposito, grazie per il consiglio. –

+0

Questa domanda è un po 'vecchia, ma l'ho ancora trovata mentre cercavo una soluzione oggi, quindi voglio aggiungere che il nuovo pacchetto 'dplyr' di Hadley ha la funzione' bind_rows', che è analoga a 'rbind.fill'. L'ho messo a confronto e corre circa 1000 volte più velocemente di 'do.call ('rbind', ...)' sulla mia macchina. Vedi [questa domanda] (https://stackoverflow.com/questions/44464441/r-is-there-a-good-replacement-for-plyrrbind-fill-in-dplyr). – qdread

0

Forse potresti fare SELECT COUNT(*) FROM table_name WHERE ... e quindi preallocare lo spazio per il tuo frame di dati.

In realtà, non penso che interrogare il database per 10k righe sia una buona idea. Cerca di evitarlo esportando i dati su un disco locale e leggi da lì. Migliorerà anche la velocità. Lo spazio di archiviazione è economico, la larghezza di banda della rete e la memoria no.