4

Suppose I have an array a of shape (2, 2, 2):

a = np.array([[[7, 9],
               [19, 18]],
              [[24, 5],
               [18, 11]]])

and an array b that is the max of a: b=a.max(-1) (row-wise):

b = np.array([[9, 19],
              [24, 18]])

I'd like to obtain the index of elements in b using index in flattened a, i.e. a.reshape(-1):

array([ 7,  9, 19, 18, 24,  5, 18, 11])

The result should be an array that is the same shape with b with indices of b in flattened a:

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

Basically this is the result of maxpool2d when return_indices= True in pytorch, but I'm looking for an implementation in numpy. I've used where but it seems doesn't work, also is it possible to combine finding max and indices in one go, to be more efficient? Thanks for any help!

1
  • since the number 18 is repeated twice in a, which index should be returned? Last occurrence of 18? Commented Jul 29, 2022 at 4:12

3 Answers 3

1

I have a solution similar to that of Andras based on np.argmax and np.arange. Instead of "indexing the index" I propose to add a piecewise offset to the result of np.argmax:

import numpy as np
a = np.array([[[7, 9],
               [19, 18]],
              [[24, 5],
               [18, 11]]])
off = np.arange(0, a.size, a.shape[2]).reshape(a.shape[0], a.shape[1])
>>> off
array([[0, 2],
       [4, 6]])

This results in:

>>> a.argmax(-1) + off
array([[1, 2],
       [4, 6]])

Or as a one-liner:

>>> a.argmax(-1) + np.arange(0, a.size, a.shape[2]).reshape(a.shape[0], a.shape[1])
array([[1, 2],
       [4, 6]])
Sign up to request clarification or add additional context in comments.

2 Comments

wow thanks @Per Joachims this works equally as well! Since you are a "New contributor", I think it would be better to accept your answer lol. Could you please explain a bit on the offset? I don't really get it... Thanks!!
I think I understand it: so basically off gives the index of the first element in each row, then plus the argmax will give the index of the max element in that row. This is simple, Thanks again
1

The only solution I could think of right now is generating a 2d (or 3d, see below) range that indexes your flat array, and indexing into that with the maximum indices that define b (i.e. a.argmax(-1)):

import numpy as np

a = np.array([[[ 7,  9],
               [19, 18]],
              [[24,  5],
               [18, 11]]])
multi_inds = a.argmax(-1)
b_shape = a.shape[:-1]
b_size = np.prod(b_shape)
flat_inds = np.arange(a.size).reshape(b_size, -1)
flat_max_inds = flat_inds[range(b_size), multi_inds.ravel()]
max_inds = flat_max_inds.reshape(b_shape)

I separated the steps with some meaningful variable names, which should hopefully explain what's going on.

multi_inds tells you which "column" to choose in each "row" in a to get the maximum:

>>> multi_inds
array([[1, 0],
       [0, 0]])

flat_inds is a list of indices, from which one value is to be chosen in each row:

>>> flat_inds
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])

This is indexed into exactly according to the maximum indices in each row. flat_max_inds are the values you're looking for, but in a flat array:

>>> flat_max_inds
array([1, 2, 4, 6])

So we need to reshape that back to match b.shape:

>>> max_inds
array([[1, 2],
       [4, 6]])

A slightly more obscure but also more elegant solution is to use a 3d index array and use broadcasted indexing into it:

import numpy as np

a = np.array([[[ 7,  9],
               [19, 18]],
              [[24,  5],
               [18, 11]]])
multi_inds = a.argmax(-1)
i, j = np.indices(a.shape[:-1])
max_inds = np.arange(a.size).reshape(a.shape)[i, j, multi_inds]

This does the same thing without an intermediate flattening into 2d.

The last part is also how you can get b from multi_inds, i.e. without having to call a *max function a second time:

b = a[i, j, multi_inds]

8 Comments

wow thanks Andras. this looks quite complex. Do you know any numpy functions that returns both max value and indices? argmax basically find max again right?
@Sam-gege my updated answer is less complex, but also harder to figure out. You can just call argmax and query the values, the same way that I did with the range in the updated answer. b = a[i, j, multi_inds] should suffice. (I've updated my answer with that too.)
cool thanks for the help! I need to try this out
thanks for your updated answer. I think I need to call max at least once in maxpool, but I also want to get its indices so that during backpropagation, I can put gradient back to the max position. i wonder since numpy array are contiguous, it shouldn't be very hard for max() function to also returns an index according to flatten input, but sadly it doesn't .Thanks again for your help!!
yep you are right I forgot that, so I only need to call argmax once. after I have those indices, I can simply call a.reshape(-1)[np.array([[1, 2],[4, 6]])] to get those max values. basically I don't have to call max, then argmax
|
0

This is a long one-liner

new = np.array([np.where(a.reshape(-1)==x)[0][0] for x in a.max(-1).reshape(-1)]).reshape(2,2)
print(new)
array([[1, 2],
       [4, 3]])

However number = 18 is repeated twice; So which index is the target.

1 Comment

Thanks for your answer damir. However, this is incorrect, as the index of the second 18 should be returned, as that 18 is the max in its row (18,11), not the first 18 at row (19,18)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.