6

I'm having a question about indexing 3 dim arrays.

Say I have a 3 dimensional array

x<- c(1:36)
dim(x) <- c(3,4,3) 

Now I want to extract values out of this array according to a matrix holding the 3rd dimension indices for all [i,j] positions.

y <- c(rep(1,4),rep(2,4),rep(3,4))
dim(y) <- c(3,4)

y
      [,1] [,2] [,3] [,4]
[1,]    1    1    2    3
[2,]    1    2    2    3
[3,]    1    2    3    3

So the result should be giving this:

     [,1] [,2] [,3] [,4]
[1,]    1    4   19   34
[2,]    2   17   20   35
[3,]    3   18   33   36

Is there some elegant way to do this? I know how to use two for loops to go over the array, but this is too slow for my data.

Richard Erickson
  • 2,568
  • 8
  • 26
  • 39
Atomhamster
  • 65
  • 1
  • 5

2 Answers2

7

help("[") tells us this:

Matrices and arrays

[...]

A third form of indexing is via a numeric matrix with the one column for each dimension: each row of the index matrix then selects a single element of the array, and the result is a vector.

Thus, we transform your y matrix to a shape that conforms with this.

library(reshape2)
z <- x[as.matrix(melt(y))]
dim(z) <- dim(y)
#     [,1] [,2] [,3] [,4]
#[1,]    1    4   19   34
#[2,]    2   17   20   35
#[3,]    3   18   33   36
Roland
  • 127,288
  • 10
  • 191
  • 288
  • Thank you very much for the help. And yes reading help files is a very good idea. Always. By the way is it possible to use some condition testing in the matrix of indices? like x>3? – Atomhamster Apr 12 '16 at 08:43
5

I'm looking at this as an opportunity for some code golf. It's definitely possible to do this as a one-liner:

> `dim<-`(x[cbind(c(row(y)), c(col(y)), c(y))], dim(y))
     [,1] [,2] [,3] [,4]
[1,]    1    4   19   34
[2,]    2   17   20   35
[3,]    3   18   33   36

As @Roland's answer shows, matrix/array indexing involves creating an n-column matrix and setting the columns equal to row, column, etc. position of each dimension of an n-dimensional array. We can use the row() and col() functions to extract the row and column positions of each element in y:

> row(y)
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    2    2    2    2
[3,]    3    3    3    3
> col(y)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    1    2    3    4
[3,]    1    2    3    4

and y itself gives the third-dimension positions. wrapping each of those in c() turns them into a vector, so that they can be cbind-ed together to create an extraction matrix.

Then, there's just some fun use of dim<-() to fit it all on one line.

Thomas
  • 43,637
  • 12
  • 109
  • 140
  • 1
    Thank you Thomas. I like your ambition. I'm also a big fan of using only base functions when possible. But I have to say I don't understand your fun use of dim. How does that work? What does the backticks do? – Atomhamster Apr 12 '16 at 08:50
  • @Atomhamster When you do `dim(x) <- dim(y)`, you're actually doing `dim<-(x, dim(y))` where `"dim<-"` is the name of a function. This is useful when you want to use a function like `dim<-()` or `names<-()`, etc. without first creating the lefthand-side object. (The backticks just let you call the `"dim<-()"` function directly, but they're hard to write in this comment markup.) – Thomas Apr 12 '16 at 09:05
  • Thanky you @Thomas for the explanation. Very interesting to know this trick. I guess it could save me some significant number of lines in my functions. – Atomhamster Apr 12 '16 at 11:25
  • @Atomhamster Another, perhaps more intuitive approach would be from the setter package: https://cran.r-project.org/web/packages/setter/setter.pdf – Thomas Apr 12 '16 at 12:54