Search code examples
rloopsmatrix

Subsetting multiple matrices of a specified size


Consider my 20x20 matrix:

M1 <- matrix(rnorm(400), nrow=20, ncol=20)

What I am trying to do is:

  • Subset a 5x5 block of this matrix
  • Calculate the median of this 5x5 block
  • Replace the middle value of this 5x5 block with that median value
  • Rinse and repeat with every 5x5 block in the matrix

So far I have managed to do this for all the columns with a fixed block of rows:

x <- 0:20

for (i in seq_along(x)) {
  matrixtest[i] <- median(as.matrix(M1[1:5, (1 + i):(5 + i)]))
  M1[3, 3 + seq_along(x)] <- matrixtest[seq_along(x)]
}

However, the problem is I want to also do this going down each row, and I can't figure out a way to do it without manually specifying the rows as I have done below.

matrixtest2[i] <- median(as.matrix(M1[2:6, (1 + i):(5 + i)]))
matrixtest3[i] <- median(as.matrix(M1[3:7, (1 + i):(5 + i)]))
matrixtest4[i] <- median(as.matrix(I[4:8, (1 + i):(5 + i)]))


M1[4, 4 + seq_along(x)] <- matrixtest2[seq_along(x)]
M1[5, 5 + seq_along(x)] <- matrixtest3[seq_along(x)]
M1[6, 6 + seq_along(x)] <- matrixtest4[seq_along(x)]

This approach is obviously undesirable - any insights on how to do this, either with a for loop or apply statement would be most appreciated, thanks.


Solution

  • Using a double for loop.

    > n <- 5L  ## define block length
    > res <- vector('list', (n - 1L)^2)  ## initialize result vector
    > for (i in seq_len(nrow(M1) - n + 1) - 1L) {
    +   for (j in seq_len(nrow(M1) - n + 1) - 1L) {
    +     tmp <- M1[1:n + i, 1:n + j]
    +     res[[i + 1L]][[j + 1L]] <- replace(tmp, ceiling(length(tmp)/2), median(tmp))
    +   }
    + }
    > 
    > res[[1]][[1]]
         [,1] [,2] [,3] [,4] [,5]
    [1,]    1    4    1    4    7
    [2,]    5    2    5    9    2
    [3,]    1    8    4    3    5
    [4,]    9    3    6    5    6
    [5,]    4    1    6    8    3
    > 
    > identical(res[[1]][[1]], replace(M1[1:5, 1:5], 13, median(M1[1:5, 1:5])))
    [1] TRUE
    

    Data:

    set.seed(42)
    M1 <- matrix(sample(1:9, 400, replace=TRUE), nrow=20, ncol=20)