numpy:检测数组中的连续1

时间:2016-10-31 07:38:08

标签: python arrays numpy

我想在numpy数组中检测1的连续跨度。实际上,我想首先确定数组中的元素是否在至少三个1的范围内。例如,我们有以下数组a:

    import numpy as np
    a = np.array([1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])

然后以下粗体显示满足要求的元素。

[ 1,1,1 ,0, 1,1,1 ,0,1, 1,0,0, 1,1,1 ,0,0, 1,1,1,1,1 < / strong>,0]

接下来,如果1的两个跨度最多分开两个0,则两个跨距构成更长的跨度。所以上面的数组被描述为

[ 1,1,1,0,1,1,1 ,0,1,1,0,0, 1, 1,1,0,0,1,1,1,1,1 ,0]

换句话说,对于原始数组作为输入,我想要输出如下:

    [True, True, True, True, True, True, True, False, False, False, False, False, True, True, True, True, True, True, True, True, True, True, False]

我一直在想一个实现这个功能的算法,但我提出的所有算法似乎都很复杂。所以我很想知道更好的方法来实现这一点 - 如果有人可以帮助我,我将不胜感激。

更新

我道歉,我没有说清楚我的问题。我希望将数组中的3个或更多个连续1标识为1的跨度,并且识别中间只有一个或两个0的任何两个1的跨度,以及分离0,作为单个长跨度。我可以通过以下方式理解我的目标:如果在1的跨度之间只有一个或两个0,我认为这些0作为错误并且应该被纠正为1&# 39; S

@ ritesht93提供的答案几乎可以满足我的需求。但是,当存在由0分隔的1个跨度时,当前答案不能识别这种情况,应将其识别为单个跨度。例如,对于数组

    a2 = np.array([0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0])

我们应该收到输出

    [False,  True,  True,  True,  True,  True,  True,  True,  True,
   True,  True,  True,  True,  True, False, False,  False, False,
   False,  True,  True,  True,  True,  True, False]

更新2:

我受到了很大的启发,发现基于正则表达式的算法最容易实现和理解 - 虽然我不确定与其他方法相比的效率。最后我使用了以下方法。

    lst = np.array([0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0])
    lst1 = re.sub(r'1{3,}', lambda x:'c'*len(x.group()), ''.join(map(str, lst)))
    print lst1

确定了1&#39的跨度

    0ccc0ccc00cccc00100ccccc0

然后连接1&#39的跨度

    lst2 = re.sub(r'c{1}0{1,2}c{1}', lambda x:'c'*len(x.group()), ''.join(map(str, lst1)))
    print lst2

给出了

    0ccccccccccccc00100ccccc0

最终结果由

给出
    np.array(list(lst2)) == 'c'

    array([False,  True,  True,  True,  True,  True,  True,  True,  True,
    True,  True,  True,  True,  True, False, False, False, False,
   False,  True,  True,  True,  True,  True, False])

4 个答案:

答案 0 :(得分:2)

我们可以使用binary dilationerosion的组合来解决它,以便超过第一阶段,然后binary closing来获得最终输出,就像这样 -

from scipy.ndimage.morphology import binary_erosion,binary_dilation,binary_closing

K = np.ones(3,dtype=int) # Kernel
b = binary_dilation(binary_erosion(a,K),K)
out = binary_closing(b,K) | b

示例运行

案例#1:

In [454]: a
Out[454]: array([1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])

In [456]: out
Out[456]: 
array([ True,  True,  True,  True,  True,  True,  True, False, False,
       False, False, False,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True, False], dtype=bool)

案例#2:

In [460]: a
Out[460]: 
array([0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0])

In [461]: out
Out[461]: 
array([False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True, False, False, False, False,
       False,  True,  True,  True,  True,  True, False], dtype=bool)

答案 1 :(得分:1)

我们可以将所有0和1转换为单个字符串,而不是解决传统的循环和维护计数方式,而是将正则表达式匹配替换为另一个字符2。完成后,我们再次拆分字符串并检查每个字符的bool()

>>> import re
>>> lst=[1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0]
>>> list(map(bool, map(int, list(re.sub(r'1{3,}0{1,2}1{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst)))))))
[True, True, True, True, True, True, True, False, True, True, False, False, True, True, True, True, True, True, True, True, True, True, False]
>>> 

所有操作都在这里进行:

re.sub(r'1{3,}0{1,2}1{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst)))

它搜索连续出现的3个或更多1个,然后是最多2个0,即1个或2个,然后是3个或更多1个并且替换为3个或更多个1&2;整个匹配的字符串具有相同长度的字符串2(使用2,因为bool(2)True)。您也可以使用tolist()中的NumPy方法从NumPy数组中获取列表:np.array([1,2, 3, 4, 5, 6]).tolist()

编辑1 :在有问题的更改后,这是更新后的答案:

>>> lst=[1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0]
>>> import re
>>> list(map(lambda x:False if x == 0 or x ==1 else True, map(int, list(re.sub(r'1{3,}0{1,2}1{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst)))))))
[True, True, True, True, True, True, True, False, False, False, False, False, True, True, True, True, True, True, True, True, True, True, False]
>>> 

编辑2最终答案

>>> import re
>>> lst=[0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0]
>>> while re.subn(r'[12]{3,}0{1,2}[12]{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst)))[1]:
...     lst=re.subn(r'[12]{3,}0{1,2}[12]{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst)))[0]
... 
>>> lst
'0222222222222200100111110'
>>> lst=list(re.sub(r'1{3,}', lambda x:'2'*len(x.group()), ''.join(map(str, lst))))
>>> lst
['0', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '0', '0', '1', '0', '0', '2', '2', '2', '2', '2', '0']
>>> list(map(lambda x:False if x == 0 or x ==1 else True, map(int, lst)))
[False, True, True, True, True, True, True, True, True, True, True, True, True, True, False, False, False, False, False, True, True, True, True, True, False]
>>> 

答案 2 :(得分:1)

我知道这不是“python-wise”,但是既然你谈到算法,我决定尝试一下(对不起,我对python不太熟悉)

import numpy as np
a = np.array([1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])
b = np.array([int])

#init 2nd array 
for x in range (0,(a.size-1)):
    b = np.append(b,0)

print (b)
#1st case
for x in range (2,(a.size)):
    if (a[x-2]==1 & a[x-1]==1 & a[x]==1): #1-1-1
        b[x] = 1
        b[x-1] = 1
        b[x-2] = 1

print (b)
#2nd case
for x in range (2,(b.size)):
    if (b[x-2]==1 & b[x]==1): #1-0-1
        if (b[x-1]==0): #sorry, i forget about logical op. in python
            b[x-1] = 1

print (b)
#3rd case
for x in range (3,(b.size)):
    if (b[x-3]==1 & b[x]==1): #1-0-0-1
        if (b[x-2]==0 & b[x]-1==0):
            b[x-1] = 1
            b[x-2] = 1

#4th case
for x in range (4,(b.size)):
    if (a[x-4]==1 & a[x-3]==1 & b[x]): #1-1-0-0-1
        if (a[x-2]==0 & a[x]-1==0):
            b[x-3] = 1
            b[x-4] = 1
print (b)

我不确定这是否完全符合您的预期结果,但这里是:
[1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0]

答案 3 :(得分:1)

很多方法。我会将其拆分为分组 pply条件组展平操作。像这样:

from itertools import groupby, starmap
import numpy as np

a = np.array([1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])

def condition(groups, key, newkey, minlen):
    return [(newkey, l) if l < minlen and k == key else (k, l) for k, l in groups]

def flatten(groups):
    return [k for g in starmap(lambda k, l: l * [k], groups) for k in g]

def group(l):
    return [(k, len(list(v))) for k, v in groupby(l)]

res = group(flatten(condition(group(a), 1, 0, 3)))
# groups zeros at the beginning or the end never change to ones,
# no matter their length
res = flatten([res[0]] + condition(res[1:-1], 0, 1, 3) + [res[-1]])
print [bool(v) for v in res]