Reputation: 790
I have to find the maximum value of a numpy array ignoring the diagonal elements.
np.amax() provides ways to find it ignoring specific axes. How can I achieve the same ignoring all the diagonal elements?
Upvotes: 16
Views: 13193
Reputation: 1344
Copying this comment as an answer:
a[~np.eye(*a.shape, dtype=bool)].min()
Upvotes: 0
Reputation:
If you came here looking for way to find the minimum values along an axis while ignoring the diagonal, then you could use numpy.where
to replace the diagonal with the array max and find min
along an axis:
row_mins = np.where(np.eye(*a.shape, dtype=bool), a.max(), a).min(axis=1)
For max
along axis change max
by min
and vice versa:
row_maxs = np.where(np.eye(*a.shape, dtype=bool), a.min(), a).max(axis=1)
Another option is to add the array max to the values on the diagonal and find min
along an axis (subtract the max for max
):
row_mins = (a+np.diag([a.max()]*len(a))).min(axis=1)
row_maxs = (a-np.diag([a.max()]*len(a))).max(axis=1)
Example:
For array:
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]])
The output of
out = np.where(np.eye(*a.shape, dtype=bool), a.max(), a).min(axis=1)
is
array([ 1, 5, 10, 15, 20])
Upvotes: 0
Reputation: 25508
Another possibility is to use NumPy's as_strided
to push the diagonal to the first column and then slice it off:
import numpy as np
from numpy.lib.stride_tricks import as_strided
b = np.arange(0,25,1).reshape((5,5))
n = b.shape[0]
out = np.max(as_strided(b, (n-1,n+1), (b.itemsize*(n+1), b.itemsize))[:,1:])
For b
, which looks like:
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]])
the above code produces
23
Where the argument to np.max
is the shifted view on b
:
In [7]: as_strided(b, (n-1,n+1), (b.itemsize*(n+1), b.itemsize))
Out[7]: 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]])
so that:
In [8]: as_strided(b, (n-1,n+1), (b.itemsize*(n+1), b.itemsize))[:,1:]
Out[8]: array([[ 1, 2, 3, 4, 5],
[ 7, 8, 9, 10, 11],
[13, 14, 15, 16, 17],
[19, 20, 21, 22, 23]])
Upvotes: 9
Reputation: 3363
This should work:
import numpy as np
import numpy.random
# create sample matrix
a = numpy.random.randint(10,size=(8,8))
a[0,0] = 100
which looks like
array([[100, 8, 6, 5, 5, 7, 4, 5],
[4, 6, 1, 7, 4, 5, 8, 5],
[0, 2, 0, 7, 4, 2, 7, 9],
[5, 7, 5, 9, 8, 3, 2, 8],
[2, 1, 3, 4, 0, 7, 8, 1],
[6, 6, 7, 6, 0, 6, 6, 8],
[6, 0, 1, 9, 7, 7, 9, 3],
[0, 5, 5, 5, 1, 5, 4, 4]])
# create mask
mask = np.ones((8,8))
mask = (mask - np.diag(np.ones(8))).astype(np.bool)
which looks like:
array([[False, True, True, True, True, True, True, True],
[ True, False, True, True, True, True, True, True],
[ True, True, False, True, True, True, True, True],
[ True, True, True, False, True, True, True, True],
[ True, True, True, True, False, True, True, True],
[ True, True, True, True, True, False, True, True],
[ True, True, True, True, True, True, False, True],
[ True, True, True, True, True, True, True, False]], dtype=bool)
Then
# calculate the maximum
out = np.amax(a[mask])
Output:
9
Upvotes: 4
Reputation: 3255
You could use a mask
mask = np.ones(a.shape, dtype=bool)
np.fill_diagonal(mask, 0)
max_value = a[mask].max()
where a
is the matrix you want to find the max of. The mask selects the off-diagonal elements, so a[mask]
will be a long vector of all the off-diagonal elements. Then you just take the max.
Or, if you don't mind modifying the original array
np.fill_diagonal(a, -np.inf)
max_value = a.max()
Of course, you can always make a copy and then do the above without modifying the original. Also, this is assuming that a
is some floating point format.
Upvotes: 31