获取布尔数组中至少 n 个连续 False 值的第一个块的索引

get index of the first block of at least n consecutive False values in boolean array

我有一个 numpy 布尔数组

w=np.array([True,False,True,True,False,False,False])

我想获取第一个在 n_at_least 处有错误值的索引。 比如这里

`n_at_least`=1 -> desired_index=1

`n_at_least`=3 -> desired_index=4

我试过了

np.cumsum(~w)

每次遇到 False 值时都会增加。 但是,当遇到 True 时,计数器不再从 0 开始,所以我只得到 False 元素的总数,而不是最后一个连续元素的计数。

它应该这样工作:

def n_at_least(n):
    for i in range(len(w)):
         if not any(w[i:i+n]):
             return i

不过不知道有没有更好的办法...

我认为您陷入了只想使用 numpy 函数的 numpy 陷阱。 python 怎么了?这个解决方案是O(n)

def f(array, n_at_least):
    curr_found_false = 0
    curr_index = 0
    for index, elem in enumerate(array):
        if not elem:
            if curr_found_false == 0:
                curr_index = index
            curr_found_false += 1
            if curr_found_false == n_at_least:
                return curr_index
        else:
            curr_found_false = 0

产出

w=np.array([True,False,True,True,False,False,False])
f(w, 1)
# --> 1
f(w, 3)
# --> 4

这是一个向量化的解决方案,可以找到起始索引、终止索引以及 岛屿 的零长度,最后使用 argmax 获取第一个岛屿的起始索引满足零计数的标准是 >= n -

def first_occ_index(w, n):
    idx = np.flatnonzero(np.r_[True, w, True])
    lens = np.diff(idx) - 1
    return idx[(lens >= n).argmax()]

样本运行-

In [107]: w
Out[107]: array([ True, False,  True,  True, False, False, False])

In [108]: first_occ_index(w, n=1)
Out[108]: 1

In [109]: first_occ_index(w, n=3)
Out[109]: 4

这是一个复杂度为 O(n) 的 numpy 解决方案:

>>> def first_consec(A, n):
...     A = np.r_[True, A, True]
...     switch, = np.where(A[:-1]!=A[1:])
...     runs = switch[1::2] - switch[::2]
...     idx = np.argmax(runs >= n)
...     if runs[idx] < n:
...         return None
...     return switch[2*idx]
... 
>>> first_consec(w, 4)
>>> first_consec(w, 3)
4
>>> first_consec(w, 2)
4
>>> first_consec(w, 1)
1

这是使用带切片的生成器表达式的一种方法:

w = np.array([True,False,True,True,False,False,False])

n = 2
val = False

res = next((i for i, j in enumerate(w[k:k+n] for k in range(len(w)-n+1)) \
            if np.all(j==val)), None)

# 4

我认为对于这种线性搜索操作,python 实现是可以的。我的建议是这样的:

def find_block(arr, n_at_least=1):
    current_index = 0
    current_count = 0
    for index, item in enumerate(arr):
         if item:
             current_count = 0
             current_index = index + 1
         else:
             current_count += 1
         if current_count == n_at_least:
             return current_index
    return None # Make sure this is outside for loop

运行 此函数产生以下输出:

>>> import numpy
>>> w = numpy.array([True, False, True, True, False, False, False])
>>> find_block(w, n_at_least=1)
1
>>> find_block(w, n_at_least=3)
4
>>> find_block(w, n_at_least=4)
>>> # None