[R] Summing up diagonals w/o for-loop
Marc Schwartz
marc_schwartz at comcast.net
Tue Feb 26 22:19:32 CET 2008
Camarda, Carlo Giovanni wrote:
> Dear R-users,
>
> is there any way to sum up the elements of the "diagonals" of a matrix
> without using a for-loop? While there is a simple way over rows and
> columns, I don't see a straightforward multiplication for the diagonals,
> am I too demanding? Or, more likely, I'm lack some algebra trick? Is
> there any R-function that can deal with this problem w/o loop?
>
> Actually I would need to sum up just the upper diagonals.
>
> Here a simple, but general, example for presenting the problem.
>
> Thanks in advance for any help,
> Carlo Giovanni Camarda
>
> m<- 7
> n<- 5
> mat<- matrix(1:35, n, m)
> ones.r<- rep(1,n)
> ones.c<- rep(1,m)
> # sum over the rows
> sum.r<- mat%*%ones.c
> # sum over the cols
> sum.c<- t(mat)%*%ones.r
> # sum over the diags
> sum.d<- numeric(m+n-1)
> sum.d[1]<- mat[n,1]
> sum.d[m+n-1]<- mat[1,m]
> for(i in 2:n){
> sum.d[i]<- sum(diag(mat[(n+1-i):n,1:i]))
> }
> for(i in 2:(m-1)){
> sum.d[i+n-1]<- sum(diag(mat[,i:m]))
> }
>
If we have 'mat':
> mat
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 1 6 11 16 21 26 31
[2,] 2 7 12 17 22 27 32
[3,] 3 8 13 18 23 28 33
[4,] 4 9 14 19 24 29 34
[5,] 5 10 15 20 25 30 35
You can use:
> rowSums(mat)
[1] 112 119 126 133 140
and:
> colSums(mat)
[1] 15 40 65 90 115 140 165
for your initial steps, rather than the matrix multiplication. See
?colSums for more information.
There may be a better way than this, but one approach for the diagonals
in the order you want is to split() the matrix into it's constituent
diagonals based upon subsetting using the row() and col() values. This
yields a list:
> split(mat, col(mat) - row(mat))
$`-4`
[1] 5
$`-3`
[1] 4 10
$`-2`
[1] 3 9 15
$`-1`
[1] 2 8 14 20
$`0`
[1] 1 7 13 19 25
$`1`
[1] 6 12 18 24 30
$`2`
[1] 11 17 23 29 35
$`3`
[1] 16 22 28 34
$`4`
[1] 21 27 33
$`5`
[1] 26 32
$`6`
[1] 31
Then we can use sapply() to sum() over the list elements:
> sum.d
[1] 5 14 27 44 65 90 115 100 81 58 31
> sapply(split(mat, col(mat) - row(mat)), sum)
-4 -3 -2 -1 0 1 2 3 4 5 6
5 14 27 44 65 90 115 100 81 58 31
If you want to sum the diagonals "the other way" replace the '-' in the
second argument in split() with a '+'.
See ?split, ?sapply, ?row and ?col for more information.
HTH,
Marc Schwartz
More information about the R-help
mailing list