2
votes

I have a big matrix (2160x2160), where first I would like to define a block matrix (say 20x20) and fill the diagonal block matrix with 0s. I am struggling on how to achieve this.

For example:

From the matrix

         [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9]
  [1,]    1      2     3     4     5     6     7     8     9
  [2,]    11     12    13    14    15    16    17    18    19
  [3,]    21     22    23    24    25    26    27    28    29
  [4,]    31     32    33    34    35    36    37    38    39
  [5,]    41     42    43    44    45    46    47    48    49
  [6,]    51     52    53    54    55    56    57    58    59
  [7,]    61     62    63    64    65    66    67    68    69
  [8,]    71     72    73    74    75    76    77    78    79
  [9,]    81     82    83    84    85    86    87    88    89

I would first like to define the block matrix as a 3x3, and then fill the diagonal-block of the original matrix with zeros, my final intended result of the example would be:

         [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9]
  [1,]    0      0     0     4     5     6     7     8     9
  [2,]    0      0     0     14    15    16    17    18    19
  [3,]    0      0     0     24    25    26    27    28    29
  [4,]    31     32    33     0     0     0    37    38    39
  [5,]    41     42    43     0     0     0    47    48    49
  [6,]    51     52    53     0     0     0    57    58    59
  [7,]    61     62    63    64    65    66     0     0     0
  [8,]    71     72    73    74    75    76     0     0     0
  [9,]    81     82    83    84    85    86     0     0     0

I have been able to define a block matrix through the function as.blockmatrix of the blockmatrix library. I suppose that I should break the blocks in sub-matrices fill the ones that I need with 0 and then re-build my original matrix with the diagonals changed, but I am having difficulty doing this. Any other ideas to achieve this are also welcome (outside the potential solution with the blockmatrix library)

I have also looked at the bdiag function instide the Matrix package and also at the adiag function under magic, but ran out of ideas.

Thanks in advance for your help!

4

4 Answers

4
votes

Let M be your matrix and blocks of zeroes be of size n x n. Then

M[(col(M) - 1) %/% n - (row(M) - 1) %/% n == 0] <- 0

gives the result, where %/% indicates integer division. Using (col(M) - 1) %/% n - (row(M) - 1) %/% n gives a block matrix where the blocks on the diagonal contain only zeroes.

3
votes

You might try:

m <- matrix(1:(2160*2160), ncol = 2160)
block <- 20
system.time({
    for(i in seq(1, nrow(m), block)) 
        if ((block+i-1)<=nrow(m))
            m[i:(block+i-1),i:(block+i-1)] <- 0
})
# time in seconds
user  system elapsed 
 1.17    0.89    2.10 

> m[1:25, 1:25]

      # [,1] [,2] [,3] [,4] [,5]  [,6]  [,7]  [,8]  [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25]
 # [1,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43201 45361 47521 49681 51841
 # [2,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43202 45362 47522 49682 51842
 # [3,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43203 45363 47523 49683 51843
 # [4,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43204 45364 47524 49684 51844
 # [5,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43205 45365 47525 49685 51845
 # [6,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43206 45366 47526 49686 51846
 # [7,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43207 45367 47527 49687 51847
 # [8,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43208 45368 47528 49688 51848
 # [9,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43209 45369 47529 49689 51849
# [10,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43210 45370 47530 49690 51850
# [11,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43211 45371 47531 49691 51851
# [12,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43212 45372 47532 49692 51852
# [13,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43213 45373 47533 49693 51853
# [14,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43214 45374 47534 49694 51854
# [15,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43215 45375 47535 49695 51855
# [16,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43216 45376 47536 49696 51856
# [17,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43217 45377 47537 49697 51857
# [18,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43218 45378 47538 49698 51858
# [19,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43219 45379 47539 49699 51859
# [20,]    0    0    0    0    0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0 43220 45380 47540 49700 51860
# [21,]   21 2181 4341 6501 8661 10821 12981 15141 17301 19461 21621 23781 25941 28101 30261 32421 34581 36741 38901 41061     0     0     0     0     0
# [22,]   22 2182 4342 6502 8662 10822 12982 15142 17302 19462 21622 23782 25942 28102 30262 32422 34582 36742 38902 41062     0     0     0     0     0
# [23,]   23 2183 4343 6503 8663 10823 12983 15143 17303 19463 21623 23783 25943 28103 30263 32423 34583 36743 38903 41063     0     0     0     0     0
# [24,]   24 2184 4344 6504 8664 10824 12984 15144 17304 19464 21624 23784 25944 28104 30264 32424 34584 36744 38904 41064     0     0     0     0     0
# [25,]   25 2185 4345 6505 8665 10825 12985 15145 17305 19465 21625 23785 25945 28105 30265 32425 34585 36745 38905 41065     0     0     0     0     0
2
votes

Here is an alternative that uses matrix subsetting.

blockFill <- function(myMat, blockSize) {
  # get a list of the block groups
  blocks <- split(seq_len(ncol(myMat)),
                  rep(seq_len(ncol(myMat)), each=blockSize, length.out=ncol(myMat)))
  # fill in 0s with subsetting
  myMat[as.matrix(do.call(rbind, lapply(blocks, function(i) expand.grid(i, i))))] <- 0

  myMat
}

Here, split constructs the groups for the block using rep with each to control the block size and length.out to result in the correct size of the matrix. In the next line, lapply executes expand.grid over the block groups indicating the elements of the matrix to manipulate. These are combined with rbind and do.call.

As an example,

myMat <- matrix(1:16, 4)

blockFill(myMat, 2)
     [,1] [,2] [,3] [,4]
[1,]    0    0    9   13
[2,]    0    0   10   14
[3,]    3    7    0    0
[4,]    4    8    0    0
2
votes

Here is another option with bdiag

library(Matrix)
M*!bdiag(replicate(3, matrix(1, 3, 3), simplify = FALSE))
#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
# [1,]    0    0    0    4    5    6    7    8    9
# [2,]    0    0    0   14   15   16   17   18   19
# [3,]    0    0    0   24   25   26   27   28   29
# [4,]   31   32   33    0    0    0   37   38   39
# [5,]   41   42   43    0    0    0   47   48   49
# [6,]   51   52   53    0    0    0   57   58   59
# [7,]   61   62   63   64   65   66    0    0    0
# [8,]   71   72   73   74   75   76    0    0    0
# [9,]   81   82   83   84   85   86    0    0    0

data

M <- matrix(1:100, 10, 10, byrow=TRUE)[-10, -10]