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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.