2015-06-03 9 views
10

Ho notato che cbind richiede molto più tempo di rbind per data.tables. Qual è la ragione di questo?cbind vs rbind con data.table

> dt <- as.data.table(mtcars)        
> new.dt <- copy(dt)          
> timeit({for (i in 1:100) dt.new <- rbind(dt.new, dt)}) 
    user system elapsed         
    0.237 0.012 0.253         
> new.dt <- copy(dt)          
> timeit({for (i in 1:100) dt.new <- cbind(dt.new, dt)}) 
    user system elapsed         
14.795 0.090 14.912  

Dove

timeit <- function(expr) 
{ 
    ptm <- proc.time() 
    expr 
    proc.time() - ptm 
} 
+0

non so la struttura interna di data.table, ma credo che l'aggiunta di nuovi record (righe) è più facile di ristrutturare la tabella con nuove variabili (colonne). – zx8754

+0

@ zx8754 Sì, anch'io sono d'accordo con il mio sospetto, curioso di sapere quale sia il collo della bottiglia specifico. Forse è l'allocazione di memoria, forse maggiori requisiti di memoria, si spera che qualcuno famoso con il nocciolo del pacchetto possa far luce ... – andrew

+3

A meno che non mi sbagli, chiamare 'rbind' su un' data.table' invierà 'rbind.data. table', che chiama la funzione 'data.table'' rbindlist'- implementata in C, e molto veloce. Vedi la risposta @ Arun [qui] (http://stackoverflow.com/questions/15673550/why-is-rbindlist-better-than-rbind). Inoltre, ci sono quasi certamente delle differenze fondamentali tra le modifiche a livello di colonna e le modifiche a livello di riga (riguardo al modo in cui gli oggetti sono archiviati in memoria), quindi non si tratta di un confronto "da mele a mele". Molto probabilmente questo è il motivo per cui 'data.table 'implementa': = 'per modificare le colonne in modo efficiente. – nrussell

risposta

8

In definitiva penso che questo si riduce a alloc.col essere lento a causa di un ciclo in cui rimuove vari attributi dalle colonne. Non sono del tutto sicuro del perché sia ​​fatto, forse Arun o Matt possono spiegare.

Come potete vedere qui sotto, le operazioni di base per cbind sono molto più veloce di rbind:

cbind.dt.simple = function(...) { 
    x = c(...) 
    setattr(x, "class", c("data.table", "data.frame")) 
    ans = .Call(data.table:::Calloccolwrapper, x, max(100L, ncol(x) + 64L), FALSE) 
    .Call(data.table:::Csetnamed, ans, 0L) 
} 

library(microbenchmark) 

microbenchmark(rbind(dt, dt), cbind(dt, dt), cbind.dt.simple(dt, dt)) 
#Unit: microseconds 
#     expr  min  lq  mean median  uq  max neval 
#   rbind(dt, dt) 785.318 996.5045 1665.1762 1234.4045 1520.3830 21327.426 100 
#   cbind(dt, dt) 2350.275 3022.5685 3885.0014 3533.7595 4093.1975 21606.895 100 
# cbind.dt.simple(dt, dt) 74.125 116.5290 168.5101 141.9055 180.3035 1903.526 100 
+0

Trovo che 'cbind2 <- function (...) (setattr (do.call (c, list (...))," class ", c (" data.table "," data.frame "))) 'è ancora più veloce. Non seguo realmente ciò che accade con le funzioni 'C *', quindi forse mi manca qualcosa. 'all.equal (cbind.dt.simple (dt, dt), cbind2 (dt, dt)) # TRUE' – Frank

+0

@Frank corretto, ma lascia alcune cose incomplete, prova ad es. 'cbind2 (dt, dt) [, newcol: = 5]' – eddi

+0

Ah, capisco. La mia prossima idea 'setDT (setattr (do.call (c, list (...))," class ", c (" data.frame "))) era molto più lenta. Oh, semplicemente 'setDT (do.call (c, list (...)))' funziona, ma è la stessa (lenta) velocità. – Frank

Problemi correlati