Michael Hallquist
Michael Hallquist

Reputation: 492

How to index a multidimensional R array dynamically?

I am working on some data in R that consist of four-dimensional arrays composed of three spatial dimensions and a time dimension: x, y, z, t. For some of my analyses, I would like to obtain all of the data in the time dimension for a set of spatial coordinates x, y, z. Thus far, I have used the which function to obtain the indices of the spatial locations of interest. But when I go to obtain all relevant data in the time dimension corresponding to the spatial locations, I cannot find an elegant R solution and have resorted to using repmat, a ported MATLAB function.

a4d <- array(rnorm(10000), rep(10,4)) #x, y, z, t

#arbitrary set of 3d spatial indices x, y, z (here, using high values at first timepoint)
indices <- which(a4d[,,,1] > 2, arr.ind=TRUE)
str(indices)

# int [1:20, 1:3] 10 2 6 5 8 2 6 8 2 10 ...
# - attr(*, "dimnames")=List of 2
# ..$ : NULL
# ..$ : chr [1:3] "dim1" "dim2" "dim3"

#Now, I would like to use these indices to get data x, y, z for all t

#Intuitive, but invalid, syntax (also not clear what the structure of the data would be)
#a4d[indices,]

#Ugly, but working, syntax
library(pracma)

#number of timepoints
nt <- dim(a4d)[4]

#create a 4d lookup matrix
lookup <- cbind(repmat(indices, nt, 1), rep(1:nt, each=nrow(indices)))

#obtain values at each timepoint for indices x, y, z
result <- cbind(lookup, a4d[lookup])

This solution works okay for the stated purpose, but seems ugly conceptually. Ideally, I would like a 2-dimensional matrix at the end: index x time. So, in this case, with 20 x, y, z coordinates in the lookup, and 10 timepoints, a 20 x 10 matrix would be ideal where rows represent each row of indices (don't need to preserve the x, y, z, values necessarily) and each column is a timepoint.

Is there is a good way to do this in R? I have played around with do.call("[", list ... etc. and using outer and prod, but these haven't worked as I had hoped.

Thanks for any suggestions! Michael

Upvotes: 4

Views: 2886

Answers (3)

Will
Will

Reputation: 1313

Accessing by each dimension individually does not work as @tilo-wiklund or I expect. Instead of 23 rows across 10 time steps, the result is a 23x23x23 cube across the 10 time steps.

r.idvdim <- a4d[indices[,1],indices[,2],indices[,3],]
r.apply  <- apply(a4d, 4, `[`, indices)
r.cbind  <- matrix(a4d[lookup],ncol=nt)

dim(r.idvdim)     # [1] 23 23 23 10
dim(r.apply)      # [1] 23 10
dim(r.cbind)      # [1] 23 10

Upvotes: 1

flodel
flodel

Reputation: 89057

I think you are looking for:

apply(a4d, 4, `[`, indices)

And to check that our results match:

result1 <- matrix(result[,5], ncol = 10)
result2 <- apply(a4d, 4, `[`, indices)
identical(result1, result2)
# [1] TRUE

Upvotes: 7

Tilo Wiklund
Tilo Wiklund

Reputation: 821

I'm probably missing something, but don't you simply want a4d[indices[,1],indices[,2],indices[,3],]?

Upvotes: 1

Related Questions