每隔n个点求和

23

我有一个向量,需要将每个n个数字相加并返回结果。这是我目前计划的方法。是否有更好的方法?

v = 1:100
n = 10
sidx = seq.int(from=1, to=length(v), by=n)
eidx = c((sidx-1)[2:length(sidx)], length(v))
thesum = sapply(1:length(sidx), function(i) sum(v[sidx[i]:eidx[i]]))

这将给出:
thesum
 [1]  55 155 255 355 455 555 655 755 855 955
9个回答

33
unname(tapply(v, (seq_along(v)-1) %/% n, sum))
# [1] 55 155 255 355 455 555 655 755 855 955 

21

更新:

如果您想求出每n个连续数字的总和,请使用colSums
如果您想求出每第n个数字的总和,请使用rowSums

根据Josh的评论,这只适用于n可以完美地整除v的长度。

rowSums(matrix(v, nrow=n))
 [1] 460 470 480 490 500 510 520 530 540 550

colSums(matrix(v, nrow=n))
 [1]  55 155 255 355 455 555 655 755 855 955


1
只有当length(v)能够被n整除时才有效。否则,向量循环将会影响你的结果。(例如,v <- 1:3; n <- 2; matrix(v, nrow=n)。) - Josh O'Brien
只有在 matrix(..., byrow=TRUE) 的情况下才能工作,因此 @Andrie 的答案中使用了 colSums 而不是 rowSums - plannapus
1
@plannapus,不清楚OP是想要每个n个连续数还是每第n个数。 - Ricardo Saporta
1
如果是每个“第n个”数字,我会说550就是答案。例如第10个、第20个等等,而不是1、11……2、12……等等。 - Arun
@Arun,你从哪里开始数n个数字? - Ricardo Saporta
我将1到100中每个第n个数字理解为每10个数字:10、20、30、...、100。这相当于550,与Andrie和Josh的早期答案相同。 - Arun

13

更新

旧版本无法工作。这里提供一个新的答案,使用rep创建分组因子。无需使用cut

n <- 5 
vv <- sample(1:1000,100)
seqs <- seq_along(vv)
tapply(vv,rep(seqs,each=n)[seqs],FUN=sum)

你可以使用tapply

tapply(1:100,cut(1:100,10),FUN=sum)

或者获取一个列表
by(1:100,cut(1:100,10),FUN=sum)

编辑

如果你拥有 1:92,你可以用以下内容替换你的剪切:

cut(1:92,seq(1,92,10),include.lowest=T)

我能理解你为什么喜欢这个答案,但是对于一个随机的数字向量,在想要对每n个元素求和时,这个方法行不通,对吗? - Max M
@MaxM 没错,我会更新我的答案并包含一个新版本。 - agstudy

7

一种方法是将您的向量转换为矩阵,然后取列和:

colSums(matrix(v, nrow=n))
[1]  55 155 255 355 455 555 655 755 855 955

请注意:这个假设前提是你的输入向量可以被重新塑造成一个矩阵。如果不能,R会重复使用你的向量元素来完成矩阵。


4
v <- 1:100

n <- 10

cutpoints <- seq( 1 , length( v ) , by = n )

categories <- findInterval( 1:length( v ) , cutpoints )

tapply( v , categories , sum )

(+1)即使v=1.92且n=10,这也会给出正确的结果。 - Arun

3
我将提供另一种不需要任何apply函数的方法来完成它。
v <- 1:100
n <- 10

diff(c(0, cumsum(v)[slice.index(v, 1)%%n == 0]))
##  [1]  55 155 255 355 455 555 655 755 855 955

2
请注意,当如 v <- 1:99 这样操作时,这不会包括最后9个数字的总和(这可能是可以接受的,也可能不可以接受)。 - Josh O'Brien
nv = length(v); i = c(seq_len(nv %/% n) * n, if (nv %% n) nv else NULL),然后diff(c(0, cumsum(v)[i]))似乎可以处理length(v) == 0length(v) %% n != 0的边缘情况。 - Martin Morgan
如果我没记错的话,slice.index(v, 1) 可以被简化为 v - Rich Scriven

2

虽然我来晚了,但是我还没有看到rowsum()的答案。证明rowsum()tapply()更有效率,而且我认为相对于其他几个回答,它也非常高效。

rowsum(v, rep(seq_len(length(v)/n), each=n))[,1]
#  1   2   3   4   5   6   7   8   9  10 
# 55 155 255 355 455 555 655 755 855 955

使用 @Josh O'Brien 的分组技术可能会进一步提高效率。
rowsum(v, (seq_along(v)-1) %/% n)[,1]
#  0   1   2   3   4   5   6   7   8   9 
# 55 155 255 355 455 555 655 755 855 955 

只需在unname()中包装即可删除组名。


2

以下是目前提供的一些主要变体:

f0 <- function(v, n) {
    sidx = seq.int(from=1, to=length(v), by=n)
    eidx = c((sidx-1)[2:length(sidx)], length(v))
    sapply(1:length(sidx), function(i) sum(v[sidx[i]:eidx[i]]))
}

f1 <- function(v, n, na.rm=TRUE) {    # 'tapply'
    unname(tapply(v, (seq_along(v)-1) %/% n, sum, na.rm=na.rm))
}

f2 <- function(v, n, na.rm=TRUE) {    # 'matrix'
    nv <- length(v)
    if (nv %% n)
        v[ceiling(nv / n) * n] <- NA
    colSums(matrix(v, n), na.rm=na.rm)
}

f3 <- function(v, n) {                # 'cumsum'
    nv = length(v)
    i <- c(seq_len(nv %/% n) * n, if (nv %% n) nv else NULL)
    diff(c(0L, cumsum(v)[i]))
}

基本测试用例可能包括:

v = list(1:4, 1:5, c(NA, 2:4), integer())
n = 2

f0在最后一项测试中失败了,但这可能可以修复。

> f0(integer(), n)
Error in sidx[i]:eidx[i] : NA/NaN argument

累加方法f3存在舍入误差,并且v中早期出现NA会“污染”后面的结果。
> f3(c(NA, 2:4), n)
[1] NA NA

就性能而言,原始解决方案还不错。

> library(rbenchmark)
> cols <- c("test", "elapsed", "relative")
> v <- 1:100; n <- 10
> benchmark(f0(v, n), f1(v, n), f2(v, n), f3(v, n),
+           columns=cols)
      test elapsed relative
1 f0(v, n)   0.012     3.00
2 f1(v, n)   0.065    16.25
3 f2(v, n)   0.004     1.00
4 f3(v, n)   0.004     1.00

但是矩阵解决方案 f2 似乎既快速又灵活(例如,调整处理少于 n 个元素的尾随块的方式)

> v <- runif(1e6); n <- 10
> benchmark(f0(v, n), f2(v, n), f3(v, n), columns=cols, replications=10)
      test elapsed relative
1 f0(v, n)   5.804   34.141
2 f2(v, n)   0.170    1.000
3 f3(v, n)   0.251    1.476

2

一个办法是使用zoo中的rollapply:

rollapply(v, width=n, FUN=sum, by=n)
# [1]  55 155 255 355 455 555 655 755 855 955

如果 v 的长度不是 n 的倍数:

v <- 1:92

rollapply(v, width=n, FUN=sum, by=n, partial=T, align="left")
# [1]  55 155 255 355 455 555 655 755 855 183

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