[英]How to vectorize a function that depends on a previous calculation in R?
[英]Vectorize a product calculation which depends on previous elements?
我正在嘗試加速/矢量化時間序列中的一些計算。 我可以在for循環中矢量化計算,這可能取決於早期迭代的結果嗎? 例如:
z <- c(1,1,0,0,0,0)
zi <- 2:6
for (i in zi) {z[i] <- ifelse (z[i-1]== 1, 1, 0) }
使用前面步驟中更新的z [i]值:
> z
[1] 1 1 1 1 1 1
在我的努力中矢量化這個
z <- c(1,1,0,0,0,0)
z[zi] <- ifelse( z[zi-1] == 1, 1, 0)
逐個元素的操作不使用操作中更新的結果:
> z
[1] 1 1 1 0 0 0
因此,這種向量化操作以“並行”而非迭代方式運行。 有沒有辦法我可以寫/向量化這個來獲得for循環的結果?
ifelse
是矢量化的,如果你在for循環中一次在一個元素上使用ifelse
會有一點點懲罰。 在您的示例中,使用if
而不是ifelse
可以獲得相當好的加速。
fun1 <- function(z) {
for(i in 2:NROW(z)) {
z[i] <- ifelse(z[i-1]==1, 1, 0)
}
z
}
fun2 <- function(z) {
for(i in 2:NROW(z)) {
z[i] <- if(z[i-1]==1) 1 else 0
}
z
}
z <- c(1,1,0,0,0,0)
identical(fun1(z),fun2(z))
# [1] TRUE
system.time(replicate(10000, fun1(z)))
# user system elapsed
# 1.13 0.00 1.32
system.time(replicate(10000, fun2(z)))
# user system elapsed
# 0.27 0.00 0.26
通過編譯,你可以從fun2
獲得額外的速度提升。
library(compiler)
cfun2 <- cmpfun(fun2)
system.time(replicate(10000, cfun2(z)))
# user system elapsed
# 0.11 0.00 0.11
所以沒有矢量化就有10倍的加速。 正如其他人所說(有些人已經說明),有一些方法可以對你的例子進行矢量化,但這可能無法轉化為你的實際問題。 希望這足以適用。
如果您可以根據自回歸或移動平均過程弄清楚如何表達問題, filter
功能對您也很有用。
這是一個很好的簡單例子, Rcpp可以發光。
那么讓我們首先重新編寫函數1和2以及它們編譯的對應函數:
library(inline)
library(rbenchmark)
library(compiler)
fun1 <- function(z) {
for(i in 2:NROW(z)) {
z[i] <- ifelse(z[i-1]==1, 1, 0)
}
z
}
fun1c <- cmpfun(fun1)
fun2 <- function(z) {
for(i in 2:NROW(z)) {
z[i] <- if(z[i-1]==1) 1 else 0
}
z
}
fun2c <- cmpfun(fun2)
我們非常容易地編寫Rcpp變體:
funRcpp <- cxxfunction(signature(zs="numeric"), plugin="Rcpp", body="
Rcpp::NumericVector z = Rcpp::NumericVector(zs);
int n = z.size();
for (int i=1; i<n; i++) {
z[i] = (z[i-1]==1.0 ? 1.0 : 0.0);
}
return(z);
")
這使用內聯包來動態編譯,加載和鏈接五線程。
現在我們可以定義我們的測試日期,我們比原來的更長一些(因為只運行原始的太少次導致不可測量的時間):
R> z <- rep(c(1,1,0,0,0,0), 100)
R> identical(fun1(z),fun2(z),fun1c(z),fun2c(z),funRcpp(z))
[1] TRUE
R>
所有答案都被視為相同。
最后,我們可以測試:
R> res <- benchmark(fun1(z), fun2(z),
+ fun1c(z), fun2c(z),
+ funRcpp(z),
+ columns=c("test", "replications", "elapsed",
+ "relative", "user.self", "sys.self"),
+ order="relative",
+ replications=1000)
R> print(res)
test replications elapsed relative user.self sys.self
5 funRcpp(z) 1000 0.005 1.0 0.01 0
4 fun2c(z) 1000 0.466 93.2 0.46 0
2 fun2(z) 1000 1.918 383.6 1.92 0
3 fun1c(z) 1000 10.865 2173.0 10.86 0
1 fun1(z) 1000 12.480 2496.0 12.47 0
對於最好的R版本,編譯版本贏得了近400倍,而對於其字節編譯版本,編譯版本幾乎達到100。 對於函數1,字節編譯的重要性要小得多,並且這兩種變體都使C ++的性能遠遠超過兩千 。
編寫C ++版本花了大約一分鍾。 速度提升表明它花了一分鍾。
為了比較,這里是更經常調用的原始短向量的結果:
R> z <- c(1,1,0,0,0,0)
R> res2 <- benchmark(fun1(z), fun2(z),
+ fun1c(z), fun2c(z),
+ funRcpp(z),
+ columns=c("test", "replications",
+ "elapsed", "relative", "user.self", "sys.self"),
+ order="relative",
+ replications=10000)
R> print(res2)
test replications elapsed relative user.self sys.self
5 funRcpp(z) 10000 0.046 1.000000 0.04 0
4 fun2c(z) 10000 0.132 2.869565 0.13 0
2 fun2(z) 10000 0.271 5.891304 0.27 0
3 fun1c(z) 10000 1.045 22.717391 1.05 0
1 fun1(z) 10000 1.202 26.130435 1.20 0
定性排名沒有變化: Rcpp版本占主導地位,功能2排名第二。 字節編譯版本的速度大約是普通R版本的兩倍,但仍然比C ++版本快三倍。 並且相對差異較小:相對而言,函數調用開銷較少,實際循環更重要:C ++在較長向量中的實際循環操作中獲得更大的優勢。 這是一個重要的結果,因為它表明更多真實大小的數據,編譯版本可以獲得更大的好處。
編輯糾正代碼示例中的兩個小疏忽。 並再次編輯,感謝Josh捕獲相對於fun2c的設置錯誤。
我認為這是作弊而不是一般化的,但是:根據你上面的規則,在向量中出現1將使所有后續元素1(通過遞歸:如果z[i-1]
z[i]
則z[i]
為1設置為1 z[i-1]
等於1;因此如果z[i-2]
等於1,則z[i]
將被設置為1;依此類推)。 根據您真正想要做的事情,如果您仔細考慮, 可能會有這樣的遞歸解決方案......
z <- c(1,1,0,0,0,0)
first1 <- min(which(z==1))
z[seq_along(z)>first1] <- 1
編輯 :這是錯的,但是我不肯承認我的錯誤。 基於一點點的游戲(並且思路較少),我認為這種遞歸的實際解決方案更加對稱,甚至更簡單:
rep(z[1],length(z))
測試用例:
z <- c(1,1,0,0,0,0)
z <- c(0,1,1,0,0,0)
z <- c(0,0,1,0,0,0)
查看zoo
的rollapply
功能。
我不是很熟悉它,但我認為這樣做你想要的:
> c( 1, rollapply(z,2,function(x) x[1]) )
[1] 1 1 1 1 1 1
我通過使用2的窗口然后僅使用該窗口的第一個元素來克服它。
對於更復雜的示例,您可以對x [1]執行一些計算並返回它。
有一個函數可以執行此特定計算: cumprod
(累積產品)
> cumprod(z[zi])
[1] 1 0 0 0 0
> cumprod(c(1,2,3,4,0,5))
[1] 1 2 6 24 0 0
否則,使用Rccp進行矢量化,如其他答案所示。
有時你只需要完全不同地思考它。 你正在做的是創建一個向量,其中每個項目與第一個項目相同,否則為1或0。
z <- c(1,1,0,0,0,0)
if (z[1] != 1) z[1] <- 0
z[2:length(z)] <- z[1]
使用原始矢量和矢量的滯后版本作為數據幀的組成列,也可以使用“apply”來完成此操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.