简体   繁体   中英

NumPy - catching the index of first three consecutive negative numbers

I need to find the index of the first occurrence of three consecutive negative numbers. In the normal Python way I would do it like this:

a = [1,-1,1,-1,1,-1,1,-1,-1,-1,1,-1,1]
b=0
for i,v in enumerate(a):
    if v<0:
        b+=1
    else:
        b=0
    if b==3:
        break
indx = i-2

Anyone has an idea how to do it in a smarter NumPy way?

Here's a vectorized solution with help from convolution -

def first_consecutive_negative_island(a, N=3):
    mask = np.convolve(np.less(a,0),np.ones(N,dtype=int))>=N
    if mask.any():
        return mask.argmax() - N + 1
    else:
        return None

Sample run -

In [168]: a = [1,-1,1,-1,1,-1,1,-1,-1,-1,1,-1,1]

In [169]: first_consecutive_negative_island(a, N=3)
Out[169]: 7

Works irrespective of where the group exists -

In [175]: a
Out[175]: [-1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1]

In [176]: first_consecutive_negative_island(a, N=3)
Out[176]: 0

With no negative numbers, it gracefully returns None -

In [183]: a
Out[183]: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

In [184]: first_consecutive_negative_island(a, N=3)

For exactly three consecutive negative numbers search, we can use slicing, like so -

def first_consecutive_negative_island_v2(a):
    m =  np.less(a,0)
    mask = m[:-2] & m[1:-1] & m[2:]
    if mask.any():
        return mask.argmax()
    else:
        return None

Timings -

In [270]: a = np.random.randint(-1,2,(1000000)).tolist()

In [271]: %timeit first_consecutive_negative_island(a, N=3)
10 loops, best of 3: 44.5 ms per loop

In [272]: %timeit first_consecutive_negative_island_v2(a)
10 loops, best of 3: 38.7 ms per loop
import numpy as np
a = np.array([1,-1,1,-1,-1,-1,1,-1,-1,-1,1,-1,1])
item_index = np.where(a < 0)
for a,b in zip( item_index[0],item_index[0][2:]):
    if b-a == 2: 
        break
index_ = a

There is no looping needed at all. Also no zipping. Just do the following:

a = np.array([1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1])
idx = np.where(a < 0)[0][:3]

np.where returns a tuple with the indices of the chosen condition. [0] indexes this tuple's first dimension (for a 1D-input array this is the only dimension) and [:3] slices these indices to only use the first three indices.

If the occurrence of the first three consecutive negative numbers is needed, you can do the following:

idx = np.where(np.diff(np.where(a < 0), n=2) == 0)[1][0] + 2

This will give you the index of the first negative number of three consecutive negative numbers. If you want the indices of all three first negative consecutive numbers, do this:

idx_start = np.where(np.diff(np.where(a < 0), n=2) == 0)[1][0] + 2
idx = np.arange(idx_start, idx_start + 3)

This will work as long as the first two numbers in the array are not part of three consecutive negative numbers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM