2012-07-26 161 views
43

用data.table包實現滑動窗口函數的最佳(最快)方法是什麼?R data.table滑動窗口

我想計算滾動中位數,但每個日期有多行(由於2個附加因素),我認爲這意味着動物園rollapply函數將無法正常工作。以下是使用幼稚循環的示例:

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,好問題。 – Ryogi 2012-07-26 21:28:53

+0

+1。你能提供更多關於數據大小和時間的信息嗎?從你的評論到Alan的回答(艾倫和艾倫是不同的人?),它需要6.4s(對於'data.frame',973s),你想進一步提高6.4s? – 2012-08-08 14:30:41

+0

艾倫和阿蘭是不同的人:)。該數據集有約650,000行。我想出了一個速度更快,但內存密集的解決方案。關於如何進一步改進的任何想法? – alan 2012-08-10 15:02:12

回答

6

data.table目前沒有任何滾動窗口的特殊功能。這裏更詳細的我的答案在這裏的另一個類似的問題:

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

滾動中位數是有趣的。這將需要一個專門的功能,有效地完成(同一條鏈路在先前的評論):

Rolling median algorithm in C

data.table解決方案的問題和答案在這裏都非常低效的,相對於適當的專業rollingmedian功能(該功能不適用於R afaik)。

+6

我們可以提高FR#2185的優先級嗎? 「爲滑動窗口添加功能/文檔」。 從我的角度來看,它不需要任何滾動總和,平均值等。 最好有一種框架函數「rollfun =」或下面的方法: 我已經嘗試使用roll = 30,mult ='all',讓c.cartesian實現它,沒有成功。 此外,「n」將會很好地接受矢量,不僅是標量。 – jangorecki 2014-01-08 15:28:09

+6

@MusX好吧,我已經把優先級提高到頂端。 – 2014-01-08 23:52:51

0

此解決方案可行,但需要一段時間。

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

謝謝!雖然它似乎需要比for循環更長的時間。我從system.time得到的時間是973s,你的代碼是6.4s。我認爲差異必須是使用data.table包 – alan 2012-07-29 17:19:29

3

我設法通過創建一個滯後的數據集並做了一個巨大的連接,將示例降低到1.4s。

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")] 

這工作得很好,在這個測試數據集,但是,我真正的失敗與8GB的內存。我將嘗試移動到其中一個高內存EC2實例(使用17,34或68GB RAM)以使其工作。任何想法如何以較少的內存密集的方式做到這一點,將不勝感激

+0

乍一看,'for'內的'rbind'將使用太多的RAM。必須是一個更直接的方式來做那件事。 – 2012-08-10 15:57:43

+0

[這個問題](http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c)提到二進制搜索,R和C爲滾動中位數。看起來很有希望你進一步調查;即考慮_algorithm_。 – 2012-08-10 16:22:53