使用R计算向量的滚动总和

4
我有一个长向量x,另一个v包含长度。 我想对x进行求和,使得答案y是长度为length(v)的向量,并且y[1]等于sum(x[1:v[i]]),y[2]等于sum(x[(1+v[1]):(v[1]+v[2])]),以此类推。本质上,这是从维度为length(x)的空间到维度为length(v)的空间执行稀疏矩阵乘法。但是,我希望不要使用“高级机器”,尽管我可能不得不这样做。它需要非常非常快。 有没有人能想到比使用稀疏矩阵软件包更简单的方法?
示例 -
x <- c(1,1,3,4,5)
v <- c(2,3)
y <- myFunc(x,v)

y 应该是 c(2,12)

我可以进行任何预处理 - 例如,将每个连续区间的起始索引存储在变量v中。


在第二句话中,1:v[i] 应该改为 1:v[1] 吗? - Iterator
4个回答

8
  y <- cumsum(x)[cumsum(v)]
  y <- c(y[1], diff(y))

这似乎会做一些额外的工作,因为它要计算整个向量的cumsum,但实际上它比其他解决方案更快,无论是对于小组数还是大组数。

以下是我模拟数据的方法。

set.seed(5)
N <- 1e6
n <- 10
x <- round(runif(N,0,100),1)
v <- as.vector(table(sample(n, N, replace=TRUE)))

在我的电脑上,当 n <- 10 时的时间如下:
  • Brandon Bertelsen (for loop): 0.017
  • Ramnath (rowsum): 0.057
  • John (split/apply): 0.280
  • Aaron (cumsum): 0.008
将其更改为 n <- 1e5 后的时间如下:
  • Brandon Bertelsen (for loop): 2.181
  • Ramnath (rowsum): 0.226
  • John (split/apply): 0.852
  • Aaron (cumsum): 0.015
我怀疑这比使用稀疏矩阵包进行矩阵乘法要快,因为不需要形成矩阵或进行任何乘法。如果需要更快的速度,我认为可以通过使用 C 语言编写来加速;使用 inlinercpp 包很容易实现,但我会留给你。

+1 我相信这是正确的方法。它几乎具有最快的吞吐量,因为内存可以预先分配,并且完全向量化。通过使用multicore包中的parallel()命令并行执行cumsum(x)cumsum(v),可以潜在地改进计算。 - Iterator
我很想了解为什么当n增加时,for循环似乎变得更慢。 - Brandon Bertelsen
n是v的长度,因此随着n的增加,for循环需要遍历更多内容。 - Aaron left Stack Overflow

1
你可以使用rowsum来完成这个操作。它应该相当快,因为它在后台使用了C代码。
y <- rowsum(x, rep(1:length(v), v))

你是如何生成虚拟数据的?请注意,v 不能是任意向量。sum(v) 应该等于 x 的长度。 - Ramnath
只是 x <- 1:100000,v <- 1:100000 - Brandon Bertelsen
2
这不起作用,因为 sum(v) 不等于 length(x)。请注意,用户访问的最后一个元素是 x[sum(v)],为了使其存在,必须满足 sum(v) <= length(x) - Ramnath
谢谢示例,但我认为for循环还是稍微快一些...奇怪,我原以为矢量化函数会更快。 - Brandon Bertelsen
你能否提供一些数据作为示例,这样我就可以看到循环为什么运行得更快? - Ramnath
1
@BrandonBertelsen:这取决于有多少组;当组数较少时,for循环更快,我猜测是因为其中的求和非常快。但是当组数很大时,它会变得很慢,甚至比分割解决方案还要慢。 - Aaron left Stack Overflow

1

这里有一个稍微不同的方法。

s <- rep(1:length(v), v)
l <- split(x, s)
y <- sapply(l, sum)

或者可以使用tapply将其转换为一行代码:tapply(x, rep(1:length(v), v), sum) - joran

0

尝试类似这样的内容:

for (i in 1:length(v)) { 
    y[i] <- ifelse(i > 1,sum(x[v[i-1]:v[i]]), sum(x[1:v[i]]))
}

我认为这需要一个 v <- cumsum(v),而且 v[i-1] 应该是 (v[i-1]+1) - Aaron left Stack Overflow
1
更好的方法是使用 v <- c(0, cumsum(v)) 来避免使用 ifelse - Aaron left Stack Overflow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接