Search code examples
rsumdataframediagonal

How to sum over diagonals of data frame


Say that I have this data frame:

     1   2   3   4      
100  8   12  5   14 
99   1   6   4   3   
98   2   5   4   11  
97   5   3   7   2   

In this above data frame, the values indicate counts of how many observations take on (100, 1), (99, 1), etc.

In my context, the diagonals have the same meanings:

     1   2   3   4
100  A   B   C   D 
99   B   C   D   E  
98   C   D   E   F 
97   D   E   F   G

How would I sum across the diagonals (i.e., sum the counts of the like letters) in the first data frame?

This would produce:

group  sum
A      8
B      13
C      13
D      28
E      10
F      18
G      2

For example, D is 5+5+4+14


Solution

  • You can use row() and col() to identify row/column relationships.

    m <- read.table(text="
        1   2   3   4      
    100  8   12  5   14 
    99   1   6   4   3   
    98   2   5   4   11  
    97   5   3   7   2")
    
    vals <- sapply(2:8,
           function(j) sum(m[row(m)+col(m)==j]))
    

    or (as suggested in comments by ?@thelatemail)

    vals <- sapply(split(as.matrix(m), row(m) + col(m)), sum)
    data.frame(group=LETTERS[seq_along(vals)],sum=vals)
    

    or (@Frank)

    data.frame(vals = tapply(as.matrix(m), 
           (LETTERS[row(m) + col(m)-1]), sum))
    

    as.matrix() is required to make split() work correctly ...