按模式查找布尔掩码

Posted

技术标签:

【中文标题】按模式查找布尔掩码【英文标题】:Find boolean mask by pattern 【发布时间】:2018-08-05 21:10:02 【问题描述】:

我有数组:

arr = np.array([1,2,3,2,3,4,3,2,1,2,3,1,2,3,2,2,3,4,2,1])
print (arr)
[1 2 3 2 3 4 3 2 1 2 3 1 2 3 2 2 3 4 2 1]

我想找到这个模式并返回布尔掩码:

pat = [1,2,3]
N = len(pat)

我用strides

#https://***.com/q/7100242/2901002
def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    c = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
    return c
print (rolling_window(arr, N))
[[1 2 3]
 [2 3 2]
 [3 2 3]
 [2 3 4]
 [3 4 3]
 [4 3 2]
 [3 2 1]
 [2 1 2]
 [1 2 3]
 [2 3 1]
 [3 1 2]
 [1 2 3]
 [2 3 2]
 [3 2 2]
 [2 2 3]
 [2 3 4]
 [3 4 2]
 [4 2 1]]

我只找到第一个值的位置:

b = np.all(rolling_window(arr, N) == pat, axis=1)
c = np.mgrid[0:len(b)][b]
print (c)
[ 0  8 11]

并定位另一个 val:

d = [i  for x in c for i in range(x, x+N)]
print (d)
[0, 1, 2, 8, 9, 10, 11, 12, 13]

in1d 的最后返回掩码:

e = np.in1d(np.arange(len(arr)), d)
print (e)
[ True  True  True False False False False False  True  True  
  True  True  True  True False False False False False False]

验证掩码:

print (np.vstack((arr, e))) 
[[1 2 3 2 3 4 3 2 1 2 3 1 2 3 2 2 3 4 2 1]
 [1 1 1 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0]]
  1 2 3           1 2 3 1 2 3   

我认为我的解决方案有点过于复杂。有没有更好、更 Pythonic 的解决方案?

【问题讨论】:

你见过this吗?不过他们似乎在做同样的事情。 是的,完全正确。我添加了这个有问题的链接 你能Boyer--Moore-搜索吗? @CongMa - 看起来不错。 您是在寻找效率还是只是为了更短的代码? 【参考方案1】:

不确定这有多安全,但另一种方法是读回布尔输出的as_strided 视图。只要您一次只有一个pat,我认为这应该不是问题,它可能可以使用更多,但我不能保证,因为回读as_strided可能有点不可预测:

def vview(a):  #based on @jaime's answer: https://***.com/a/16973510/4427777
    return np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))

def roll_mask(arr, pat):
    pat = np.atleast_2d(pat)
    out = np.zeros_like(arr).astype(bool)
    vout = rolling_window(out, pat.shape[-1])
    vout[np.in1d(vview(rolling_window(arr, pat.shape[-1])), vview(pat))] = True
    return out

np.where(roll_mask(arr, pat))
(array([ 0,  1,  2,  8,  9, 10, 11, 12, 13], dtype=int32),)

pat = np.array([[1, 2, 3], [3, 2, 3]])
print([i for i in arr[roll_mask(arr, pat)]])
[1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3]

它似乎有效,但我不会给初学者这个答案!

【讨论】:

【参考方案2】:

我们可以在最后使用 Scipy 支持的二进制膨胀来简化事情 -

from scipy.ndimage.morphology import binary_dilation

m = (rolling_window(arr, len(pat)) == pat).all(1)
m_ext = np.r_[m,np.zeros(len(arr) - len(m), dtype=bool)]
out = binary_dilation(m_ext, structure=[1]*N, origin=-(N//2))

为了性能,我们可以引入具有模板匹配功能的 OpenCV,因为我们在这里基本上是这样做的,就像这样 -

import cv2

tol = 1e-5
pat_arr = np.asarray(pat, dtype='uint8')
m = (cv2.matchTemplate(arr.astype('uint8'),pat_arr,cv2.TM_SQDIFF) < tol).ravel()

【讨论】:

谢谢你的帮助,但第二个解决方案对我来说失败了,你能从这个answer 中检查样本中应该有什么问题吗?

以上是关于按模式查找布尔掩码的主要内容,如果未能解决你的问题,请参考以下文章

1.6 比较掩码和布尔逻辑

通过布尔掩码数组选择numpy数组的元素

python TensorFlow做布尔掩码

按布尔值过滤 DataGridView

选择带有由 isnull() 创建的 2D 布尔掩码的数据框

如何通过逐行求和(SQL Alchemy)来创建布尔掩码?