How can I determine if a numpy boolean array contains only one "True`s" block?

If I have a numpy array containing booleans, say the result of some mathematical comparison, the best way to determine if this array contains only one continuous True s block, for example

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

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

+7
python numpy
source share
4 answers

numpy.diff is useful in this case. You can count the number -1 in the diff ed array.

Note that you also need to check the last element - if it is True, it will not have -1 in the diff ed array to indicate this. Even better, you can add False to the array before diff ing.

 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 

To add False to the end:

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

Now the "sequence ..., True, False, ..., True ... never happens" iff (d<0).sum() < 2 .

The trick to avoid the append operation (and make your code more obscure) is: (d<0).sum() + a[-1] < 2 (that is, if [-1] is True, read it like a block). This will only work if a is not empty.

+5
source share

Not a native numpy approach, but you can use itertools.groupby to reduce blocks of continuous values ​​into a single element, and then verify that only one true value appears with any . Since grouped is grouped , the first any returns True , as soon as the true value is found, you then resume checking the rest of the iteration and make sure there is no other true value.

 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 
+3
source share

If you have only one Trues block, it means that you have one transition in the array or you have two transitions, and the array starts and ends with False. There is also a trivial case where the entire array is True. So you can do something like:

 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 

This is actually the same logic, but the code is a little cleaner.

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

Some time related to comments:

 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 
+1
source share
 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 
0
source share

All Articles