Indexing and subscripts for R arrays with variable dimensions - arrays

Suppose you have an n-dimensional, homogeneous data structure (array) in R with a variable but fixed number of dimensions:
n_dim <- 3
n_row <- 3
a <- array(seq(n_row ^ n_dim), dim = rep(n_row, n_dim))
a
>, , 1
>
> [,1] [,2] [,3]
>[1,] 1 4 7
>[2,] 2 5 8
>[3,] 3 6 9
>[..]
Is there a simple syntax to access a subscript of a given dimension, instead of counting the commas from n_dim or counting the position with 1-dim positions a[c(1,...)]? Is there any a[get row in dim] method that provides this already?
# Give me the 1st row of the "last" dimension
expr <- paste0("a[",
paste0(rep(", ", n_dim - 1), collapse = ""),
x,
"]")
expr
> [1] "a[, , 1]"
eval(parse(text = expr))
> [,1] [,2] [,3]
>[1,] 1 4 7
>[2,] 2 5 8
>[3,] 3 6 9
Thank you!

One option would be abind
library(abind)
asub(a, 1, n_dim)
# [,1] [,2] [,3]
#[1,] 1 4 7
#[2,] 2 5 8
#[3,] 3 6 9

Related

R - Find unique minimum across array dimension

In a 4D array, I would like to find the unique minimum value across the 4th dimension. I want to get a matrix of the array indices for the minimum.
I have tried solving the issue with the following code block. I would have liked using which.min, but I haven't found a good way to return the array indices.
dims =c(3,3,3,4)
# create sample data with multiple mins in [,,,1]
mat_rep = array(c(rep(0,3),sample(1:prod(dims))), dim = dims)
pos_rep = apply(mat_rep, 4, function(x) which(x == min(x), arr.ind = T)) # get position of unique minimum
# create sample data with unique min
mat_norep = array(sample(1:prod(dims)), dim = dims)
pos_norep = apply(mat_norep, 4, function(x) which(x == min(x), arr.ind = T))
# formating depending on class of pos_ object
format_pos = function(x, dims){
if(class(x) == "matrix") x = t(x)
if(class(x) == "list") x = do.call(rbind, lapply(x, head, 1))
x = cbind(x, 1:dims[4]) # add 4th dimension
return(x)
}
format_pos(pos_norep, dims = dims)
format_pos(pos_rep, dims = dims)
The described solution works, however it doesn't work generally and the if(class()) and cbind(x, 1:dims[4]) in my opinion is prone to producing errors.
Does someone have a cleaner way of solving this issue?
To create uniform outputs you can call arrayInd explicitly on the output of apply(..., which.min), instead of implicitly as in which(..., arr.ind = TRUE). The fourth dimension indices still need to be added manually though:
## add 4D indices using 1D values
start.ind <- (seq_len(dims[4]) - 1) * prod(head(dims, 3))
arrayInd(apply(mat_rep, 4, which.min) + start.ind, .dim = dims)
#> [,1] [,2] [,3] [,4]
#> [1,] 1 1 1 1
#> [2,] 1 3 3 2
#> [3,] 1 3 2 3
#> [4,] 3 1 1 4
arrayInd(apply(mat_norep, 4, which.min) + start.ind, .dim = dims)
#> [,1] [,2] [,3] [,4]
#> [1,] 2 1 3 1
#> [2,] 1 2 1 2
#> [3,] 1 2 1 3
#> [4,] 2 2 3 4
## add 4D indices using cbind
cbind(arrayInd(apply(mat_rep, 4, which.min), .dim = head(dims, 3)), seq_len(dims[4]))
#> [,1] [,2] [,3] [,4]
#> [1,] 1 1 1 1
#> [2,] 1 3 3 2
#> [3,] 1 3 2 3
#> [4,] 3 1 1 4
cbind(arrayInd(apply(mat_norep, 4, which.min), .dim = head(dims, 3)), seq_len(dims[4]))
#> [,1] [,2] [,3] [,4]
#> [1,] 2 1 3 1
#> [2,] 1 2 1 2
#> [3,] 1 2 1 3
#> [4,] 2 2 3 4
Data
dims <- c(3,3,3,4)
mat_rep <- array(c(rep(0,3),sample(1:prod(dims))), dim = dims)
mat_norep <- array(sample(1:prod(dims)), dim = dims)

Apply function for multidimensional array in R to find the mean for each column at each array

z <- array(1:12, dim = c(2,2,3))
z
, , 1
[,1] [,2]
[1,] 1 3
[2,] 2 4
, , 2
[,1] [,2]
[1,] 5 7
[2,] 6 8
, , 3
[,1] [,2]
[1,] 9 11
[2,] 10 12
For the above array of 3 matrices, I would like to create one single matrix by taking the mean values of each column for each array. So my expected matrix dimension should be 3 by 2. The first entry should be the mean of 1 & 2. The second entry of the first row is the mean of 3 & 4.
Can I use any apply function or any loop to create the matrix?
Appreciate your help.

Using a list-matrix to supply indices to N-dimensional object

I am working on a partial differential equations project where some N-dimensional objects are required. I got stuck in padding one N-dim object with a copy of each of its dimensions. Here is the function:
padreplicate <- function(a, padSize) {
# Pad an array by replicating values.
numDims <- length(padSize)
idx <- vector("list", numDims)
for (k in 1:numDims) {
M <- dim(a)[k] # 32
onesVector <- ones(1, padSize[k])
idx[[k]] <- c(onesVector, 1:M, M * onesVector)
}
# return(a[ unlist(idx[1]), unlist(idx[2]) ]) # this works for 2D
# return(a[ idx[[1]], idx[[2]] ]) # this also works for 2D
# return(a[apply(idx, 1, function(x) unlist[x])]) #:( doesn't work
# a[sapply(apply(idx, 1:length(dim(idx)), function(x) eval(parse(text=x))), unlist)] #:(
# 2D: "a[idx[[1]], idx[[2]]]" 3D: "a[idx[[1]], idx[[2]]], idx[[3]]]"
dim_text = paste0("a", "[ ",
paste0(sapply(1:length(idx), function(x)
paste0("idx", "[[", x, "]]")), collapse = ", ")," ]")
eval(parse(text=dim_text)) # this works
}
The first argument is the N-dim object; it could be a matrix, a 3D array or higher dimensional array.
An example for a 2D object or matrix would be:
# pad a matrix 4x3 with c(1,1)
set.seed(123456)
mx = matrix(sample.int(9, size = 9*100, replace = TRUE), nrow = 4, ncol = 3)
mx
# [,1] [,2] [,3]
# [1,] 8 4 9
# [2,] 7 2 2
# [3,] 4 5 8
# [4,] 4 1 6
padreplicate(mx, c(1,1))
The padded matrix looks like this:
[,1] [,2] [,3] [,4] [,5]
[1,] 8 8 4 9 9
[2,] 8 8 4 9 9
[3,] 7 7 2 2 2
[4,] 4 4 5 8 8
[5,] 4 4 1 6 6
[6,] 4 4 1 6 6
For a 3-D array the input array and the padded array.
ar = array(sample.int(9, size = 9*100, replace = TRUE), dim = c(3, 3, 1))
ar
padreplicate(ar, c(1,1,1))
# input 3x3x1 array
, , 1
[,1] [,2] [,3]
[1,] 3 4 6
[2,] 7 2 9
[3,] 3 4 8
# padded 5x5x3 array
, , 1
[,1] [,2] [,3] [,4] [,5]
[1,] 3 3 4 6 6
[2,] 3 3 4 6 6
[3,] 7 7 2 9 9
[4,] 3 3 4 8 8
[5,] 3 3 4 8 8
, , 2
[,1] [,2] [,3] [,4] [,5]
[1,] 3 3 4 6 6
[2,] 3 3 4 6 6
[3,] 7 7 2 9 9
[4,] 3 3 4 8 8
[5,] 3 3 4 8 8
, , 3
[,1] [,2] [,3] [,4] [,5]
[1,] 3 3 4 6 6
[2,] 3 3 4 6 6
[3,] 7 7 2 9 9
[4,] 3 3 4 8 8
[5,] 3 3 4 8 8
These are all correct results.
My question is this: "is there a better way of doing this N-dim padding operation?
EDIT 1
Thanks to Frank, now the N-dimensional padreplicate function:
padreplicate <- function(a, padSize) {
# Pad an array by replicating values.
numDims <- length(padSize)
idx <- vector("list", numDims)
for (k in 1:numDims) {
M <- dim(a)[k] # 32
onesVector <- ones(1, padSize[k])
idx[[k]] <- c(onesVector, 1:M, M * onesVector)
}
do.call( `[`, c(list(a), idx))
}
EDIT 2
Using matrix instead of my function ones. Sorry about that.
padreplicate <- function(a, padSize) {
# Pad an array by replicating values.
numDims <- length(padSize)
idx <- vector("list", numDims)
for (k in 1:numDims) {
M <- dim(a)[k] # 32
onesVector <- matrix(1, 1, padSize[k])
idx[[k]] <- c(onesVector, 1:M, M * onesVector)
}
do.call( `[`, c(list(a), idx))
}

R: extract matrix from array, using a matrix of indices

I´m coding in R and I have a 3 dimensional array that contains data (ab in the example). Then I have a matrix that contains indices of the 3rd array dimension (idx). This matrix has the same number of rows and columns of the array. I want to use the indices contained in idx to extract data from the array, to get a matrix with same dimension of idx. Please see the example below:
a <- c(1:9)
b <- rev(a)
#array of data
ab <- array(c(a,b), dim = c(3,3,2))
ab
, , 1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
, , 2
[,1] [,2] [,3]
[1,] 9 6 3
[2,] 8 5 2
[3,] 7 4 1
#matrix of indices
idx <- matrix(sample(1:2,9,replace=TRUE), nrow = 3)
idx
[,1] [,2] [,3]
[1,] 2 2 2
[2,] 2 1 1
[3,] 1 1 1
#now I want to get the following matrix:
[,1] [,2] [,3]
[1,] 9 6 3
[2,] 8 5 8
[3,] 3 6 9
#these two don´t do the job
ab[idx]
ab[ , ,idx]
Does anybody know how can I get that?
Many thanks!
Sara
We need an index for row/column and the third dimension (from 'idx') to extract the elements. We do this by cbinding the row index, column index with the 'idx'.
i1 <- dim(ab)[1]
j1 <- dim(ab)[2]
matrix(ab[cbind(rep(seq_len(i1), j1),rep(seq_len(j1), each = i1), c(idx))], ncol=3)
# [,1] [,2] [,3]
#[1,] 9 6 3
#[2,] 8 5 8
#[3,] 3 6 9
Might be a bit complicated, but it works at least:
First of all my idx:
[,1] [,2] [,3]
[1,] 2 2 1
[2,] 1 2 1
[3,] 2 2 2
First I made it both to a vector:
idVec <- as.vector(idx)
d3mat <- apply(ab,3,as.vector)
Then I constructed a vector, to run over each element:
len <- length(idVec)
len <- 1:len
Then I apllied a function to each element of the vector len:
resultvec <- sapply(len, function(x, vec, mat){return(mat[x,vec[x]])}, mat=d3mat, vec=idVec)
And then transform the result back to a matrix:
matrix(resultVec,3,3)
[,1] [,2] [,3]
[1,] 9 6 7
[2,] 2 5 8
[3,] 7 4 1

Converting a complicated list of matrices into an array of matrices

I would like to convert a list of matrices into an array of stapled matrices. My list looks like l3 in:
getme<-matrix(1,3,3)
getme2<-matrix(2,4,4)
l1<-list(getme,getme2)
names(l1)<-c('getme','getme2')
getme<-matrix(3,3,3)
getme2<-matrix(4,4,4)
l2<-list(getme,getme2)
names(l2)<-c('getme','getme2')
l3<-list(l1,l2)
The goal is to append each conformable matrix into an array. The name of the array/variable should be equal to the name of the level of the list.
So for example:
getme<-array(dim=c(3,3,2))
m=2
for(i in 1:m){getme[,,i]<-l3[[i]]$getme}
Since my lists get vary large in terms of both mand number of getmematrices, I need to automatize this procedure across all m levels of the list l3. Ideally I would want code that produces j arrays getme, getme2,...getme_j, where only l3is used as an input. That is the names of the variable levels, names(l3[[1]]), are applied on the way.
The combination of matrices into arrays is easy with abind from the abind package:
library(abind)
res <- lapply(seq_along(l3[[1]]), function(x)
abind(lapply(l3, "[[", x), along = 3))
The outer lapply function is used to apply a function to the numbers form 1 to the length of the list l3. The current number is represented by parameter x. The other lapply command is used to extract the xth element from the list l3. These elements are combined into an array with abind. Since the length of l3 is 2, the outer lapply returns a list of length 2.
This returns a list of arrays:
[[1]]
, , 1
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
, , 2
[,1] [,2] [,3]
[1,] 3 3 3
[2,] 3 3 3
[3,] 3 3 3
[[2]]
, , 1
[,1] [,2] [,3] [,4]
[1,] 2 2 2 2
[2,] 2 2 2 2
[3,] 2 2 2 2
[4,] 2 2 2 2
, , 2
[,1] [,2] [,3] [,4]
[1,] 4 4 4 4
[2,] 4 4 4 4
[3,] 4 4 4 4
[4,] 4 4 4 4
You can use the original names with
names(res) <- names(l3[[1]])

Resources