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.
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
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With