Accessing particular element of an array

Hi!

I want to create a function, which takes input of a k-dimensional array, say Y of dimension (d_1 x d_2 x ...x d_k), and a k-dimensional vector x = (x_1, x_2, ..., x_k). I want to access the (x_1, x_2, ..., x_k)_th element of Y inside the function. I can do that by Y[x[1], x[2], ..., x[k]], if k is known.

But my problem is that k is unknown and so I can't just write Y[x[1], x[2], ..., x[k]] (with dots) inside the function. Also, if k is large, maybe even 20, it's pretty boring to type x[1], x[2], ..., x[20] manually.

My question is that is it possible to do it in an easier way, say, in the form Y[x]?

I'm giving an illustration below.

# 4-dimensional array
Y <- array(data = 1:120, dim = c(3, 5, 2, 4))

# 4-dimensional vector
x <- c(2, 3, 2, 3)

# element of Y at the x-th position
Y_x <- Y[x[1], x[2], x[3], x[4]]

# Objective is to get this by Y[x] or something like that
# to access the element in a general k-dimensional set-up
# and also to avoid typing x[i] repeatedly

Thanks.

Hi @Yarnabrina! You can't index an array with a vector, unfortunately, but you can index one with a matrix! The matrix needs to have a column for each dimension; each row of the matrix will be a value to access:

# here's your example data -------------------------------------

# 4-dimensional array
Y <- array(data = 1:120, dim = c(3, 5, 2, 4))

# 4-dimensional vector
x <- c(2, 3, 2, 3)

# access a single value in a 4-dimensional matrix --------------
# (rbind() and cbind() bind vectors into a matrix, but they work
# with just a single vector too!)

rbind(x)
#>      [,1] [,2] [,3] [,4]
#> [1,]    2    3    2    3

Y[rbind(x)]
#> [1] 83

# access several values in a 4-dimensional matrix --------------

x1 <- c(2, 3, 2, 3)
x2 <- c(1, 1, 1, 1)
x3 <- c(1, 2, 1, 2)

x_vals <- rbind(x1, x2, x3)
x_vals
#>    [,1] [,2] [,3] [,4]
#> x1    2    3    2    3
#> x2    1    1    1    1
#> x3    1    2    1    2

Y[x_vals]
#> [1] 83  1 34

Created on 2019-01-22 by the reprex package (v0.2.0).

How's that? :slight_smile:

2 Likes

It's awesome. Thanks

Just to note, Y[t(x)] works fine, Y[matrix(x)] is not needed.

1 Like

Thanks! I actually updated the example to use rbind() instead—not because there's any advantage to it, just because it's a bit less mental logistics when going from a single value to many values (and it signals more clearly which direction that matrix is going to run, IMO). But any or all of the above will get you where you need to go :slight_smile:

1 Like

I've a related question.

Can you suggest a way to access the marginal totals (or in general, the i_th, component of the j_th dimension, where 1 <= i <= d_j and 1 <= j <=k)?

For example, suppose I want Y[, 2,,]. How do I get it?

I couldn't find anything in the system library to do what you're asking, but the abind package has a function called asub that I think should do what you're asking! It essentially builds the subsetting calls for you. I actually found it already installed on my system; my guess is that it's a popular dependency (EDIT: turns out the stars package, which is a current effort to improve higher-dimensional spatiotemporal array functionality in R, uses this package).

Here're some examples. It's pretty flexible!

library(abind)
#> Warning: package 'abind' was built under R version 3.5.2

Y <- array(data = 1:120, dim = c(3, 5, 2, 4))

# index (idx) goes first, then dimension (dims)
asub(Y, 3, 2)
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    7   22
#> [2,]    8   23
#> [3,]    9   24
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   37   52
#> [2,]   38   53
#> [3,]   39   54
#> 
#> , , 3
#> 
#>      [,1] [,2]
#> [1,]   67   82
#> [2,]   68   83
#> [3,]   69   84
#> 
#> , , 4
#> 
#>      [,1] [,2]
#> [1,]   97  112
#> [2,]   98  113
#> [3,]   99  114
dim(asub(Y, 3, 2))
#> [1] 3 2 4

# add drop = FALSE to avoid collapsing a dimension when you
# only specify a single index
asub(Y, 3, 2, drop = FALSE)
#> , , 1, 1
#> 
#>      [,1]
#> [1,]    7
#> [2,]    8
#> [3,]    9
#> 
#> , , 2, 1
#> 
#>      [,1]
#> [1,]   22
#> [2,]   23
#> [3,]   24
#> 
#> , , 1, 2
#> 
#>      [,1]
#> [1,]   37
#> [2,]   38
#> [3,]   39
#> 
#> , , 2, 2
#> 
#>      [,1]
#> [1,]   52
#> [2,]   53
#> [3,]   54
#> 
#> , , 1, 3
#> 
#>      [,1]
#> [1,]   67
#> [2,]   68
#> [3,]   69
#> 
#> , , 2, 3
#> 
#>      [,1]
#> [1,]   82
#> [2,]   83
#> [3,]   84
#> 
#> , , 1, 4
#> 
#>      [,1]
#> [1,]   97
#> [2,]   98
#> [3,]   99
#> 
#> , , 2, 4
#> 
#>      [,1]
#> [1,]  112
#> [2,]  113
#> [3,]  114
dim(asub(Y, 3, 2, drop = FALSE))
#> [1] 3 1 2 4

# you can specify a range of indices too...
asub(Y, 3:5, 2)
#> , , 1, 1
#> 
#>      [,1] [,2] [,3]
#> [1,]    7   10   13
#> [2,]    8   11   14
#> [3,]    9   12   15
#> 
#> , , 2, 1
#> 
#>      [,1] [,2] [,3]
#> [1,]   22   25   28
#> [2,]   23   26   29
#> [3,]   24   27   30
#> 
#> , , 1, 2
#> 
#>      [,1] [,2] [,3]
#> [1,]   37   40   43
#> [2,]   38   41   44
#> [3,]   39   42   45
#> 
#> , , 2, 2
#> 
#>      [,1] [,2] [,3]
#> [1,]   52   55   58
#> [2,]   53   56   59
#> [3,]   54   57   60
#> 
#> , , 1, 3
#> 
#>      [,1] [,2] [,3]
#> [1,]   67   70   73
#> [2,]   68   71   74
#> [3,]   69   72   75
#> 
#> , , 2, 3
#> 
#>      [,1] [,2] [,3]
#> [1,]   82   85   88
#> [2,]   83   86   89
#> [3,]   84   87   90
#> 
#> , , 1, 4
#> 
#>      [,1] [,2] [,3]
#> [1,]   97  100  103
#> [2,]   98  101  104
#> [3,]   99  102  105
#> 
#> , , 2, 4
#> 
#>      [,1] [,2] [,3]
#> [1,]  112  115  118
#> [2,]  113  116  119
#> [3,]  114  117  120
dim(asub(Y, 3, 2))
#> [1] 3 2 4

# ... as well as several dimensions.
# but in this case, you need to provide the indices for each
# dimension separately, as a list
asub(Y, list(3, 3:5), 1:2)
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    9   24
#> [2,]   12   27
#> [3,]   15   30
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   39   54
#> [2,]   42   57
#> [3,]   45   60
#> 
#> , , 3
#> 
#>      [,1] [,2]
#> [1,]   69   84
#> [2,]   72   87
#> [3,]   75   90
#> 
#> , , 4
#> 
#>      [,1] [,2]
#> [1,]   99  114
#> [2,]  102  117
#> [3,]  105  120
dim(asub(Y, list(3, 3:5), 1:2))
#> [1] 3 2 4

# remember, drop = FALSE keeps the original dimensionality
asub(Y, list(3, 3:5), 1:2, drop = FALSE)
#> , , 1, 1
#> 
#>      [,1] [,2] [,3]
#> [1,]    9   12   15
#> 
#> , , 2, 1
#> 
#>      [,1] [,2] [,3]
#> [1,]   24   27   30
#> 
#> , , 1, 2
#> 
#>      [,1] [,2] [,3]
#> [1,]   39   42   45
#> 
#> , , 2, 2
#> 
#>      [,1] [,2] [,3]
#> [1,]   54   57   60
#> 
#> , , 1, 3
#> 
#>      [,1] [,2] [,3]
#> [1,]   69   72   75
#> 
#> , , 2, 3
#> 
#>      [,1] [,2] [,3]
#> [1,]   84   87   90
#> 
#> , , 1, 4
#> 
#>      [,1] [,2] [,3]
#> [1,]   99  102  105
#> 
#> , , 2, 4
#> 
#>      [,1] [,2] [,3]
#> [1,]  114  117  120
dim(asub(Y, list(3, 3:5), 1:2), drop = FALSE)
#> Error in dim(asub(Y, list(3, 3:5), 1:2), drop = FALSE): 2 arguments passed to 'dim' which requires 1

Created on 2019-01-22 by the reprex package (v0.2.0).

1 Like

Thanks for the detailed illustrative example.

I'm marking this as the solution, as accessing elements is just a special case of this. I love the previous one, though.

# multi-dimensional array
Y <- array(data = 1:120, dim = c(3, 5, 2, 4))

# accessing single element
x <- c(2, 3, 2, 3)

## equivalent to Y[t(x)]
(Y_x <- abind::asub(x = Y, idx = as.list(x)))
#> [1] 83

# accessing multiple elements
z1 <- c(2, 3, 2, 3)
z2 <- c(1, 1, 1, 1)
z3 <- c(1, 2, 1, 2)
z_vals <- rbind(z1, z2, z3)

## unname and as.numeric are used just to get in the same form as Y[z_vals]
(Y_z <- unname(obj = as.numeric(x = apply(X = z_vals, MARGIN = 1, FUN = function(t) abind::asub(x = Y, idx = as.list(t))))))
#> [1] 83  1 34

Created on 2019-01-22 by the reprex package (v0.2.1)

Thanks, once again. :slightly_smiling_face:

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.