130

I'm trying to get the indices of the maximum element in a Numpy array. This can be done using numpy.argmax. My problem is, that I would like to find the biggest element in the whole array and get the indices of that.

numpy.argmax can be either applied along one axis, which is not what I want, or on the flattened array, which is kind of what I want.

My problem is that using numpy.argmax with axis=None returns the flat index when I want the multi-dimensional index.

I could use divmod to get a non-flat index but this feels ugly. Is there any better way of doing this?

Seanny123
  • 8,776
  • 13
  • 68
  • 124
Andreas Mueller
  • 27,470
  • 8
  • 62
  • 74

3 Answers3

226

You could use numpy.unravel_index() on the result of numpy.argmax():

>>> a = numpy.random.random((10, 10))
>>> numpy.unravel_index(a.argmax(), a.shape)
(6, 7)
>>> a[6, 7] == a.max()
True
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • This seems to fail when array is masked like `a[a>3].argmax` – majkrzak May 30 '23 at 12:27
  • see: https://colab.research.google.com/drive/1RLJ5KowcCLZL4u_8WeGxPg8ijzF2iKXq?usp=sharing – majkrzak May 30 '23 at 12:34
  • 1
    @majkrzak Advanced indexing like `a[a > 3]` does not create a _masked array_. It creates a flat, one-dimensional _copy_ of the data containining only the matching elements. The return value of `argmax()` applies to that new array, but you can't use it as an index into `a`. – Sven Marnach May 30 '23 at 12:39
  • it's not a real copy as modification affects the original array. But it probably may be suitable for a separated question – majkrzak May 30 '23 at 12:42
  • @majkrzak It is a real copy, and modification does not affect the original. Try `b = a[a > 3]`, and then modifying `b`. Changes won't be reflected in `a`. You are probably thinking of things like `a[a > 3] += 1` or similar, which uses `__setitem__()` instead of `__getitem__()`, so is irrelevant for this case. – Sven Marnach May 30 '23 at 13:03
  • exactly, thats why I wouln't call a[a>5] a real copy – majkrzak May 30 '23 at 13:04
  • 1
    @majkrzak `a[a > 3]` in _expression context_ is as real a copy as it gets. The fact that you can use the same string of characters as an assignment target may be confusing, but it doesn't change that `a[a > 3].argmax()` runs on a "real" copy of the data, since we have `a[a > 3]` in expression context and not as an assignment target. (Don't blame me for Python's data model.) – Sven Marnach May 30 '23 at 13:06
27
np.where(a==a.max())

returns coordinates of the maximum element(s), but has to parse the array twice.

>>> a = np.array(((3,4,5),(0,1,2)))
>>> np.where(a==a.max())
(array([0]), array([2]))

This, comparing to argmax, returns coordinates of all elements equal to the maximum. argmax returns just one of them (np.ones(5).argmax() returns 0).

eumiro
  • 207,213
  • 34
  • 299
  • 261
  • 10
    This will iterate the array three times, not only twice. One time to find the maximum, a second time to build the result of `==`, and a third time to extract the `True` values from this result. Note that there might be more than one item equal to the maximum. – Sven Marnach Feb 28 '12 at 14:40
1

To get the non-flat index of all occurrences of the maximum value, you can modify eumiro's answer slightly by using argwhere instead of where:

np.argwhere(a==a.max())

>>> a = np.array([[1,2,4],[4,3,4]])
>>> np.argwhere(a==a.max())
array([[0, 2],
       [1, 0],
       [1, 2]])
  • It's not effective since you get three passes and a matrix creation. Imagine we've got 9000x7000 image (A3@600dpi) - would you still insist on your solution? – Maksym Ganenko Nov 20 '18 at 10:14