我拼命地试图避免for循环来计算自定义财务指标(多个股票,每个股票5000行)。我正在尝试使用purrr::map2
,并且在对现有向量进行数学运算时很好,但我需要引用我试图创建的向量的滞后(以前)值。不引用前一个值,purrr::map2
工作得很好:
some_function <- function(a, b) { (a * b) + ((1 - a) * b) }
a <- c(0.019, 0.026, 0.012, 0.022) # some indicator
b <- c(15.5, 16.7, 14.8, 13.1) # close price
purrr::map2(a, b, some_function)
只得到原来的关闭值
15.5, 16.7, 14.8, 13.1
但我真正想做的是创建一个新的向量(c),它回顾自己(滞后)作为计算的一部分。如果是第一行,c == b,否则:
desired_function <- function(a, b, c) { (a * b) + ((1 - a) * lag(c)) }
所以我创建了一个向量c
并填充并尝试:
c <- c(15.5, 0, 0, 0)
purrr::map2(a, b, c, desired_function)
显然得到所有NULL值。
c的值应为:15.50, 15.53, 15.52, 15.47
引用前一个值是指示器中常见的事情,它迫使我使用笨拙,缓慢的"for循环"。如有任何建议,不胜感激。
这里有一些解决方案,第一个是指您使用stats::lag
的想法(使用stats::
,因为dplyr包总是屏蔽lag
!),
r <- numeric(4L)
for (i in 1:4) {
r[i] <- c[i + 1] <- a[i]*b[i] + (1 - a[i])*stats::lag(c)[i]
}
r
# [1] 15.50000 15.53120 15.52243 15.46913
和另一个使用在每次迭代中更新的起始值,这大约快20%。
r <- numeric(4L)
sval <- 15.5
for (i in 1:4) {
r[i] <- sval <- a[i]*b[i] + (1 - a[i])*sval
}
r
# [1] 15.50000 15.53120 15.52243 15.46913
数据:
a <- c(0.019, 0.026, 0.012, 0.022)
b <- c(15.5, 16.7, 14.8, 13.1)
c <- c(15.5, 0, 0, 0)
如果计算向量中的某个值需要来自同一向量的另一个值,那么它就不能矢量化;你必须一个接一个地计算它们。
For
循环本身并不慢;关键是你如何使用它们。例如,每次从数据帧中检索一个值,或者每次插入一个值,这是一种非常慢的常见做法。
for循环在R中的实现在过去的10年里有了很大的改进,据说它们曾经效率较低,在以前的帖子中你会发现很多人在抱怨它。
推荐阅读:
https://www.r-bloggers.com/2018/06/why-loops-are-slow-in-r/
还有这两个老问题(好吧,它们的答案):
加速R
中的循环操作为什么R中的循环很慢?
一个小实验
让我们用purrr::map()对一个没有延迟的函数的最简单(最愚蠢?)for循环实现进行基准测试:c = a*b + (1-a) * b
在这个包含1000万个条目的基准测试中,for循环比purrr::map2()快15倍以上。
# functions ---------------------------------------------------------------
desired_function <- function(a,b) { a*b + (1-a) * b }
des_fnc_for <- function(a, b) {
c <- numeric(length(a))
c[1] <- b[1]
for(i in seq_along(a)) c[i] <- a[i] * b[i] + (1 - a[i]) * b[i]
return(c)
}
# verify --------------------------------------------------------------------
a <- c(0.019, 0.026, 0.012, 0.022) # some indicator
b <- c(15.5, 16.7, 14.8, 13.1) # close price
unlist(purrr::map2(a,b,desired_function))
[1] 15.5 16.7 14.8 13.1
des_fnc_for(a,b)
[1] 15.5 16.7 14.8 13.1
# benchmark ---------------------------------------------------------------
a <- runif(10000000, 0.01, 0.03)
b <- runif(10000000, 13, 17)
system.time( des_fnc_for(a,b) )
user system elapsed
1.143 0.007 1.163
system.time( purrr::map2(a,b,desired_function) )
user system elapsed
18.570 0.627 19.761