Chad
Chad

Reputation: 24699

Set values along a diagonal in a matrix

I am trying to use the matrix() and diag() functions to create the following pattern, but with a 100 x 100 matrix rather than 5 x 5.

5 x 5 matrix:

| 0 1 0 0 0 |
| 1 0 1 0 0 |
| 0 1 0 1 0 |
| 0 0 1 0 1 |
| 0 0 0 1 0 |

In other words, I want to have two diagonals with values of 1, one to the left of the main diagonal, and one to the right of the main diagonal.

Upvotes: 3

Views: 317

Answers (3)

IRTFM
IRTFM

Reputation: 263499

The diag() function (actually the diag<- function) can be used for assignment:

mat <- matrix( 0, 100,100)
diag(mat) <- 1
mat[1:10,1:10]
#-----------
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    0    0    0    0    0    0    0    0     0
 [2,]    0    1    0    0    0    0    0    0    0     0
 [3,]    0    0    1    0    0    0    0    0    0     0
 [4,]    0    0    0    1    0    0    0    0    0     0
 [5,]    0    0    0    0    1    0    0    0    0     0
 [6,]    0    0    0    0    0    1    0    0    0     0
 [7,]    0    0    0    0    0    0    1    0    0     0
 [8,]    0    0    0    0    0    0    0    1    0     0
 [9,]    0    0    0    0    0    0    0    0    1     0
[10,]    0    0    0    0    0    0    0    0    0     1

You, however, want the sub-diagonal and super-diagonal to be assigned values, so use logical expressions with col and row:

mat <- matrix( 0, 100,100)
mat[row(mat)==col(mat)-1] <- 1 
mat[row(mat)==col(mat)+1] <- 1
mat[1:10,1:10]
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    1    0    0    0    0    0    0    0     0
 [2,]    1    0    1    0    0    0    0    0    0     0
 [3,]    0    1    0    1    0    0    0    0    0     0
 [4,]    0    0    1    0    1    0    0    0    0     0
 [5,]    0    0    0    1    0    1    0    0    0     0
 [6,]    0    0    0    0    1    0    1    0    0     0
 [7,]    0    0    0    0    0    1    0    1    0     0
 [8,]    0    0    0    0    0    0    1    0    1     0
 [9,]    0    0    0    0    0    0    0    1    0     1
[10,]    0    0    0    0    0    0    0    0    1     0

(This method does not depend on having a square matrix. I have a vague memory that there is a faster method that does not require using row and col. For very large objects each of those functions returns a matrix of the same dimensions as their arguments.)

Upvotes: 2

d.b
d.b

Reputation: 32558

For the main diagonal, the row and column indices are the same. For the other diagonals, there is a difference of 1 between the row index and column index. Generate those indices directly and assign values in those indices.

sz = 5
m = matrix(0, sz, sz)
inds1 = cbind(r = 1:(sz-1), c = 2:sz)
inds2 = cbind(r = 2:sz, c = 1:(sz-1))
m[inds1] = 1
m[inds2] = 1
m

# OR, to make it concise
m = matrix(0, sz, sz)
inds = rbind(cbind(1:(sz-1), 2:sz), cbind(2:sz, 1:(sz-1)))
replace(m, inds, 1)

#     [,1] [,2] [,3] [,4] [,5]
#[1,]    0    1    0    0    0
#[2,]    1    0    1    0    0
#[3,]    0    1    0    1    0
#[4,]    0    0    1    0    1
#[5,]    0    0    0    1    0

Upvotes: 2

Ronak Shah
Ronak Shah

Reputation: 389355

We could create a function using a math trick which would work for all square matrix.

get_off_diagonal_1s <- function(n) {
  #Create a matrix with all 0's
  mat <- matrix(0, ncol = n, nrow = n)
  #Subtract row indices by column indices
  inds = row(mat) - col(mat)
  #Replace values where inds is 1 or -1
  mat[inds == 1 | inds == -1] = 1
  mat
}

get_off_diagonal_1s(5)
#     [,1] [,2] [,3] [,4] [,5]
#[1,]    0    1    0    0    0
#[2,]    1    0    1    0    0
#[3,]    0    1    0    1    0
#[4,]    0    0    1    0    1
#[5,]    0    0    0    1    0

get_off_diagonal_1s(8)
#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
#[1,]    0    1    0    0    0    0    0    0
#[2,]    1    0    1    0    0    0    0    0
#[3,]    0    1    0    1    0    0    0    0
#[4,]    0    0    1    0    1    0    0    0
#[5,]    0    0    0    1    0    1    0    0
#[6,]    0    0    0    0    1    0    1    0
#[7,]    0    0    0    0    0    1    0    1
#[8,]    0    0    0    0    0    0    1    0

Upvotes: 1

Related Questions