I have a 13 dimensional array: MatrixQ<-array(0,dim=c(2,2,2,2,2,2,2,2,2,2,2,2,3))
How can I refer to ith dimension of it in a loop?
for (i in 1:13) { I want to assign the number i to the ith dimension of the array } What command I should use? Thanks,
To my knowledge, there is no easy way of doing this built into R. However, you can take advantage of the fact that an array is actually stored as a vector with a dimension (dim
) attribute. You can therefore actually access elements directly without using multiple commas. The difficulty is working out which indices of the underlying vector represent the slice of the array you want. This just requires a bit of maths.
I think you're looking for something like this:
at <- function(Array, Dimension, Slice, index = FALSE)
{
if(!is.array(Array))
stop("'at()' can only be called on arrays")
if(!(is.numeric(Dimension) && Dimension > 0))
stop("Invalid value of Dimension supplied to 'at()'")
# Get the numbers of dimensions and elements in our Array
n <- length(Array)
dims <- dim(Array)
n_dims <- length(dims)
if(Dimension > n_dims | Dimension < 1)
stop("Invalid dimension chosen in 'at()'")
if(max(Slice) > dims[Dimension])
stop("Invalid slice chosen for given dimension")
final_result <- numeric()
for(i in seq_along(Slice))
{
run_length <- cumprod(c(1, dims)[-(n_dims + 1)])[Dimension]
skip_length <- run_length * (dims[Dimension] - 1)
# Now we simply make a repeating pattern of membership / non-membership
pattern <- rep_len(c(rep(T, run_length), rep(F, skip_length)), n)
shifted_pattern <- c(rep(FALSE, run_length * (Slice[i] - 1)), pattern)
loop_result <- which(shifted_pattern[seq(n)])
final_result <- c(final_result, loop_result)
}
if(index == FALSE)
{
dims[Dimension] <- length(Slice)
return(array(Array[final_result], dim = dims))
}
return(sort(final_result))
}
Here's how you can use it. Start with an array (let's have just 3 dimensions)
my_array <- array(0, dim = c(2, 3, 4))
my_array
#> , , 1
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#>
#> , , 2
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#>
#> , , 3
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#>
#> , , 4
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
Now I can get the indices of the underlying array that represent the 2nd matrix of the third dimension (i.e. my_array[, , 2]
) like this:
at(my_array, 3, 2, index = T)
# [1] 7 8 9 10 11 12
That means if I write anything to my_array[c(7, 8, 9, 10, 11, 12)]
, it will change the elements in the appropriate slice of the matrix:
my_array[at(my_array, 3, 2, index = T)] <- 69
my_array
#> , , 1
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#>
#> , , 2
#>
#> [,1] [,2] [,3]
#> [1,] 69 69 69
#> [2,] 69 69 69
#>
#> , , 3
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#>
#> , , 4
#>
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
And of course this means we can do a loop where we can select dimensions. Here, we'll first reset my_array to zeros, then we'll put the dimension number into each element in the first slice within that dimension. Notice that some cells get overwritten in the process if they belong to the first slice of multiple dimensions.
# Reset my_array first
my_array[] <- 0
for(i in 1:3)
{
my_array[at(my_array, i, 1, index = T)] <- i;
}
my_array
#> , , 1
#>
#> [,1] [,2] [,3]
#> [1,] 3 3 3
#> [2,] 3 3 3
#>
#> , , 2
#>
#> [,1] [,2] [,3]
#> [1,] 2 1 1
#> [2,] 2 0 0
#>
#> , , 3
#>
#> [,1] [,2] [,3]
#> [1,] 2 1 1
#> [2,] 2 0 0
#>
#> , , 4
#>
#> [,1] [,2] [,3]
#> [1,] 2 1 1
#> [2,] 2 0 0
Although the example is for a 3-D array, this should work for any number of dimensions.
The reason for the index = T
is that by omitting that, we can directly get the slice we want without using indexing:
at(my_array, 3, 1:2)
#> , , 1
#>
#> [,1] [,2] [,3]
#> [1,] 3 3 3
#> [2,] 3 3 3
#>
#> , , 2
#>
#> [,1] [,2] [,3]
#> [1,] 2 1 1
#> [2,] 2 0 0
For the example in the OP's answer, you could use this to do as follows:
Indexlist2 <- numeric()
for(j in 2:4)
{
if(StartPoint2QQQ[j] == 1)
{
j_matches <- which(at(MatrixQQQ, j, 1) == max(at(MatrixQQQ, j, 1)), arr.ind = T)
Indexlist2 <- rbind(Indexlist2, j_matches)
}
}
Now we have
Indexlist2
#> dim1 dim2 dim3 dim4
#> [1,] 2 2 1 2
#> [2,] 2 2 2 1
MatrixQQQ[Indexlist2]
#> [1] 5.0 0.2