Reputation: 380
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
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
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