for loop to sum values in a vector by groups of two in R

Viewed 50

Suppose I have the following code

u <- c(1,2,3,4,5,6)
l <- c()
for (i in j) {
  l[i] <- u[i]+u[i+1]
}

I would like to create a for-loop such that I get the three following values: u[1]+u[2] and u[3]+u[4] and finally u[5]+u[6].

3 Answers

A for-loop is not needed, you can do:

tapply(u, ceiling(seq_along(u)/2), sum)

 1  2  3 
 3  7 11 
for (i in 1:length(u)) {
  if(i %% 2 ==0){
    print(u[i] + u[i-1])
  }
}

i %% 2 == 0 means that i is an even number.

Result

[1] 3
[1] 7
[1] 11

I would propose the follwing, as it is vivid: create a length(u) by 2 matrix and sum up the rows

rowSums(matrix(u, ncol = 2, byrow = TRUE))

This also chould be a relly fast solution, since you can make use of massive vectorization in R. For example

u <- c(1,2,3,4,5,6)
microbenchmark::microbenchmark(rowSums(matrix(u,ncol=2,byrow = TRUE)),
                               tapply(u, ceiling(seq_along(u)/2), sum))

gives on my machine the following benchmarks:

Unit: microseconds
                                       expr  min    lq   mean median   uq   max neval
 rowSums(matrix(u, ncol = 2, byrow = TRUE))  4.3  4.80  6.920   6.50  7.3  27.1   100
    tapply(u, ceiling(seq_along(u)/2), sum) 77.5 83.45 89.378  85.25 87.8 206.4   100

Eypanding u to say 10000 elements, the tapply-solution becomes very slow, see

u <- rnorm(1:1e4)
microbenchmark::microbenchmark(rowSums(matrix(u,ncol=2,byrow = TRUE)),
                               tapply(u, ceiling(seq_along(u)/2), sum))

Unit: microseconds
                                       expr     min       lq      mean   median       uq     max neval
 rowSums(matrix(u, ncol = 2, byrow = TRUE))    49.1    57.45    71.145    67.25    73.50   166.4   100
    tapply(u, ceiling(seq_along(u)/2), sum) 17995.5 19488.75 20653.714 20388.00 21223.55 34126.6   100

Update: You can even squeeze out more by using colSums(matrix(u, nrow = 2)), since with that you use the default byrow=FALSE-matrix-construction (also resulting in less code) in R giving:

u <- 1:1e5
microbenchmark::microbenchmark(rowSums(matrix(u, ncol = 2, byrow = TRUE)),
                               colSums(matrix(u, nrow = 2)))


Unit: microseconds
                                       expr   min     lq    mean median     uq    max neval
 rowSums(matrix(u, ncol = 2, byrow = TRUE)) 767.5 812.65 916.154 851.95 948.25 1669.4   100
               colSums(matrix(u, nrow = 2)) 217.0 232.90 263.355 240.00 272.45  469.8   100

Related