user36302
user36302

Reputation: 380

R: Apply to slices of an array and return an array

I want to find the simplest way to apply a function across slices of an array, where each application of the function returns a matrix, and receive a sensible array object as the final answer.

I feel like there should be a simple way to do this with apply function, but I feel like I need a simplify = 'array' option, which would be super convenient. The closest I get is this:

dims = c(2,3,10,4,5)
arr = array(runif(prod(dims)),dim = dims, dimnames = 
          list(
            paste0('a',1:dims[1]),
            paste0('b',1:dims[2]),
            paste0('c',1:dims[3]),
            paste0('d',1:dims[4]),
            paste0('e',1:dims[5])
          ))

result = apply(arr, c(4,5), function(x) apply(x, c(3), function(y) (y - y[,1])^2 ) )

The result should be an array with dimension c(2,3,10,4,5) or some permutation thereof.

I could get to the right form by calling as.array on result with the appropriate dimensions, but there should be an easier way that preserves the dimnames automatically.

Upvotes: 1

Views: 2519

Answers (2)

user36302
user36302

Reputation: 380

This can be done with the aaply function in plyr package. This is one of several apply functions in this package, where the naming convention is that aa in aaply stands for array to array.

dims = c(2,3,10,4,5)
arr = array(runif(prod(dims)),dim = dims, dimnames = 
          list(
            paste0('a',1:dims[1]),
            paste0('b',1:dims[2]),
            paste0('c',1:dims[3]),
            paste0('d',1:dims[4]),
            paste0('e',1:dims[5])
          ))

aresult = (aaply(arr, c(4,5), function(x) aaply(x, c(3), function(y) (y - y[,1])^2 ) ) )
result = aperm(abind(aresult), c(4,5,3,1,2))

Upvotes: 1

alistaire
alistaire

Reputation: 43354

To iterate over a multidimensional array with apply, specify the margins you want to keep. It's easier to visualize in two dimensions:

m <- matrix(1, 2, 3)

m
#>      [,1] [,2] [,3]
#> [1,]    1    1    1
#> [2,]    1    1    1

apply(m, 1, sum)
#> [1] 3 3

—iterating over rows keeps that dimension, and collapses the unmentioned column one. For the more complicated array in question, then,

res <- apply(arr, 2:5, function(x){mean(x^2)})
# or even just apply(arr^2, 2:5, mean) (h/t @thelatemail)

str(res)
#>  num [1:2, 1:3, 1:4, 1:5] 0.313 0.215 0.32 0.29 0.288 ...
#>  - attr(*, "dimnames")=List of 4
#>   ..$ : chr [1:2] "b1" "b2"
#>   ..$ : chr [1:3] "c1" "c2" "c3"
#>   ..$ : chr [1:4] "d1" "d2" "d3" "d4"
#>   ..$ : chr [1:5] "e1" "e2" "e3" "e4" ...

Upvotes: 0

Related Questions