r语言 - 使用Purrr::map2或pmap来避免for循环



我拼命地试图避免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 

最新更新