向量化取决于先前元素的乘积计算?
我正在尝试加速/矢量化时间序列中的一些计算。 我可以在 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 循环的结果?
I'm trying to speed up/vectorize some calculations in a time series.
Can I vectorize a calculation in a for loop which can depend on results from an earlier iteration? For example:
z <- c(1,1,0,0,0,0)
zi <- 2:6
for (i in zi) {z[i] <- ifelse (z[i-1]== 1, 1, 0) }
uses the z[i] values updated in earlier steps:
> z
[1] 1 1 1 1 1 1
In my effort at vectorizing this
z <- c(1,1,0,0,0,0)
z[zi] <- ifelse( z[zi-1] == 1, 1, 0)
the element-by-element operations don't use results updated in the operation:
> z
[1] 1 1 1 0 0 0
So this vectorized operation operates in 'parallel' rather than iterative fashion. Is there a way I can write/vectorize this to get the results of the for loop?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
ifelse
是矢量化的,如果您在 for 循环中一次对一个元素使用它,则会有一些损失。在您的示例中,通过使用if
而不是ifelse
可以获得相当好的加速。您可以通过编译
fun2
来获得一些额外的速度提升。因此,无需矢量化即可实现 10 倍的加速。正如其他人所说(以及一些人已经说明的那样),有多种方法可以对示例进行矢量化,但这可能无法转化为您的实际问题。希望这足够普遍以适用。
如果您能够弄清楚如何用自回归或移动平均过程来表达您的问题,那么
filter
函数可能对您也很有用。ifelse
is vectorized and there's a bit of a penalty if you're using it on one element at a time in a for-loop. In your example, you can get a pretty good speedup by usingif
instead ofifelse
.You can get some additional speed gains out of
fun2
by compiling it.So there's a 10x speedup without vectorization. As others have said (and some have illustrated) there are ways to vectorize your example, but that may not translate to your actual problem. Hopefully this is general enough to be applicable.
The
filter
function may be useful to you as well if you can figure out how to express your problem in terms of a autoregressive or moving average process.这是一个很好且简单的示例,其中 Rcpp 可以大放异彩。
因此,让我们首先重新构建函数 1 和 2 及其编译后的对应项:
我们编写一个 Rcpp 变体非常容易:
这使用 inline 包来编译、加载和链接五行代码苍蝇。
现在我们可以定义我们的测试日期,我们将其设置为比原始日期稍长一些(因为运行原始数据的次数太少会导致无法测量的时间):
所有答案都被视为相同。
最后,我们可以进行基准测试:
编译版本比最好的 R 版本高出近 400 倍,比字节编译版本高出近 100 倍。对于函数 1,字节编译的影响要小得多,并且两个变体都落后于 C++ 远超过两千。
写C++版本大约花了一分钟。速度的增加表明这一分钟花得值。
为了进行比较,以下是更常调用的原始短向量的结果:
定性排名不变: Rcpp 版本占主导地位,function2 次之。字节编译版本的速度大约是普通 R 版本的两倍,但仍然比 C++ 版本慢近三倍。并且相对差异较小:相对而言,函数调用开销更重要,而实际循环更重要:C++ 在较长向量中的实际循环操作上具有更大优势。这是一个重要的结果,因为它表明更多真实大小的数据,编译版本可能会获得更大的好处。
进行编辑以纠正代码示例中的两个小疏忽。感谢 Josh 再次编辑以捕获与 fun2c 相关的设置错误。
This is a nice and simple example where Rcpp can shine.
So let us first recast functions 1 and 2 and their compiled counterparts:
We write a Rcpp variant very easily:
This uses the inline package to compile, load and link the five-liner on the fly.
Now we can define our test-date, which we make a little longer than the original (as just running the original too few times result in unmeasurable times):
All answers are seen as identical.
Finally, we can benchmark:
The compiled version wins by a factor of almost 400 against the best R version, and almost 100 against its byte-compiled variant. For function 1, the byte compilation matters much less and both variants trail the C++ by a factor of well over two-thousand.
It took about one minute to write the C++ version. The speed gain suggests it was a minute well spent.
For comparison, here is the result for the original short vector called more often:
The qualitative ranking is unchanged: the Rcpp version dominates, function2 is second-best. with the byte-compiled version being about twice as fast that the plain R variant, but still almost three times slower than the C++ version. And the relative difference are lower: relatively speaking, the function call overhead matters less and the actual looping matters more: C++ gets a bigger advantage on the actual loop operations in the longer vectors. That it is an important result as it suggests that more real-life sized data, the compiled version may reap a larger benefit.
Edited to correct two small oversights in the code examples. And edited again with thanks to Josh to catch a setup error relative to fun2c.
我认为这是作弊并且不可概括,但是:根据上面的规则,向量中任何出现的 1 都会使所有后续元素为 1 (通过递归:
z[i]
is 1 set如果z[i-1]
等于 1,则为 1;因此,如果z[i-2]
,则z[i]
将设置为 1;等于 1;依此类推)。根据您真正想做的事情,如果您仔细考虑的话,可能可以使用这样的递归解决方案...编辑:这是错误的,但我是留下来承认我的错误。基于一点点的尝试(和更少的思考),我认为这个递归的实际解决方案更加对称,甚至更简单:
测试用例:
I think this is cheating and not generalizable, but: according to the rules you have above, any occurrence of 1 in the vector will make all subsequent elements 1 (by recursion:
z[i]
is 1 set to 1 ifz[i-1]
equals 1; thereforez[i]
will be set to 1 ifz[i-2]
equals 1; and so forth). Depending on what you really want to do, there may be such a recursive solution available if you think carefully about it ...edit: this is wrong, but I'm leaving it up to admit my mistakes. Based on a little bit of playing (and less thinking), I think the actual solution to this recursion is more symmetric and even simpler:
Test cases:
查看
zoo
中的rollapply
函数。我对它不是很熟悉,但我认为这就是你想要的:
我通过使用 2 的窗口然后只使用该窗口的第一个元素来拼凑它。
对于更复杂的示例,您可以对 x[1] 执行一些计算并返回它。
Check out the
rollapply
function inzoo
.I'm not super familiar with it, but I think this does what you want:
I'm sort of kludging it by using a window of 2 and then only using the first element of that window.
For more complicated examples you could perform some calculation on x[1] and return that instead.
有时你只需要完全不同地思考它。你正在做的是创建一个向量,其中每个项目都与第一个项目相同,否则为 1 或 0。
Sometimes you just need to think about it totally differently. What you're doing is creating a vector where every item is the same as the first if it's a 1 or 0 otherwise.
有一个函数可以执行此特定计算:
cumprod
(累积乘积)否则,请使用 Rccp 进行矢量化,如其他答案所示。
There is a function that does this particular calculation:
cumprod
(cumulative product)Otherwise, vectorize with Rccp as other answers have shown.
也可以通过使用原始向量和向量的滞后版本作为数据帧的组成列来“应用”来完成此操作。
It's also possible to do this with "apply" using the original vector and a lagged version of the vector as the constituent columns of a data frame.