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?
2 Answers
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

- 218,885
- 19
- 262
- 358
-
1this is a great answer and since it's the most efficient one it should be accepted as correct – pythonic833 Apr 14 '18 at 14:51
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)

- 3,054
- 1
- 12
- 27