2

I am trying to apply a function over consecutive values of two vectors. The problem can be simplified to this:

x = c(2,4,6,8,10)
y = c(1,2,3,4,5)

for (i in 1:(length(x)-1)) {
    a = mean(x[i:(i+1)])
    b = mean(y[i:(i+1)])
    print(a/b)
}

I am wondering what would be a more efficient way to do the same by using a function from the apply family? Thanks!

striatum
  • 1,428
  • 3
  • 14
  • 31
  • [Calculating moving average](https://stackoverflow.com/questions/743812/calculating-moving-average) – Henrik Jul 05 '18 at 09:25

4 Answers4

4
library(zoo)
rollmean(x,2) / rollmean(y,2)
Lennyy
  • 5,932
  • 2
  • 10
  • 23
4

you can use base R:

a = sapply(1:(length(x)-1),function(x)x:(x+1))
colMeans(structure(x[a],.Dim=dim(a)))/colMeans(structure(y[a],.Dim=dim(a)))
[1] 2 2 2 2

or even

 tapply(x[a], col(a), mean)/tapply(y[a], col(a), mean)

These base functions seem to work faster than the rollapply since the rollapply will do the rollapply twice:

microbenchmark::microbenchmark(
   COLMEANS={a=sapply(1:(length(x)-1),function(x)x:(x+1))
   colMeans(structure(x[a],.Dim=dim(a)))/
     colMeans(structure(y[a],.Dim=dim(a)))},
   ROLLAPPLY=rollapply(x,2,mean)/rollapply(y,2,mean),
   TAPPLY={a=sapply(1:(length(x)-1),function(x)x:(x+1))
     tapply(x[a], col(a), mean)/tapply(y[a], col(a), mean)
   },
   ROLLMEANS=rollmean(x,2) / rollmean(y,2)
 )
 Unit: microseconds
      expr      min        lq      mean   median        uq      max neval
  COLMEANS   76.517  109.0040  187.7223  138.499  178.0405 4598.685   100
 ROLLAPPLY 1752.185 1954.8040 2144.2619 2028.543 2211.9260 6244.430   100
    TAPPLY  398.827  519.3725  665.5016  604.224  682.4505 5304.859   100
ROLLMEANS 1366.610 1619.6715 1815.3349 1731.0260 1957.7965 2615.240   100
Onyambu
  • 67,392
  • 3
  • 24
  • 53
  • This is a great answer! Thanks! However, if, in general, the function is not a simple mean but "handmade" one, could you use another `apply` over instances of a? – striatum Jul 05 '18 at 11:58
3

You could use rollapply from the zoo library:

  library(zoo)
  rollapply(x,2,mean)/rollapply(y,2,mean)
Ma Li
  • 131
  • 6
3

Well, maybe stating the obvious, but :

(x[-1] + x[-length(x)]) / (y[-1] + y[-length(x)])
# [1] 2 2 2 2

(that's 15 times faster than the COLMEANS solution from Onyambu's benchmark)

moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
  • Nice. I'd also be very surprised if `stats::filter()` wasn't also faster (than colMeans) for a rolling mean when the data gets bigger. Something like Matti's answer here - https://stackoverflow.com/a/4862334/496803 – thelatemail Jul 05 '18 at 10:34