[英]Group consecutive True in 1-D numpy array
假设我们有一个 boolean 数组x=np.array([True, True, False, True, False])
。 有两组连续的True
。 我想要的是创建 boolean arrays l
的列表,其中l
中的每个数组只包含一组连续的True
。 例如, x
应该与y
相同,定义为
y = np.zeros_like(x)
for e in l:
y = y|e
到目前为止,我唯一成功的尝试是使用consecutive
function by https://stackoverflow.com/a/7353335/4755229
def consecutive_bools(bool_input):
consecutive_idx = consecutive(np.argwhere(bool_input).flatten())
ret = [np.zeros_like(bool_input) for i in range(len(consecutive_idx))]
for i, idx in enumerate(consecutive_idx):
ret[i][idx] = True
return ret
这似乎过于复杂。 有没有更好(简洁,可能更快)的方法来做到这一点?
考虑以下:
import numpy as np
x=np.array([True, True, False, True, False])
idx, = np.where(np.insert(x,0,False) ^ np.insert(x,-1,False))
l = [np.zeros_like(x),np.zeros_like(x)]
l[0][idx[0]:idx[1]] = True
l[1][idx[2]:idx[3]] = True
这里的想法是 idx 的元素是从 True 到 False 或从 True 到 False 的任何开关的索引。 由于您正好有 2 个连续的True
组,因此 idx 正好有 4 个元素。
对于任意数量的连续组:
idx, = np.where(np.insert(x,0,False) ^ np.insert(x,-1,False))
l = [np.zeros_like(x) for _ in range(len(idx)//2)]
for a,p in zip(l,np.split(idx,np.arange(2,len(idx),2))):
a[slice(*p)] = True
一个有趣的方法是构造每个段的起点和终点,然后通过np.arange(x.size)
构造一个数组。 比较它和所有以>=
开头的,并比较它和所有以<
结尾的。 两个结果的逻辑与产生所需的 output:
def my_consecutive_bools(ar):
indices, = np.concatenate([ar[:1], ar[:-1] != ar[1:], ar[-1:]]).nonzero()
arange = np.arange(ar.size)
return np.logical_and(arange >= indices[::2, None],
arange < indices[1::2, None])
>>> x = np.array([True, True, False, True, False])
>>> my_consecutive_bools(x)
array([[ True, True, False, False, False],
[False, False, False, True, False]])
这种方法在一些小的arrays上效果很好,但是时间复杂度高。 对于较大的 arrays,您可以简单地遍历 start 和 stop 来赋值:
def my_consecutive_bools_loop(ar):
indices, = np.concatenate([ar[:1], ar[:-1] != ar[1:], ar[-1:]]).nonzero()
result = np.zeros((indices.size // 2, ar.size), bool)
for row, start, stop in zip(result, indices[::2], indices[1::2]):
row[start:stop] = True
return result
简单基准:
In [_]: rng = np.random.default_rng()
In [_]: small = rng.choice([True, False], 100, p=[0.8, 0.2])
In [_]: big = rng.choice([True, False], 100000, p=[0.8, 0.2])
In [_]: %timeit consecutive_bools(small)
109 µs ± 286 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [_]: %timeit my_consecutive_bools(small)
13.3 µs ± 46.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
In [_]: %timeit my_consecutive_bools_loop(small)
20 µs ± 122 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [_]: %timeit consecutive_bools(big)
699 ms ± 6.62 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [_]: %timeit my_consecutive_bools(big)
2.98 s ± 17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [_]: %timeit my_consecutive_bools_loop(big)
33.4 ms ± 1.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
这可以通过创建一个数组来实现,每个 True 条纹都分配有一个不同的组标识符。 使用反转的 True/False 的累积和可以产生对于连续的 True 位置保持相同的组号。 将其与原始数组相交会将 False position 放入第 0 组(我们可以排除)。 通过将唯一组编号与组序列(在广播中)进行比较,可以获得 True 条纹的预期矩阵:
import numpy as np
x=np.array([True, True, False, True, False])
groups = (~x).cumsum() + 1
trues = np.unique(groups[x])[:,None] == groups * x
print(trues)
[[ True True False False False]
[False False False True False]]
适用于任意数量的组(您不需要提前知道):
x=np.array([False,True,True,True,False,True,False,False,True,True,True])
groups = (~x).cumsum() + 1
trues = np.unique(groups[x])[:,None] == groups * x
print(trues)
[[False True True True False False False False False False False]
[False False False False False True False False False False False]
[False False False False False False False False True True True]]
这应该比任何涉及循环的解决方案都快得多。 它也更加简洁。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.