8

If I have a numpy array containing booleans, say the output of some math comparison, what's the best way of determining whether that array contains only a single contiguous block of Trues, e.g.

array([False, False, False, True, True, True, False, False, False], dtype=bool)

i.e. where the sequence ...,True, False, ..., True... never occurs?

4

4 回答 4

6

numpy.diff在这种情况下很有用。diff您可以计算ed 数组中 -1 的数量。

请注意,您还需要检查最后一个元素——如果它是 True,则diffed 数组中不会有一个 -1 来表明这一点。更好的是,您可以在ing之前附加False到数组。diff

import numpy as np
a = np.array([False, False, False, True, True, True, False, False, False], dtype=bool)
d = np.diff(np.asarray(a, dtype=int))
d
=> array([ 0,  0,  1,  0,  0, -1,  0,  0])
(d < 0).sum()
=> 1

在末尾追加False

b = np.append(a, [ False ])
d = np.diff(np.asarray(b, dtype=int))
...

现在,“序列 ...,True, False, ..., True... 永远不会出现” iff (d<0).sum() < 2

避免该append操作(并使您的代码更加晦涩)的技巧是:((d<0).sum() + a[-1] < 2即,如果 a[-1] 为真,则将其视为一个块)。当然,这只有在 a 不为空时才有效。

于 2013-09-13T19:29:33.747 回答
3

不是numpy本机方法,但您可以使用itertools.groupby将连续值块减少为单个项目,然后使用any. 由于grouped是可迭代的,一旦找到真值,第一个any返回,然后您继续检查可迭代的其余部分并确保没有另一个真值。True

from itertools import groupby

def has_single_true_block(sequence):
    grouped = (k for k, g in groupby(sequence))
    has_true = any(grouped)
    has_another_true = any(grouped)
    return has_true and not has_another_true
于 2013-09-13T20:05:07.507 回答
1

如果您只有一个 True 块,这意味着您要么在数组中有一个转换,要么有两个转换并且数组以 False 开始和结束。还有一个小例子,整个数组为真。因此,您可以执行以下操作:

def singleBlockTrue(array):
   if len(array) == 0:
       return False
   transitions = (array[1:] != array[:-1]).sum()
   if transitions == 0:
       return array[0]
   if transitions == 1:
       return True
   if transitions == 2:
       return not array[0]
   return False

这实际上是相同的逻辑,但代码更简洁一些。

def singleBlockTrue(array):
    if len(array) == 0:
        return False
    transitions = (array[1:] != array[:-1]).sum()
    transitions = transitions + array[0] + array[-1]
    return transitions == 2

与评论相关的一些时间:

In [41]: a = np.zeros(1000000, dtype=bool)

In [42]: timeit a[:-1] != a[1:]
100 loops, best of 3: 2.93 ms per loop

In [43]: timeit np.diff(a.view('uint8'))
100 loops, best of 3: 2.45 ms per loop

In [44]: timeit np.diff(a.astype('uint8'))
100 loops, best of 3: 3.41 ms per loop

In [45]: timeit np.diff(np.array(a, 'uint8'))
100 loops, best of 3: 3.42 ms per loop
于 2013-09-13T19:39:46.353 回答
0
import numpy as np

def has_single_true_block(arr):
    if not len(arr):
        return False
    blocks = len(np.array_split(arr, np.where(np.diff(arr) != 0)[0] + 1))
    if blocks > 3:
        return False
    elif blocks == 3 and arr[0] and arr[-1]:
        return False
    elif blocks == 1 and not arr[0]:  # 0 True blocks
        return False
    return True

# TESTS

a1 = np.array([False, False, True, True, True, False, False], dtype=bool)
has_single_true_block(a1)  # => True

a2 = np.array([True, True, False, False], dtype=bool)
has_single_true_block(a2)  # => True

a3 = np.array([False, False, True, True], dtype=bool)
has_single_true_block(a3)  # => True

f1 = np.array([False, False, True, False, True, False, False], dtype=bool)
has_single_true_block(f1)  # => False

f2 = np.array([True, True, False, False, True, True], dtype=bool)
has_single_true_block(f2)  # => False

f3 = np.array([False, False, False], dtype=bool)
has_single_true_block(f3)  # => False
于 2013-09-13T19:56:49.877 回答