Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply a function to each layer of a 3d array, returning an array

Tags:

r

Imagine you have a 3-dimensional array with rows, columns, and layers:

A <- array (1:27, c(3,3,3))

and imagine you have a function that takes a matrix as input and returns a matrix as output, like t.

How can you apply the function to each layer of the array, returning another array of the same size as the first?

I feel like I ought to be able to do it with apply somehow, but I can't.

Bonus question (I'd be very grateful if you answered this): is it faster to do this, or to make a list of each of the layer matrices and lapply the function to them?

--

Edit: please don't think that this question is answered - the answer below does not answer the question.

like image 821
Marius Kempe Avatar asked Dec 10 '12 23:12

Marius Kempe


People also ask

How do you write a 3D array?

You can think the array as a table with 3 rows and each row has 4 columns. Similarly, you can declare a three-dimensional (3d) array. For example, float y[2][4][3];

What is a 3 way array?

A three-way array X (or three-dimensional matrix) is an array of numbers xijk subscripted by three indices. A triad is a multiplicative array, xijk = aibjck.


2 Answers

You have to think about which margin(s) over which you wish to extract the values.

you can transpose each of the 3rd dimension matrices by applying over dimensions 1 and 2 (rows and columns for want of a better word)

apply(A,1:2,t)
, , 1

     [,1] [,2] [,3]
[1,]    1    2    3
[2,]   10   11   12
[3,]   19   20   21

, , 2

     [,1] [,2] [,3]
[1,]    4    5    6
[2,]   13   14   15
[3,]   22   23   24

, , 3

     [,1] [,2] [,3]
[1,]    7    8    9
[2,]   16   17   18
[3,]   25   26   27

You can also use plyr and aaply which may act more intuitively

library(plyr)

aaply(A,3,t)
, ,  = 1


X1   1  2  3
  1  1  4  7
  2 10 13 16
  3 19 22 25

, ,  = 2


X1   1  2  3
  1  2  5  8
  2 11 14 17
  3 20 23 26

, ,  = 3


X1   1  2  3
  1  3  6  9
  2 12 15 18
  3 21 24 27

As to which is faster lapply or apply, I would think perhaps lapply would win, but you would still have to do the thinking about which margins you wanted to extract the matrices from.

I usually find it far easier to think in one dimension. Everything would be more straight forward if the earth were flat!

like image 72
mnel Avatar answered Oct 01 '22 04:10

mnel


Another possibility for the transposition is function aperm.

A <- array (1:27, c(3,3,3))
# aperm permute the dimensions in the given order
# here we want to permute dimension 1 and 2
# so the new order is 2-1-3 instead of 1-2-3
aperm(A, c(2,1,3)) 
    , , 1

     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

, , 2

     [,1] [,2] [,3]
[1,]   10   11   12
[2,]   13   14   15
[3,]   16   17   18

, , 3

     [,1] [,2] [,3]
[1,]   19   20   21
[2,]   22   23   24
[3,]   25   26   27

As far as applying a function to each layer (say you want to multiply each layer by a different value) is concerned, here's another possibility:

vec <- c(1,5,3)
lapply(seq_along(vec), function(x)A[,,x]*vec[x])
[[1]]
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

[[2]]
     [,1] [,2] [,3]
[1,]   50   65   80
[2,]   55   70   85
[3,]   60   75   90

[[3]]
     [,1] [,2] [,3]
[1,]   57   66   75
[2,]   60   69   78
[3,]   63   72   81

But as you see it creates a list, so it needs one more step using function simplify2array:

simplify2array(lapply(seq_along(vec),function(x)A[,,x]*vec[x]))
, , 1

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

, , 2

     [,1] [,2] [,3]
[1,]   50   65   80
[2,]   55   70   85
[3,]   60   75   90

, , 3

     [,1] [,2] [,3]
[1,]   57   66   75
[2,]   60   69   78
[3,]   63   72   81

Or directly with sapply:

sapply(seq_along(vec), function(x)A[,,x]*vec[x], simplify="array")
, , 1

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

, , 2

     [,1] [,2] [,3]
[1,]   50   65   80
[2,]   55   70   85
[3,]   60   75   90

, , 3

     [,1] [,2] [,3]
[1,]   57   66   75
[2,]   60   69   78
[3,]   63   72   81
like image 26
plannapus Avatar answered Oct 01 '22 03:10

plannapus