1
votes

I'm trying to figure out how to create a new matrix with all the diagonals arranged column wise.For example say I have the following matrix

0 1 2 7 0 0 0 0
0 0 3 6 7 0 0 0
0 0 0 3 1 7 0 0
0 0 0 0 4 4 7 0
0 0 0 0 0 5 8 7
0 0 0 0 0 0 1 8
0 0 0 0 0 0 0 4
0 0 0 0 0 0 0 0

Extracting off diagonals we get,

1 3 3 4 5 1 4
2 6 1 4 8 8
7 7 7 7 7    

Now,I am searching for efficient solution in R to arrange these diagonal vectors such that the resulting matrix is

1 2 7  
3 6 7 
3 1 7 
4 4 7 
5 8 7 
1 8 0
4 0 0
0 0 0

Also,to achieve the reverse form i.e. smallest diagonal first like this

0 0 0
0 0 1
0 2 3
7 6 3
7 1 4
7 4 5
7 8 1
7 8 4

I have tried using for loop,but that solution is not computationally efficient,since the matrix can be large(10^3) I feel the efficient solution will be ridiculously simple, but I am unable to figure it out.

2
How did you "extract off" the diagonals? - yeedle
by selecting all non zero diagonals using this answer stackoverflow.com/a/27935808/5273845 - Mkty
how do you determine which one is non-zero? - yeedle

2 Answers

2
votes

You can subset the matrix successively and extract the diagonal elements

sapply(0:2, function(i)
            diag(m[-(nrow(m):(nrow(m)-i)), -(1:(1+i))])[1:nrow(m)] )
#     [,1] [,2] [,3]
#[1,]    1    2    7
#[2,]    3    6    7
#[3,]    3    1    7
#[4,]    4    4    7
#[5,]    5    8    7
#[6,]    1    8   NA
#[7,]    4   NA   NA
#[8,]   NA   NA   NA

OR

m2 = t(m)[which(t(m) != 0)]
m2 = append(m2, m2[length(m2)])
m2[length(m2) - 1] = NA
m2[(length(m2)+1):(NROW(m)*3)] = NA
matrix(m2, ncol = 3, byrow = TRUE)
#     [,1] [,2] [,3]
#[1,]    1    2    7
#[2,]    3    6    7
#[3,]    3    1    7
#[4,]    4    4    7
#[5,]    5    8    7
#[6,]    1    8   NA
#[7,]    4   NA   NA
#[8,]   NA   NA   NA

DATA

m = structure(c(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 
3, 0, 0, 0, 0, 0, 0, 7, 6, 3, 0, 0, 0, 0, 0, 0, 7, 1, 4, 0, 0, 
0, 0, 0, 0, 7, 4, 5, 0, 0, 0, 0, 0, 0, 7, 8, 1, 0, 0, 0, 0, 0, 
0, 7, 8, 4, 0), .Dim = c(8L, 8L))
0
votes

I think the easiest approach is to use the sparseMatrix format, where the non-zero entries are defined in the object by their row (i), and column (j) position, and the values (x) that they take. From this it is easy to from a column matrix from the diagonals. A for loop is also fast(er) here, but perhaps slightly harder to generalise.

Data

m = structure(c(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 
3, 0, 0, 0, 0, 0, 0, 7, 6, 3, 0, 0, 0, 0, 0, 0, 7, 1, 4, 0, 0, 
0, 0, 0, 0, 7, 4, 5, 0, 0, 0, 0, 0, 0, 7, 8, 1, 0, 0, 0, 0, 0, 
0, 7, 8, 4, 0), .Dim = c(8L, 8L))  

Arrange matrix

b = 3  # Set width of band
n = nrow(m)

library(Matrix)

sm <- as(m, "TsparseMatrix") 

sparseMatrix(i = sm@i+1,
             j = sm@j - sm@i,
             x = sm@x,
             dims = c(nrow(sm),b))
#8 x 3 sparse Matrix of class "dgCMatrix"

#[1,] 1 2 7
#[2,] 3 6 7
#[3,] 3 1 7
#[4,] 4 4 7
#[5,] 5 8 7
#[6,] 1 8 .
#[7,] 4 . .
#[8,] . . .

Using a for loop to loop through each diagonal will be faster but I think the sparseMatrix approach is a bit more general

 out = matrix(0, n, b)
 for(i in 1:b) {
        ro = 1:(n-i)
        co = (1+i):n
        out[ro, i] = m[cbind(ro, co)]
  }
  out
#     [,1] [,2] [,3]
#[1,]    1    2    7
#[2,]    3    6    7
#[3,]    3    1    7
#[4,]    4    4    7
#[5,]    5    8    7
#[6,]    1    8    0
#[7,]    4    0    0
#[8,]    0    0    0