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) 2As 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 fornp.nonzero(np.less.outer(np.arange(len(a)), np.arange(len(a))))np.tril_indices(len(a), k=-1)is a shorthand fornp.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.