How to get indices of non-diagonal elements of a numpy array?

How to get indices of non-diagonal elements of a numpy array?

a = np.array([[7412, 33, 2], [2, 7304, 83], [3, 101, 7237]]) 

I tried as follows:

diag_indices = np.diag_indices_from(a) print diag_indices (array([0, 1, 2], dtype=int64), array([0, 1, 2], dtype=int64)) 

After that, no idea... The expected result should be:

result = [[False, True, True], [True, False, True], [True, True, False]] 

4 Answers

To get the mask, you can use np.eye, like so -

~np.eye(a.shape[0],dtype=bool) 

To get the indices, add np.where -

np.where(~np.eye(a.shape[0],dtype=bool)) 

Sample run -

In [142]: a Out[142]: array([[7412, 33, 2], [ 2, 7304, 83], [ 3, 101, 7237]]) In [143]: ~np.eye(a.shape[0],dtype=bool) Out[143]: array([[False, True, True], [ True, False, True], [ True, True, False]], dtype=bool) In [144]: np.where(~np.eye(a.shape[0],dtype=bool)) Out[144]: (array([0, 0, 1, 1, 2, 2]), array([1, 2, 0, 2, 0, 1])) 

There are few more ways to get such a mask for a generic non-square input array.

With np.fill_diagonal -

out = np.ones(a.shape,dtype=bool) np.fill_diagonal(out,0) 

With broadcasting -

m,n = a.shape out = np.arange(m)[:,None] != np.arange(n) 
2
>>> import numpy as np >>> a = np.array([[7412, 33, 2], ... [2, 7304, 83], ... [3, 101, 7237]]) >>> non_diag = np.ones(shape=a.shape, dtype=bool) - np.identity(len(a)).astype(bool) >>> non_diag array([[False, True, True], [ True, False, True], [ True, True, False]], dtype=bool) 
2

As an additional idea to previous answers, you could select the indices of the upper and lower triangles:

a = np.array([[7412, 33, 2], [2, 7304, 83], [3, 101, 7237]]) # upper triangle. k=1 excludes the diagonal elements. xu, yu = np.triu_indices_from(a, k=1) # lower triangle xl, yl = np.tril_indices_from(a, k=-1) # Careful, here the offset is -1 # combine x = np.concatenate((xl, xu)) y = np.concatenate((yl, yu)) 

As described in the doc, you can then use those to index and assign values:

out = np.ones((3,3), dtype=bool) out[(x, y)] = False 

gives:

>>> out array([[ True, False, False], [False, True, False], [False, False, True]]) 

To extend @PlasmaBinturong's and @Divakar's answers, you could use advanced indexing based on np.triu_indices and np.tril_indices:

triu_idx = np.triu_indices(len(a), k=1) #finding advanced indices of upper right triangle tril_idx = np.tril_indices(len(a), k=-1) #finding advanced indices of lower left triangle out = np.ones(a.shape, dtype=bool) out[triu_idx] = False #padding upper right triangle with zeros out[tril_idx] = False #padding upper left triangle with zeros >>> out array([[ True, False, False], [False, True, False], [False, False, True]]) 
  • triu_idx = np.triu_indices(len(a), k=1) is a shorthand for np.nonzero(np.less.outer(np.arange(len(a)), np.arange(len(a))))

  • np.tril_indices(len(a), k=-1) is a shorthand for np.nonzero(np.greater.outer(np.arange(len(a)), np.arange(len(a))))

So instead of np.less.outer(...) & np.greater.outer(...) you could use:

>>> np.not_equal.outer(np.arange(len(a)), np.arange(len(a))) array([[False, True, True], [ True, False, True], [ True, True, False]]) 

which could be replaced by Syntactic Sugar in @Divakar's solution: np.arange(len(a))[:, None] != np.arange(len(a))

This is an output desired but besides that, let's plug it into a previous code to compare with the previous process:

out = np.ones(a.shape, dtype=bool) idx = np.not_equal.outer(np.arange(len(a)), np.arange(len(a))) # you need only this tri_both = np.nonzero(idx) out[tri_both] = False >>> out array([[False, True, True], [ True, False, True], [ True, True, False]]) 

Conclusion. Don't convert boolean indices to numerical ones and then back. This is inefficient as to compare with working on boolean indices straight.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like