numpy数组中带有通配符的二维模式匹配

时间:2018-08-29 21:24:41

标签: python arrays numpy

首先,我知道thisthisthis有关二维模式匹配的文章,但是这些文章都不包含通配符匹配。此外,我知道有this one之类的几篇论文已经解决了我面临的问题。但是,我只是熟悉二维数组中的模式匹配,而目前尝试在论文中实现算法对我来说并不平凡。

因此,以下问题是我所面临的。

给出一个二维数组:

[[1 3 4 5 3 3]
 [4 1 4 5 5 2]
 [5 4 3 4 4 2] # pattern
 [4 5 3 4 1 3] # pattern
 [3 3 3 4 4 4] # pattern
 [3 4 3 4 2 5] # pattern
 [4 5 3 4 1 2] # pattern
 [5 1 1 2 4 2]
 [2 1 3 2 1 5]
 [4 4 1 3 3 1]
 [1 4 3 4 4 1]
 [5 2 4 4 4 1]]

以及以下示例模式(其中?表示通配符匹配):

[[? ? 3 4 ? ?]
 [? ? 3 4 ? ?]
 [3 3 3 4 4 4]
 [? ? 3 4 ? ?]
 [? ? 3 4 ? ?]]

如何编写一个包含二维数组和模式的函数,如果该模式存在于数组中,则返回True,否则返回False?

如果可能的话,将高度赞赏该问题的通用解决方案,因为我尝试匹配许多不同的模式。如果需要,我非常愿意提供其他示例。

2 个答案:

答案 0 :(得分:1)

由于您的搜索空间非常小,因此我们不必通过拆开窗口视图来担心内存错误。

首先,您需要掩码其中模式中包含值的

mask

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

然后您需要一个数组什么值在那些位置:

val = np.array([ 3.,  4.,  3.,  4.,  3.,  3.,  3.,  4.,  4.,  4.,
                 3.,  3.,  3.,  4.,  4.,  4.,  3.,  4.,  3.,  4.])

然后,您需要一个跨输入的滑动窗口。最容易使用的实现是skimage.util.view_as_windows,但是您可以使用我的pure numpy implementatation here

windows = skimage.util.view_as_windows(input, pattern.shape)
# or
windows = window_nd(input, pattern.shape)

现在,通常在此处执行windows[mask]会很危险-如果您在多个窗口上进行卷积,它会创建一个巨大的数组。但是,因为我们在此可以拥有的最多窗户数是12 * 6 = 72,所以我们不必为此担心。

loc = np.where(np.all(window[mask] == val, axis = -1))

现在loc是匹配窗口左上角的坐标。

或者应该是。也许提供一个可以复制/粘贴到解释器中的测试用例?

答案 1 :(得分:1)

此函数采用input_arraypattern和使您可以识别通配符的函数。在这里,我已将np.nan用作通配符,但可以是任何东西,因为您可以自己制作wildcard_function

它适用于任何维数(1或更大)的数组。我已经为您的示例进行了测试,看起来还可以。

from itertools import product
import numpy as np


def match_pattern(input_array, pattern, wildcard_function=np.isnan):

    pattern_shape = pattern.shape
    input_shape = input_array.shape

    is_wildcard = wildcard_function(pattern) # This gets a boolean N-dim array

    if len(pattern_shape) != len(input_shape):
        raise ValueError("Input array and pattern must have the same dimension")

    shape_difference = [i_s - p_s for i_s, p_s in zip(input_shape, pattern_shape)]

    if any((diff < -1 for diff in shape_difference)):
        raise ValueError("Input array cannot be smaller than pattern in any dimension")

    dimension_iterators = [range(0, s_diff + 1) for s_diff in shape_difference]

    # This loop will iterate over every possible "window" given the shape of the pattern
    for start_indexes in product(*dimension_iterators):
        range_indexes = [slice(start_i, start_i + p_s) for start_i, p_s in zip(start_indexes, pattern_shape)]
        input_match_candidate = input_array[range_indexes]

        # This checks that for the current "window" - the candidate - every element is equal 
        #  to the pattern OR the element in the pattern is a wildcard
        if np.all(
            np.logical_or(
                is_wildcard, (input_match_candidate == pattern)
            )
        ):
            return True

    return False