Search code examples
rmatrix

How to split a square matrix into cubes without loops


I'm struggling to split a matrix without using loops. I want to split a 9x9 matrix into nine 3x3 matrices (not overlaping). I tried using split() passing a vector as the "factor" grouping without success. Also tried using asplit() without success. I think I don't understand how "MARGIN" works on asplit(). I can get the 3x3 "cubes" using loops, but I'm just wondering if there's a way to do it with simple utility functions.

m <- matrix(1:81, 9, 9)

I want to store the 9 resulting 3x3 matrices in a list. The expected result for first cube is:

cube[[1]]

     [,1] [,2] [,3]
[1,]   01   10   19
[2,]   02   11   20
[3,]   03   12   21

I tried

# all these split by row
indices <- matrix(1:9, 3, 3)
cubes <- split(m, as.vector(indices))

indices <- array(1:9, dim = c(3, 3, 9))
cubes <- split(m, as.vector(indices))

indices <- array(matrix(1:9, 3, 3), dim = c(3, 3, 9))
cubes <- split(m, as.vector(indices))

# this gives an error
asplit(m, MARGIN = c(3,1))

Solution

  • There is a function as.blockmatrix in package blockmatrix that does this. Although the function is written in base R and uses for-loops

     blockmatrix::as.blockmatrix(m, 3,3)
    

    If you want to use base R, you could write your own function as follows:

    blocks <- function(x, nr, nc){
      c(tapply(x, list((row(x) - 1)%/%nr, (col(x) -  1)%/%nc), array, c(nr, nc)))
    }
    
    blocks(m, 3, 3)
    

    EDIT:

    playing around with aperm was able to get the below code to work:

    c(asplit(aperm(array(m, c(3,3,3,3)), c(1,3,4,2)), 3:4))