1

There is something I want to do which I believe should be easily achieved with rollapply, but I am having some trouble.

For simple vectors we have

> a <- c(1:10)
> a
 [1]  1  2  3  4  5  6  7  8  9 10
> rollapply(a, 2, mean)
 [1] 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5

which is as it should be. But problems arise for higher dimensions

> b <- array(c(1:6), c(2,3))
> b
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> rollapply(b, 2, mean)
     [,1] [,2] [,3]
[1,]  1.5  3.5  5.5

which is fine if I want to apply over columns - but surely there must also be some way to apply over rows instead and get

     [,1] [,2]
[1,]    2    4
[2,]    3    5

The rollapply function offers no way to do this that I can see.

Drubbels
  • 327
  • 2
  • 4
  • 11

3 Answers3

3

You could simply transpose your matrix and then transpose it back:

t(rollapply(t(b), 2, mean))
     [,1] [,2]
[1,]    2    4
[2,]    3    5
Onyambu
  • 67,392
  • 3
  • 24
  • 53
2

We can simply use apply with MARGIN = 1 to loop over the rows and apply the rollapply. The MARGIN can be adjusted for higher dimensions as well i.e. it is more general solution

t(apply(b, 1, function(x) rollapply(x, 2, mean)))

-output

#      [,1] [,2]
#[1,]    2    4
#[2,]    3    5

Or use dapply from collapse

library(collapse)
dapply(b, FUN = function(x) rollapply(x, 2, fmean), MARGIN = 1)
akrun
  • 874,273
  • 37
  • 540
  • 662
  • 1
    Thank you, this seems to do the trick in this case. I have just posted a different but related question at https://stackoverflow.com/questions/67511569/rollapply-for-multidimensional-arrays-r - would you perhaps consider taking a look at that one as well? – Drubbels May 12 '21 at 22:08
2

A base R option

> do.call(`+`, lapply(c(1, ncol(b)), function(k) b[, -k])) / 2
     [,1] [,2]
[1,]    2    4
[2,]    3    5

Follow-UP

If you would like to implement with base R and extend to general cases, i.e., more than 2, then you can try the code below, where a function f is defined:

f <- function(b, m) {
  apply(
    simplify2array(
      lapply(
        data.frame(t(embed(seq(ncol(b)), m))[m:1, ]),
        function(k) b[, k]
      )
    ), 1:2, mean
  )
}

and you will see

> f(array(c(1:6), c(2, 3)), 2)
     [,1] [,2]
[1,]    2    4
[2,]    3    5

> f(array(c(1:12), c(2, 6)), 4)
     [,1] [,2] [,3] [,4]
[1,]    3    5    7    9
[2,]    4    6    8   10
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
  • This is very interesting - could this be extended for window sizes other than 2? And for a weighted average (as opposed to a straight-up mean)? – Drubbels May 12 '21 at 22:12
  • @Drubbels Yes, you can see my update, where a function `f` is defined such that you can used it for general cases. Regarding the weighted average, you'd better to provide an example to show how you want it, since there are many ways to add weights if you don't specify one. – ThomasIsCoding May 13 '21 at 12:14