2016-04-12 10 views
6

Risposta breve: sì, vedere la risposta accettata.È possibile avere un oggetto funzione come elemento


Ho la due data.table di seguito.

stocks = data.table(Ticker = c('xx','xx','yy','yy'), Date = c(as.IDate("2000-01-01"), as.IDate("2000-01-02")), t = c(1.8, 3.5)) 
    Ticker  Date t 
1:  xx 2000-01-01 1.8 
2:  xx 2000-01-02 3.5 
3:  yy 2000-01-01 1.8 
4:  yy 2000-01-02 3.5 
tt = data.table(Date = c(as.IDate("2000-01-01"), as.IDate("2000-01-02")), t0 = c(1,2), t1 = c(2,3), t2 = c(3,4), y0 = c(10, 20), y1 = c(-20, -30), y2 = c(33,44)) 
     Date t0 t1 t2 y0 y1 y2 
1: 2000-01-01 1 2 3 10 -20 33 
2: 2000-01-02 2 3 4 20 -30 44 

Per ogni riga in stocks, voglio trovare il approssimativa y dato t, sulla base di interpolazione lineare di valori in tt.

zz = tt[stocks, on = 'Date'] 
zz[, y.approx := approx(c(t0,t1,t2), c(y0,y1,y2), t)$y, by = 'Date,Ticker'] 
     Date t0 t1 t2 y0 y1 y2 Ticker t y.approx 
1: 2000-01-01 1 2 3 10 -20 33  xx 1.8  -14 
2: 2000-01-02 2 3 4 20 -30 44  xx 3.5  7 
3: 2000-01-01 1 2 3 10 -20 33  yy 1.8  -14 
4: 2000-01-02 2 3 4 20 -30 44  yy 3.5  7 

Il problema è che fare in questo modo ha molti calcoli duplicati. Idealmente, voglio definire un approxfun per ogni giorno e applicarlo a ciascuna riga in stocks. Ma datatable non può assumere oggetti funzione come suo elemento.

tt[, ff := approxfun(c(t0,t1,t2), c(y0,y1,y2)), by = Date] 
Error in `[.data.table`(tt, , `:=`(ff, approxfun(c(t0, t1, t2), c(y0, : 
    j evaluates to type 'closure'. Must evaluate to atomic vector or list. 

La mia domanda è:

  1. C'è un modo migliore di fare approx su ogni riga (e di essere lenta)?
  2. È possibile che Datatable abbia oggetti funzione come elemento?

risposta

6

E 'abbastanza facile da memorizzare le funzioni in un data.table - è sufficiente metterli in una lista:

tt[, ff := .(list(approxfun(c(t0,t1,t2), c(y0,y1,y2)))), by = Date] 
#   Date t0 t1 t2 y0 y1 y2   ff 
#1: 2000-01-01 1 2 3 10 -20 33 <function> 
#2: 2000-01-02 2 3 4 20 -30 44 <function> 

stocks[tt, y.approx := ff[[1]](t), on = 'Date', by = .EACHI] 
stocks 
# Ticker  Date t y.approx 
#1:  xx 2000-01-01 1.8  -14 
#2:  xx 2000-01-02 3.5  7 
#3:  yy 2000-01-01 1.8  -14 
#4:  yy 2000-01-02 3.5  7 
2

Siamo in grado di incidere con un elenco globale di funzioni e l'operatore superassignment:

x <- list(); 
invisible(tt[,{ x[[as.character(Date)]] <<- approxfun(c(t0,t1,t2),c(y0,y1,y2)); 0; },Date]); 
x; 
## $`2000-01-01` 
## function (v) 
## .approxfun(x, y, v, method, yleft, yright, f) 
## <bytecode: 0x602762000> 
## <environment: 0x603118610> 
## 
## $`2000-01-02` 
## function (v) 
## .approxfun(x, y, v, method, yleft, yright, f) 
## <bytecode: 0x602762000> 
## <environment: 0x60312c9d0> 
## 
stocks[,y.approx:=x[[as.character(Date)]](t),Date]; 
## Ticker  Date t y.approx 
## 1:  xx 2000-01-01 1.8  -14 
## 2:  xx 2000-01-02 3.5  7 
## 3:  yy 2000-01-01 1.8  -14 
## 4:  yy 2000-01-02 3.5  7 
+2

inutili hackiness – eddi

5

ne dite qualcosa di simile:

> zz 
     Date t0 t1 t2 y0 y1 y2 Ticker t 
1: 2000-01-01 1 2 3 10 -20 33  xx 1.8 
2: 2000-01-02 2 3 4 20 -30 44  xx 3.5 
3: 2000-01-01 1 2 3 10 -20 33  yy 1.8 
4: 2000-01-02 2 3 4 20 -30 44  yy 3.5 

> zz[t0<=t & t<=t1, y.approx:={a=(t-t0)/(t1-t0); y0+a*(y1-y0)}] 
> zz 
     Date t0 t1 t2 y0 y1 y2 Ticker t y.approx 
1: 2000-01-01 1 2 3 10 -20 33  xx 1.8  -14 
2: 2000-01-02 2 3 4 20 -30 44  xx 3.5  NA 
3: 2000-01-01 1 2 3 10 -20 33  yy 1.8  -14 
4: 2000-01-02 2 3 4 20 -30 44  yy 3.5  NA 

> zz[t1<=t & t<=t2, y.approx:={a=(t-t1)/(t2-t1); y1+a*(y2-y1)}] 
> zz 
     Date t0 t1 t2 y0 y1 y2 Ticker t y.approx 
1: 2000-01-01 1 2 3 10 -20 33  xx 1.8  -14 
2: 2000-01-02 2 3 4 20 -30 44  xx 3.5  7 
3: 2000-01-01 1 2 3 10 -20 33  yy 1.8  -14 
4: 2000-01-02 2 3 4 20 -30 44  yy 3.5  7 
> 

Non so come generale avete bisogno di essere (quante colonne hai davvero). Ma vale la pena provare a vettorizzare in questo modo per salvare la funzione chiamata overhead per riga. Diverse iterazioni di un ciclo for per il delta numero di volte (2 in questo caso) dovrebbero essere più veloci del ciclo per riga (fateci sapere se andate in quel modo e dovete generare la query dinamicamente per ogni delta orario).

+0

Grazie Matt. Penso che più avanti avrò un'approssimazione più complessa (che lineare), quindi probabilmente non posso spiegare come hai fatto qui. – jf328

+0

@ jf328 Potresti usare 'approx()' al posto dell'ortografia. La cosa principale per l'efficienza è di farlo alla rinfusa; tutto tranne una chiamata di funzione per riga. –

Problemi correlati