2012-07-26 16 views
43

Qual è il modo migliore (più veloce) per implementare una funzione finestra scorrevole con il pacchetto data.table?R data.table sliding window

Sto provando a calcolare una media mobile, ma ho più righe per data (a causa di 2 fattori aggiuntivi), che penso significhi che la funzione di rollapply dello zoo non funzionerebbe. Ecco un esempio di naive for loop:

library(data.table) 
df <- data.frame(
    id=30000, 
    date=rep(as.IDate(as.IDate("2012-01-01")+0:29, origin="1970-01-01"), each=1000), 
    factor1=rep(1:5, each=200), 
    factor2=1:5, 
    value=rnorm(30, 100, 10) 
) 

dt = data.table(df) 
setkeyv(dt, c("date", "factor1", "factor2")) 

get_window <- function(date, factor1, factor2) { 
    criteria <- data.table(
    date=as.IDate((date - 7):(date - 1), origin="1970-01-01"), 
    factor1=as.integer(factor1), 
    factor2=as.integer(factor2) 
) 
    return(dt[criteria][, value]) 
} 

output <- data.table(unique(dt[, list(date, factor1, factor2)]))[, window_median:=as.numeric(NA)] 

for(i in nrow(output):1) { 
    print(i) 
    output[i, window_median:=median(get_window(date, factor1, factor2))] 
} 
+2

+1, buona domanda. – Ryogi

+0

+1. Puoi fornire maggiori informazioni su dimensioni e tempi dei dati. Dal tuo commento alla risposta di Alan (Alan e Alan sono persone diverse?), Ci vogliono 6.4 secondi (contro 973 per 'data.frame') e vorresti migliorare ulteriormente i 6.4? –

+0

Alan e Alan sono persone diverse :). Il set di dati ha ~ 650.000 righe. Ho trovato una soluzione che funziona molto più velocemente ma richiede molta memoria. Qualche idea su come può essere ulteriormente migliorata? – alan

risposta

6

data.table non ha alcuna funzionalità speciale per il rolling windows, al momento. Ulteriori dettagli qui nella mia risposta a un'altra domanda simile qui:

Is there a fast way to run a rolling regression inside data.table?

rotolamento mediana è interessante. Si avrebbe bisogno di una funzione specializzata di fare in modo efficiente (stesso link come nel precedente commento):

Rolling median algorithm in C

Le data.table soluzioni alla domanda e le risposte qui sono tutti molto inefficiente, rispetto ad una vera e propria funzione specializzata rollingmedian (che non è disponibile per R afaik).

+6

Possiamo aumentare la priorità di FR # 2185? "Aggiungi funzionalità/documentazione per le finestre scorrevoli". Dal mio punto di vista non è necessario alcun riepilogo, media, ecc. È meglio avere una specie di funzione framework "rollfun =" o i metodi seguenti: Ho già provato self join con roll = 30, mult = 'all', allow.cartesian per raggiungerlo, senza successo. Anche la "n" sarebbe buona per accettare il vettore, non solo scalare. – jangorecki

+6

@MusX Ok ho alzato la priorità in alto. –

0

Questa soluzione funziona, ma ci vuole un po '.

df <- data.frame(
    id=30000, 
    date=rep(seq.Date(from=as.Date("2012-01-01"),to=as.Date("2012-01-30"),by="d"),each=1000), 
    factor1=rep(1:5, each=200), 
    factor2=1:5, 
    value=rnorm(30, 100, 10) 
) 

myFun <- function(dff,df){ 
    median(df$value[df$date>as.Date(dff[2])-8 & df$date<as.Date(dff[2])-1 & df$factor1==dff[3] & df$factor2==dff[4]]) 
} 

week_Med <- apply(df,1,myFun,df=df) 

week_Med_df <- cbind(df,week_Med) 
+0

Grazie! Tuttavia, sembra che richieda più tempo del ciclo for. I tempi che ricevo da system.time sono 973 per il codice 6.4s per il ciclo. Penso che la differenza debba essere l'uso del pacchetto data.table – alan

3

Sono riuscito a ridurre l'esempio a 1,4 s creando un set di dati ritardato e facendo un enorme join.

df <- data.frame(
    id=30000, 
    date=rep(as.IDate(as.IDate("2012-01-01")+0:29, origin="1970-01-01"), each=1000), 
    factor1=rep(1:5, each=200), 
    factor2=1:5, 
    value=rnorm(30, 100, 10) 
) 

dt2 <- data.table(df) 
setkeyv(dt, c("date", "factor1", "factor2")) 

unique_set <- data.table(unique(dt[, list(original_date=date, factor1, factor2)])) 
output2 <- data.table() 
for(i in 1:7) { 
    output2 <- rbind(output2, unique_set[, date:=original_date-i]) 
}  

setkeyv(output2, c("date", "factor1", "factor2")) 
output2 <- output2[dt] 
output2 <- output2[, median(value), by=c("original_date", "factor1", "factor2")] 

che funziona abbastanza bene su questo test insieme di dati, ma il mio vero non riesce con 8GB di RAM. Proverò a passare a una delle istanze di High Memory EC2 (con 17, 34 o 68 GB di RAM) per farlo funzionare. Qualunque idea su come farlo in un modo meno intensivo di memoria sarebbe apprezzata

+0

Solo a prima vista, 'rbind' all'interno di' for' userà troppa RAM. Deve essere un modo più diretto per fare quel bit. –

+0

[Questa domanda] (http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c) menziona la ricerca binaria, R e C per la media mobile. Sembra promettente per te indagare ulteriormente; cioè, pensa al _algorithm_. –