[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