1

I want to extract the anti-diagonals of an array

m=array(1:18,c(3,3,2))

My best shot

k=dim(m)[3]

mn=matrix(nrow = k, ncol = 3)

for (i in 1:k){
  mn=diag(m[,,i][3:1,1:3])
  }

This returns 12 14 16, the anti-diagonal of the second matrix in the array. I want to achieve this

[1] 3  5  7
[2] 12 14 16

I want the “anti-diags” as arrays

Manually diag(m[,,1][3:1,1:3]) and diag(m[,,2][3:1,1:3]) works fine, but the array I’m working with is dim(c(3,3,22)), so I thought "loop!"

MQ: How to extract the anti-diagonals from an array using the loop? (better and elegant solutions are more than welcome)

Adam
  • 434
  • 1
  • 4
  • 18

2 Answers2

2

This should work:

mn <- array(NA, dim=dim(m))
for (i in 1:dim(m)[3]){
   mn[,,i]=diag(m[,,i][cbind(3:1,1:3)])
              }

It was unclear whether you want the "anti-diag" to become the new diag, but that is what your code suggested as the intent. The form matrix[cbind(vec1,vec2)] pulls the (R,C) referenced elements from the matrix.

If you do not want them as arrays then this is an alternate result:

 mn <- array(NA, dim=c(2,3))
     for (i in 1:dim(m)[3]){
        mn[i,]=m[,,i][cbind(3:1,1:3)]
                            }
 mn
     [,1] [,2] [,3]
[1,]    3    5    7
[2,]   12   14   16

This is a loopless way of getting the same values:

 m[cbind( rep(3:1,2), rep(1:3,2), rep(1:2,each=3)) ]
[1]  3  5  7 12 14 16
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • Thank you @DWin. The second solution is what I'm looking for, I edited the question. Thanks for explaining the cbind in this case. – Adam Aug 21 '13 at 09:15
1

You could use lapply across the third dimension and extract the anti-diagonal by first rotating the matrix ( see this great answer ) by reversing the column order and taking the diagonal of that. Basically like this...

out <- lapply( 1:dim(m)[3] , function(x) diag( t( apply( m[,,x] , 2 , rev ) ) ) )
[[1]]
[1] 3 5 7

[[2]]
[1] 12 14 16

If you need them glued together as an array then use do.call...

do.call( rbind , out )
     [,1] [,2] [,3]
[1,]    3    5    7
[2,]   12   14   16

In this particular case, a for loop will be much quicker (benchmark it) and you should use @DWin's answer.

It occurs to me that we can simplfy this a bit and avoid using lists and bad use of lapply (by assuming thatm is available outside the scope of lapply) because we can also simply apply across the third dimension of your matrices. So we can apply once to rotate the matrices, then take the diag of each rotated matrix like so...

rotM <- apply( m , 2:3 , rev )    
out <- t( apply( rotM , 3 , diag ) )
     [,1] [,2] [,3]
[1,]    3    5    7
[2,]   12   14   16
Community
  • 1
  • 1
Simon O'Hanlon
  • 58,647
  • 14
  • 142
  • 184
  • @SimononO101, one word awesome – Adam Aug 21 '13 at 09:23
  • 1
    @Adam thanks. I added a second solution which I think is a lot neater. – Simon O'Hanlon Aug 21 '13 at 09:29
  • @SimononO101, Indeed DWin's quicker. But using lapply is awesome (for me) because I would not think of the ‘apply family’ in such cases – Adam Aug 21 '13 at 09:33
  • @Adam: The swap of `sapply` for `lapply` would probably make the output a matrix although it would likely be column-oriented so `t(sapply(...))` should give same output as `do.call(rbind, lapply(...))`. – IRTFM Aug 21 '13 at 18:15