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?
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.
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]])
m nxn arrays: np.max(as_strided(c, (m,n-1,n+1), (c.itemsize*n*n, c.itemsize*(n+1), c.itemsize))[:,:,1:], axis=(1,2)): for a better answer, you should ask a new question I think.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
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])