1

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.

Mkty
  • 21
  • 3

2 Answers2

2

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))
d.b
  • 32,245
  • 6
  • 36
  • 77
  • Can you please suggest a more efficient or faster way to do so since its still quadratic to the number of elements of matrix?Can we take advantage of the fact that mostly the matrix will be sparse with off diagonal elements near main diagonal being dense.Thanks in advance! – Mkty Apr 01 '17 at 16:16
  • Also we can obtain the vector of non zero elements like (1,2,3,7,6,3,7,1,4 and so on) in the given matrix. – Mkty Apr 01 '17 at 16:23
  • @Mkty, for the second question, one of `which(t(m) != 0)` or `which(m != 0)` should work. – d.b Apr 02 '17 at 13:42
0

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
user20650
  • 24,654
  • 5
  • 56
  • 91
  • This answer is v. similar to the answer [here](http://stackoverflow.com/questions/43173211/faster-alternative-to-compute-colcumsums-of-a-band-matrix/43185310#43185310) but added as the question is phrased quite differently. – user20650 Apr 03 '17 at 16:02