28

I'm trying to figure out how to get all the diagonals of a matrix. For example, say I have the following matrix: A <- matrix(1:16,4)

using the diag(A) function will return

[1] 1 6 11 16 

In addition to the primary diagonal, I would like a list of all the diagonals above and below it.

5 10 15 2 7 12 9 14 3 8 4 13 

I found the following link https://stackoverflow.com/a/13049722 which gives me the diagonals directly above and below the primary one, however I cannot seem to figure out how to extend the code to get the rest of them for any size matrix. I tried two nested for loops since it appears that some kind of incrementing of the matrix subscripts would produce the result I am looking for. I tried using ncol(A), nrow(A) in the for loops, but couldn't seem to figure out the right combination. Plus I am aware that for loops are generally frowned upon in R.

The code given was:

diag(A[-4,-1]) diag(A[-1,-4]) 

which returned the two diagonals, both upper and lower

Of course this is a square matrix and not all of the matrices I want to perform this on will be square. Filling in the non-square area with NAs would be acceptable if necessary. The answer I need may be in one of the other answers on the page, but the original question involved means, sums, etc. which added a layer of complexity beyond what I am trying to do. I have a feeling the solution to this will be ridiculously simple, but it just isn't occurring to me. I'm also surprised I was not able to find this question anywhere on SO, it would seem to be a common enough question. Maybe I don't know the proper terminology for this problem.

4
  • 1
    Why aren't you looking at @GavinSimpson's answer there? Commented Jan 14, 2015 at 3:55
  • I did read that answer as well which is why I understand the solution involves incrementing based on the number of rows and columns, however how to package this up as a function that returns all the diagonals in one matrix is what is evading me here. I read all the answers repeatedly. Commented Jan 14, 2015 at 4:02
  • 6
    Side note: for loops are not frowned upon. That's a myth (IMO). They're all over the source code. They just need to be used appropriately, like any other function. :-) Commented Jan 14, 2015 at 4:37
  • 3
    @RichardScriven - it's not a myth that for loops are frowned upon. It is however a myth that all that frowning necessarily has any impact on the speed of the code. Commented Jan 14, 2015 at 5:36

3 Answers 3

68
A <- matrix(1:16, 4) # create an indicator for all diagonals in the matrix d <- row(A) - col(A) # use split to group on these values split(A, d) # # $`-3` # [1] 13 # # $`-2` # [1] 9 14 # # $`-1` # [1] 5 10 15 # # $`0` # [1] 1 6 11 16 # # $`1` # [1] 2 7 12 # # $`2` # [1] 3 8 # # $`3` # [1] 4 
Sign up to request clarification or add additional context in comments.

1 Comment

and can be changed for reverse diagonals row(A) + col(A)
6

Since you're dealing with square matrices, it should be really easy to convert Gavin's answer into a small function that first calculates the range that should be used as the offset values. Here's such a function:

AllDiags <- function(inmat, sorted = TRUE) { Range <- ncol(inmat) - 1 Range <- -Range:Range if (isTRUE(sorted)) Range <- Range[order(abs(Range))] lapply(Range, function(x) { inmat[row(inmat) == (col(inmat) - x)] }) } 

Here's the output on your sample matrix "A".

AllDiags(A) # [[1]] # [1] 1 6 11 16 # # [[2]] # [1] 2 7 12 # # [[3]] # [1] 5 10 15 # # [[4]] # [1] 3 8 # # [[5]] # [1] 9 14 # # [[6]] # [1] 4 # # [[7]] # [1] 13 

Comments

3

Here is one solution based on an observation that you can get all the diagonals by shrinking and expanding the matrix. That is first consider row N col 1 (get diag of that) then rows (N-1): and cols (1:2). Get diagonal of that. etc..

N <- ncol(A) rows <- cbind(c(N:1, rep(1,N-1)), c(rep(N,N), (N-1):1)) # row indeces cols <- apply(rows, 2, rev) # col indeces diagMatSubset <- function(mat, i1, i2, j1, j2) diag(mat[i1:i2, j1:j2, drop=FALSE]) Map(diagMatSubset, list(A), rows[,1], rows[,2], cols[,1], cols[,2]) [[1]] [1] 4 [[2]] [1] 3 8 [[3]] [1] 2 7 12 [[4]] [1] 1 6 11 16 [[5]] [1] 5 10 15 [[6]] [1] 9 14 [[7]] [1] 13 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.