Erdnase
Erdnase

Reputation: 790

Minimum of Numpy Array Ignoring Diagonal

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

Answers (5)

goweon
goweon

Reputation: 1344

Copying this comment as an answer:

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

Upvotes: 0

user7864386
user7864386

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

xnx
xnx

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

plonser
plonser

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

hunse
hunse

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

Related Questions