2015-08-13 10 views
10

Perché il secondo metodo diventeranno più lenta aumentando la dimensione data.table:metodo veloce per sostituire i valori dei dati condizionale in data.table (confronto velocità)

library(data.table) 
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9) 

1:

DF1=DF2=DF 

system.time(DF[y==6,"y"]<-10) 
user system elapsed 
2.793 0.699 3.497 

2 :

system.time(DF1$y[DF1$y==6]<-10) 
user system elapsed 
6.525 1.555 8.107 

3:

system.time(DF2[y==6, y := 10]) # slowest! 
user system elapsed 
7.925 0.626 8.569 

>sessionInfo() 
R version 3.2.1 (2015-06-18) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 14.04.3 LTS 

Esiste un modo più rapido per eseguire questa operazione?

risposta

10

Sul tuo ultimo caso, è una conseguenza della funzione di indicizzazione automatica in data.table, poiché v1.9.4 +. Leggi di più per l'immagine completa :-).

Quando si esegue DT[col == .] o DT[col %in% .], un indice viene generato automaticamente su la prima esecuzione. L'indice è solo il order della colonna specificata. Il calcolo degli indici è abbastanza veloce (usando il conteggio sort/true radix sorting).

La tabella è di 120 milioni di righe e ci vogliono circa:

# clean session 
require(data.table) 
set.seed(1L) 
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9) 

system.time(data.table:::forderv(DF, "y")) 
# 3.923 0.736 4.712 

Nota a margine: Colonna y Non bisogna essere davvero doppia (su cui ordinamento richiede più tempo). Se convertiamo al tipo intero:

DF[, y := as.integer(y)] 
    system.time(data.table:::forderv(DF, "y")) 
    # user system elapsed 
    # 0.569 0.140 0.717 

Il vantaggio è che ogni sottoinsiemi successivi su quella colonna utilizzando == o %in% saranno velocissimo (Slides, R script, video della presentazione di Matt). Ad esempio:

# clean session, copy/paste code from above to create DF 
system.time(DF[y==6, y := 10]) 
# user system elapsed 
# 4.750 1.121 5.932 

system.time(DF[y==6, y := 10]) 
# user system elapsed 
# 4.002 0.907 4.969 

Oh aspetta un minuto .. non è veloce. Ma ... indicizzazione ..?!? Sostituiamo sempre la stessa colonna con un nuovo valore. Ciò si traduce nell'ottenere la modifica di quella colonna (rimuovendo quindi l'indice). sottoinsiemi di Let y, ma modificando v:

# clean session 
require(data.table) 
set.seed(1L) 
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9) 

system.time(DF[y==6, v := 10L]) 
# user system elapsed 
# 4.653 1.071 5.765 
system.time(DF[y==6, v := 10L]) 
# user system elapsed 
# 0.685 0.213 0.910 

options(datatable.verbose=TRUE) 
system.time(DF[y==6, v := 10L]) 
# Using existing index 'y' 
# Starting bmerge ...done in 0 secs 
# Detected that j uses these columns: v 
# Assigning to 40000059 row subset of 120000000 rows 
# user system elapsed 
# 0.683 0.221 0.914 

si può vedere che il tempo per calcolare gli indici (usando la ricerca binaria) batte 0 secondi. Controlla anche ?set2key().

Se non avete intenzione di fare ripetute sottoinsiemi, o come nel tuo caso, sottoinsiemi e modificando la stessa colonna, allora ha senso per disattivare la funzione facendo options(datatable.auto.index = FALSE), depositata #1264:

# clean session 
require(data.table) 
options(datatable.auto.index = FALSE) # disable auto indexing 
set.seed(1L) 
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9) 

system.time(DF[y==6, v := 10L]) 
# user system elapsed 
# 1.067 0.274 1.367 
system.time(DF[y==6, v := 10L]) 
# user system elapsed 
# 1.100 0.314 1.443 

La differenza non è molto qui. Il tempo di scansione vettoriale è system.time(DF$y == 6) = 0.448s.

Per riassumere, nel tuo caso, la scansione vettoriale ha più senso. Ma in generale, l'idea è che è meglio pagare la penalità una volta e avere risultati rapidi su sottoinsiemi futuri su quella colonna, piuttosto che scansionare ogni volta.

funzione Auto indicizzazione è relativamente nuovo, e sarà esteso nel corso del tempo, e probabilmente ottimizzato (forse ci sono posti non abbiamo guardato). Rispondendo a questo Q, mi sono reso conto che non mostriamo il tempo per calcolare l'ordinamento (usando fsort(), e immagino che il tempo trascorso lì potrebbe essere il motivo per cui i tempi sono piuttosto vicini, archiviati #1265).


Per quanto riguarda il secondo caso è lento, non del tutto sicuro perché. Sospetto che potrebbe essere dovuto a copie non necessarie della parte di R. Quale versione di R stai usando? Per il futuro, pubblica sempre l'output sessionInfo().

+0

@DavidArenburg, il tempo di scansione vettoriale/ricerca binaria non cambia (che è la parte che richiede tempo qui). – Arun

+1

Mi chiedo se potresti spiegare perché 'DF [y == 6," y "] <- 10' funziona mentre' DF [y == 6, "y"] 'praticamente non fa nulla. Questo tipo di colpi alla testa. –

+2

@DavidArenburg, controlla 'data.table ::: \' [<-. Data.table \ ''. Non l'ho mai usato così. Non credo che nessuno lo usi. Deve essere ancora lì per compatibilità con le versioni precedenti, non è sicuro. Qualsiasi e tutti i miglioramenti non si verificano anche in quella funzione (dal momento che non è idiomatico) sarebbe la mia ipotesi. – Arun

Problemi correlati