0

I have a numpy array created with np.zeros((10,10)). There are 1-s randomly in the array e.g.:

arr[2][4] = 1
arr[3][5] = 1
arr[4][6] = 1

resulting in:

[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

I am given a position in the array e.g.: arr[2][4]. How do I extract both diagonals(top left to bottom right and top right to bottom left) using Numpy methods, taking bounds into calculation. Here is a complete example of what I want to achive:

import numpy as np

arr = np.zeros((10,10))
arr[2][4] = 1
arr[3][5] = 1
arr[4][6] = 1

def extract_diags(arr, position):
   # some numpy magic here
   return diags

topl_bottomr_diag, topr_bottoml_diag = extract_diags(arr, (2,4))


print(topl_bottomr_diag)
>>> (0, 0, 1, 1, 1, 0, 0, 0)
print(topr_bottoml_diag)
>>> (0, 0, 1, 0, 0, 0, 0)

1 Answer 1

2
def extract_diags(arr, position):
   return np.diag(arr, position[1]-position[0]), np.diag(arr[::-1,:], position[1]+position[0]-len(arr)+1)

Example

M=np.arange(64).reshape(-1,8) # Easier to verify result than with almost all 0. 
#array([[ 0,  1,  2,  3,  4,  5,  6,  7],
#       [ 8,  9, 10, 11, 12, 13, 14, 15],
#       [16, 17, 18, 19, 20, 21, 22, 23],
#       [24, 25, 26, 27, 28, 29, 30, 31],
#       [32, 33, 34, 35, 36, 37, 38, 39],
#       [40, 41, 42, 43, 44, 45, 46, 47],
#       [48, 49, 50, 51, 52, 53, 54, 55],
#       [56, 57, 58, 59, 60, 61, 62, 63]])


extract_diags(M, (2,4)) # M[2,4]=20
# (array([ 2, 11, 20, 29, 38, 47]), array([48, 41, 34, 27, 20, 13,  6]))

Explanation

np.diag(M, offset) is the diagonal of matrix M, if offset=0, and with some shifting (positive over the diagonal, negative under) if specified. So, any M[i,i] is on the diagonal, hence offset=i-i=0. Any M[i,i+1] is on the diagonal just over, hence offset=i+1-i=1. Any M[i,i+k] is on a diagonal k places over the main one, hence offset=i+k-i=k. And generally speaking, offset for M[i,j] is j-i.

For the other diagonal, it is the same, with matrix M[::-1], taking into account that element at position [i,j] in M is at position [len(M)-1-i,j] in M[::-1]. Hence offset in M[::-1] of j-(len(M)-1-i) = j+i-len(M)+1

Sign up to request clarification or add additional context in comments.

9 Comments

Great answer! What if my matrix is really big, say 2000x2000 and I only want to extract a part of the matrix like 4 elements each diagonal way, taking bounds into consideration?
@Showertime 2000×2000 is not that big (well, for a matrix it is quite big, if you want to invert it, find its eigenvectors, or things like that. But for an array, as it is here... the diagonal we extract is, at most, 2000 elements). So that would not be really a computational problem at least. That being said, if, for some other reason you want to restrict the returned value, it is quite easy. Consider the fact that the element [i,j] is either the ith or the jth (whatever come first) of its diagonal (just draw it visually: the diagonal starts either at the top line, or at the leftest column)
@Showertime So, you know your element is at min(i,j) of the currently returned diagonal. If you want a window w=4 around that, just extract res[min(i,j)-w:min(i,j)+w+1]. And because w could be too big, clip it. So res[np.clip(min(i,j)-w,0,len(M)-1), np.clip(min(i,j)+w+1, 0, len(M)-1)]. With res being the 1st diagonal returned
@Showertime For the other diagonal, likewise, but in M[::-1]. And in M[::-1], element is at position [len(M)-i-1, j] in the flipped matrix. So at position min(len(M)-i-1, j) of its diagonal.
@Showertime You could wonder if that is optimal (to extract the whole diagonal, and then drop everything but a few values from it). But I think it is. Trying to pick up, one by one, in pure python the values is probably costier tha those numpy operations. If size could go to the millions, then, after a while O(n), even vectorized O(n) costs more than O(4) (cost of your partial extraction). But, and that is why I insist on the fact that, as an array dimension, 2000 is not that much: at 2000 we are probably not yet at the situation "numpy O(n) > pure python O(4)"
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.