3

In Numpy, argmax is already defined, but I need argsecondmax, which is basically the second maxima. How can I do this, I'm a bit confused?

S.Mandal
  • 171
  • 2
  • 8

2 Answers2

4

Finding Nth largest indices

An efficient one could use np.argparition that skips sorting and simply parititions, which when sliced would give us the required indices. We would also generalize it to find Nth largest one along a specified axis or a global one (similar to ndarray.argmax()), like so -

def argNmax(a, N, axis=None):
    if axis is None:
        return np.argpartition(a.ravel(), -N)[-N]
    else:
        return np.take(np.argpartition(a, -N, axis=axis), -N, axis=axis)

Sample runs -

In [66]: a
Out[66]: 
array([[908, 770, 258, 534],
       [399, 376, 808, 750],
       [655, 654, 825, 355]])

In [67]: argNmax(a, N=2, axis=0)
Out[67]: array([2, 2, 1, 0])

In [68]: argNmax(a, N=2, axis=1)
Out[68]: array([1, 3, 0])

In [69]: argNmax(a, N=2) # global second largest index
Out[69]: 10

Finding Nth smallest indices

Extending this to find the Nth smallest one along an axis or globally, we would have -

def argNmin(a, N, axis=None):
    if axis is None:
        return np.argpartition(a.ravel(), N-1)[N-1]
    else:
        return np.take(np.argpartition(a, N-1, axis=axis), N-1, axis=axis)

Sample runs -

In [105]: a
Out[105]: 
array([[908, 770, 258, 534],
       [399, 376, 808, 750],
       [655, 654, 825, 355]])

In [106]: argNmin(a, N=2, axis=0)
Out[106]: array([2, 2, 1, 0])

In [107]: argNmin(a, N=2, axis=1)
Out[107]: array([3, 0, 1])

In [108]: argNmin(a, N=2)
Out[108]: 11

Timings

To give a perspective on the benefit on using argpartition over actual sorting with argsort as shown in @pythonic833's post, here's a quick runtime test on the global argmax version -

In [70]: a = np.random.randint(0,99999,(1000,1000))

In [72]: %timeit np.argsort(a)[-2] # @pythonic833's soln
10 loops, best of 3: 40.6 ms per loop

In [73]: %timeit argNmax(a, N=2)
100 loops, best of 3: 2.12 ms per loop
Divakar
  • 218,885
  • 19
  • 262
  • 358
3

you can use np.argsort to do so

test =np.random.randint(1,5,10)

Output

array([3, 2, 3, 1, 4, 1, 3, 1, 3, 3])

to get the second maximum we do

test[np.argsort(test)[-2]]

np.argsort sorts in ascending order, so for the maximum we take the last value, for the second maximum take the second last.

Edit: In order to improve this answer I write a function similar to the one Divakar (https://stackoverflow.com/a/49832435/9534390) provided.

def argNmax(a, N, axis=None):
    return np.take(np.argsort(a, axis=axis), -N)
pythonic833
  • 3,054
  • 1
  • 12
  • 27