orange
orange

Reputation: 8090

Get first index in array where relation is true

How can I get the last index of the element in a where b > a when a and b have different length using numpy.

For instance, for the following values:

>>> a = np.asarray([10, 20, 30, 40])
>>> b = np.asarray([12, 25])

I would expect a result of [0, 1] (0.. because 12 > 10 -> index 0 in a; 1.. because 25 > 20 -> index 1 in a). Obviously, the length of the result vector should equal the length of b (and the values of the result list should be less than the length of a (as they refer to the indices in a)).

Another test is for b = np.asarray([12, 25, 31, 9, 99]) (same a as above), the result should be array([ 0, 1, 2, -1, 3]).

Upvotes: 0

Views: 95

Answers (4)

Nils Werner
Nils Werner

Reputation: 36765

A vectorized solution:

Remember that you can compare all elements in b with all elements in a using broadcasting:

b[:, None] > a
# array([[ True, False, False, False],     # b[0] > a[:]
#        [ True,  True, False, False]])    # b[1] > a[:]

And now find the index of the last True value in each row, which equals to the first False value in each row, minus 1

np.argmin((b[:, None] > a), axis=1) - 1
# array([0, 1])

Note that there might be an ambiguity as to what a returned value of -1 means. It could mean

  1. b[x] was larger than all elements in a, or
  2. b[x] was not larger than any element in a

In our data, this means

a = np.asarray([10, 20, 30, 40])
b = np.asarray([9, 12, 25, 39, 40, 41, 50])

mask = b[:, None] > a
# array([[False, False, False, False],   # 9 is smaller than a[:], case 2
#        [ True, False, False, False],
#        [ True, False, False, False],
#        [ True,  True,  True, False],
#        [ True,  True,  True, False],
#        [ True,  True,  True,  True],   # 41 is larger than a[:], case 1
#        [ True,  True,  True,  True]])  # 50 is larger than a[:], case 1

So for case 1 we need to find rows with all True values:

is_max = np.all(mask, axis=1)

And for case 2 we need to find rows with all False values:

none_found = np.all(~mask, axis=1)

This means we can use the is_max to find and replace all case 1 -1 values with a positive index

mask = b[:, None] > a
is_max = np.all(mask, axis=1)
# array([False, False, False, False, False,  True,  True])
idx = np.argmin(mask, axis=1) - 1
# array([-1,  0,  0,  2,  2, -1, -1])
idx[is_max] = len(a) - 1
# array([-1,  0,  0,  2,  2,  3,  3])

However be aware that the index -1 has a meaning: Just like 3 it already means "the last element". So if you want to use idx for indexing, keeping -1 as an invalid value marker may cause trouble down the line.

Upvotes: 3

RohithS98
RohithS98

Reputation: 510

np.asarray([i for i in range(len(b)) if b[i]>a[i]])

This should give you the answer. Also the length does not have to be same as that of either a or b.

Upvotes: 0

Sunitha
Sunitha

Reputation: 12015

You can zip a and b to combine them and then enumerate to iterate it with its index

[i for i,(x,y) in enumerate(zip(a,b)) if y>x]
# [0, 1]

Upvotes: 0

ᴀʀᴍᴀɴ
ᴀʀᴍᴀɴ

Reputation: 4528

Works even a has shorter length than b , first choose shorter list length then check if its has smaller numbers element wise :

[i for i in range(min(len(a),len(b))) if min(a, b, key=len)[i] > max(a, b, key=len)[i]]
# [0, 1]

Upvotes: 0

Related Questions