5

I have a flat array b:

a = numpy.array([0, 1, 1, 2, 3, 1, 2])

And an array c of indices marking the start of each "chunk":

b = numpy.array([0, 4])

I know I can find the maximum in each "chunk" using a reduction:

m = numpy.maximum.reduceat(a,b)
>>> array([2, 3], dtype=int32)

But... Is there a way to find the index of the maximum <edit>within a chunk</edit> (like numpy.argmax), with vectorized operations (no lists, loops)?

4
  • Deleted my question temporarily because I thought I had an answer: numpy.argmax(numpy.equal.outer(m,a), axis=1), but that doesn't work for examples where the same max occurs in many places... Commented Jan 24, 2017 at 17:07
  • For instance on this array: a = numpy.array([0, 1, 1, 3, 3, 1, 2]), where the same maximum 3 occurs in the two chunks. Commented Jan 24, 2017 at 17:13
  • The problem is that np.maximum is a ufunc with reduceat - which effectively iterates through the array, comparing 2 values at a time. But np.max and np.argmax are functions that operate on the whole array at once. They aren't ufunc. Commented Jan 24, 2017 at 17:27
  • @hpaulj, yes, I'm aware of that. I'm asking if anyone can think of a workaround with the same behaviour. Commented Jan 24, 2017 at 17:29

1 Answer 1

3

Borrowing the idea from this post.

Steps involved :

  • Offset all elements in a group by a limit-offset. Sort them globally, thus limiting each group to stay at their positions, but sorting the elements within each group.

  • In the sorted array, we would look for the last element, which would be the group max. Their indices would be the argmax after offsetting down for the group lengths.

Thus, a vectorized implementation would be -

def numpy_argmax_reduceat(a, b):
    n = a.max()+1  # limit-offset
    grp_count = np.append(b[1:] - b[:-1], a.size - b[-1])
    shift = n*np.repeat(np.arange(grp_count.size), grp_count)
    sortidx = (a+shift).argsort()
    grp_shifted_argmax = np.append(b[1:],a.size)-1
    return sortidx[grp_shifted_argmax] - b

As a minor tweak and possibly faster one, we could alternatively create shift with cumsum and thus have a variation of the earlier approach, like so -

def numpy_argmax_reduceat_v2(a, b):
    n = a.max()+1  # limit-offset
    id_arr = np.zeros(a.size,dtype=int)
    id_arr[b[1:]] = 1
    shift = n*id_arr.cumsum()
    sortidx = (a+shift).argsort()
    grp_shifted_argmax = np.append(b[1:],a.size)-1
    return sortidx[grp_shifted_argmax] - b
Sign up to request clarification or add additional context in comments.

2 Comments

Both solutions work out nicely in my case, because I already have shift from an earlier operation. Nice answer.
Hey you have answered my questions a few months ago could you take a look at this post: stackoverflow.com/questions/67680199/…. it is related to this question.

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.