rollapply for two-dimensional arrays (R)

Viewed 87

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.

3 Answers

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

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)

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
Related