根据布尔数组查找连续序列

时间:2019-03-26 07:16:40

标签: python numpy

我正在尝试从数组 b 中提取序列,该数组的布尔数组 a 用作索引(len(a) >= len(b),但{{1} },即 a 中的true值与 b 中的元素一样多。序列应在结果中表示为 a 的开始和结束索引,其中(a==True).sum() == len(b)为true,并且具有连续的值。

例如,对于以下 a b

数组
a[i]

结果应为a = np.asarray([True, True, False, False, False, True, True, True, False]) b = [1, 2, 3, 4, 5] ,因此数组中的所有元素与真实序列一样多。每个正确的序列都应包含 a 的开始索引和结束索引,以及它们与 b 关联的值。

对于上述情况,

[((0, 1), [1, 2]), ((5, 7), [3, 4, 5])]

如何在numpy中有效地做到这一点?

5 个答案:

答案 0 :(得分:2)

使用itertools.groupbyitertools.islice

import itertools

res = []
gen = (i for i in b)
for k, g in itertools.groupby(enumerate(a), lambda x:x[1]):
    if k:
        ind, bools = list(zip(*g))
        res.append((ind[0::len(ind)-1], list(itertools.islice(gen, len(bools)))))

输出

[((0, 1), [1, 2]), ((5, 7), [3, 4, 5])]

见解:

  • itertools.groupby返回TrueFalse的分组对象。
  • list[0::len(list)-1]返回list的第一个和最后一个元素。
  • 由于b始终具有相同数量的True,因此将b设为generator,并获取与True一样多的元素。 / li>

花费时间:

def itertool_version():
    res = []
    gen = (i for i in b)
    for k, g in itertools.groupby(enumerate(a), lambda x:x[1]):
        if k:
            ind, bools = list(zip(*g))
            res.append((ind[0::len(ind)-1], list(itertools.islice(gen, len(bools)))))
    return res

%timeit itertool()
7.11 µs ± 313 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

答案 1 :(得分:2)

这是一个基于NumPy的游戏,灵感来自this post-

Other C Flags

样品运行-

def func1(a,b):
    # "Enclose" mask with sentients to catch shifts later on
    mask = np.r_[False,a,False]

    # Get the shifting indices
    idx = np.flatnonzero(mask[1:] != mask[:-1])

    s0,s1 = idx[::2], idx[1::2]
    idx_b = np.r_[0,(s1-s0).cumsum()]
    out = []
    for (i,j,k,l) in zip(s0,s1-1,idx_b[:-1],idx_b[1:]):
        out.append(((i, j), b[k:l]))
    return out

时间-

In [104]: a
Out[104]: array([ True,  True, False, False, False,  True,  True,  True, False])

In [105]: b
Out[105]: [1, 2, 3, 4, 5]

In [106]: func1(a,b)
Out[106]: [((0, 1), [1, 2]), ((5, 7), [3, 4, 5])]

答案 2 :(得分:1)

我不知道使用numpy的解决方案,但以下for-loop解决方案可能会帮助您(或其他人)找到其他更有效的解决方案:

import numpy as np

a = np.asarray([True, True, False, False, False, True, True, True, False])
b = []
temp_list = []
count = 0
for val in a:
    if (val):
        count += 1
        temp_list.append(count) if len(temp_list) == 0 else None  # Only add the first 'True' value in a sequence
    # Code only reached if val is not true > append b if temp_list has more than 1 entry
    elif (len(temp_list) > 0):
        temp_list.append(count)  # Add the last true value in a sequence
        b.append(temp_list)
        temp_list = []
print(b)

>>> [[1, 2], [3, 5]]

答案 3 :(得分:1)

这是我的两分钱。希望能帮助到你。 [编辑]

# Get Data
a = np.asarray([True, True, False, False, False, True, True, True, False])
b = [1, 2, 3, 4, 5]

# Assign Index names
ac = ac.astype(float)
ac[ac==1] = b


# Select edges
ac[(np.roll(ac, 1) != 0) & (np.roll(ac, -1) != 0)] = 0 # Clear out intermediates
indices = ac[ac != 0] # Select only edges
indices.reshape(2, int(indices.shape[0]/2)) # group in pairs

输出

>> [[1, 2], [3, 5]]

答案 4 :(得分:1)

解决方案使用numpy中的方法 where()

result = []
f = np.where(a)[0]
m = 1
for j in list(create(f)):
    lo = j[1]-j[0]+1
    result.append((j, [*range(m, m + lo)]))
    m += lo

print(result)
#OUTPUT: [((0, 1), [1, 2]), ((5, 7), [3, 4, 5])]

有一种方法可以拆分数组[0 1 5 6 7]-> [(0,1),(5,7)]:

def create(k):
    le = len(k)
    i = 0

    while i < le:
        left = k[i]
        while i < le - 1 and k[i] + 1 == k[i + 1]:
            i += 1
        right = k[i]
        if right - left >= 1:
            yield (left, right)
        elif right - left == 1:
            yield (left, )
            yield (right, )
        else:
            yield (left, )
        i += 1