12

I need to access and assign single slots of an m*n matrix inside a for loop. The code so far:

rowCount <- 9
similMatrix = matrix(nrow = rowCount - 1, ncol = rowCount)
show(similMatrix)
for(i in (rowCount - 1)){
  for (j in rowCount)
    if (i == j){
      similMatrix[i == j] <- 0;
    }
}
show(similMatrix)

so if i = j the NA value in the matrix needs to be replaced with 0.

Matthew Lundberg
  • 42,009
  • 6
  • 90
  • 112
DI MI
  • 245
  • 2
  • 5
  • 16

2 Answers2

40

You want the function diag<-

m <- matrix(1:12, nrow=3)
m
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

diag(m) <- 0
m
     [,1] [,2] [,3] [,4]
[1,]    0    4    7   10
[2,]    2    0    8   11
[3,]    3    6    0   12
Matthew Lundberg
  • 42,009
  • 6
  • 90
  • 112
14

For the purpose of setting the "diagonal" elements to zero you have already been given an answer but I wonder if you were hoping for something more general. The reasons for lack of success with that code were two-fold: the construction of your indices were flawed and the indexing was wrong. This would have succeeded:

for(i in 1:(rowCount - 1)){  # need an expression that retruns a sequence
  for (j in 1:rowCount)      # ditto
    if (i == j){
      similMatrix[i,j] <- 0;  # need to index the matrix with two element if using i,j
    }
}
#----------
> show(similMatrix)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]    0   NA   NA   NA   NA   NA   NA   NA   NA
[2,]   NA    0   NA   NA   NA   NA   NA   NA   NA
[3,]   NA   NA    0   NA   NA   NA   NA   NA   NA
[4,]   NA   NA   NA    0   NA   NA   NA   NA   NA
[5,]   NA   NA   NA   NA    0   NA   NA   NA   NA
[6,]   NA   NA   NA   NA   NA    0   NA   NA   NA
[7,]   NA   NA   NA   NA   NA   NA    0   NA   NA
[8,]   NA   NA   NA   NA   NA   NA   NA    0   NA

But resorting to loops in R is generally considered a last resort (sometimes for the wrong reasons.) There is a much more compact way of doing the same "loop" operation and it generalizes more widely than just setting the diagonal.

similMatrix[ row(similMatrix) == col(similMatrix) ] <- 0
> similMatrix
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]    0   NA   NA   NA   NA   NA   NA   NA   NA
[2,]   NA    0   NA   NA   NA   NA   NA   NA   NA
[3,]   NA   NA    0   NA   NA   NA   NA   NA   NA
[4,]   NA   NA   NA    0   NA   NA   NA   NA   NA
[5,]   NA   NA   NA   NA    0   NA   NA   NA   NA
[6,]   NA   NA   NA   NA   NA    0   NA   NA   NA
[7,]   NA   NA   NA   NA   NA   NA    0   NA   NA
[8,]   NA   NA   NA   NA   NA   NA   NA    0   NA

If you wanted to set the subdiagonal to zero you could just use:

similMatrix[ row(similMatrix)-1 == col(similMatrix) ] <- 0

You can avoid generating the extra row and col matrices using this:

 mind <- min( dim(similMatrix) )
 # avoid going outside dimensions if not symmetric
 similMatrix[ cbind( seq(maxd),seq(maxd) ) <- 0
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • 1
    I imagine `row(mat)` and `col(mat)` will generate two matrices, wouldn't this mean that we are tripling memory consumption here? If `mat` happened to be big, I guess this can cause problems. – qed Jun 11 '14 at 19:06
  • Quite right. I didn't see any warnings that these were big matrices. The "compactness" I was offering was in expression, but as you point out this may not be compact in memory footprint. One can imagine using `similMatrix[cbind(1:(rowCount - 1), 1:(rowCount - 1)] <- 0` and I'm guessing it would perform in a superior fashion for both memory and speed. I'm thinking that might have an even lower footprint that the `mat[diag(mat)] <-0` approach. – IRTFM Jun 11 '14 at 19:13