【问题标题】:Finding Patterns in a Numpy Array在 Numpy 数组中查找模式
【发布时间】:2017-07-18 10:25:01
【问题描述】:

我试图在numpy array 中找到模式,称为values。我想返回模式的起始索引位置。我知道我可以迭代每个元素并检查该元素和下一个元素是否与模式匹配,但是在一个非常低效的大型数据集上,我正在寻找更好的替代方案。

我有一个使用 np.where 来搜索单个值的有效解决方案,但我无法使用它来查找模式或两个数字。

例子:

import numpy as np
values = np.array([0,1,2,1,2,4,5,6,1,2,1])
searchval = [1,2]
print  np.where(values == searchval)[0]

输出:

[]

预期输出:

[1, 3, 8]

【问题讨论】:

标签: python python-2.7 numpy


【解决方案1】:

我认为这样做可以:

np.where((values == 1) & (np.roll(values,-1) == 2))[0]

【讨论】:

  • 由于我们有大量的解决方案,我对运行时间感到好奇,发现 hpaulij 解决方案在相当长的随机数组(例如 1 百万条目)上比简单滚动快大约 2 倍。 Ed Smith 的解决方案慢了大约 100 倍,而 betontalpfa 的解决方案再慢了 100 倍。条目数和点击数对数字的影响很大,但对整体排名没有影响。
【解决方案2】:

你不能简单地使用np.where(假设这是查找元素的最佳方式)然后只检查满足第一个条件的模式。

import numpy as np
values = np.array([0,1,2,1,2,4,5,6,1,2,1])
searchval = [1,2]
N = len(searchval)
possibles = np.where(values == searchval[0])[0]

solns = []
for p in possibles:
    check = values[p:p+N]
    if np.all(check == searchval):
        solns.append(p)

print(solns)

【讨论】:

  • 如果输入是随机的(值有一些重复),这个解决方案会很快
【解决方案3】:

如果输入是随机的,Ed Smith 解决方案会更快。但是,如果您有一些可用值,则此哈希解决方案可以提供帮助:

"""
Can be replaced with any revertable hash
"""
def my_hash(rem, h, add):
    return rem^h^add

"""
Imput
"""
values = np.array([0,1,2,1,2,4,5,6,1,2,1])
searchval = [1,2]


"""
Prepare
"""
sh = 0
vh = 0
ls = len(searchval)
lv = len(values)

for i in range(0, len(searchval)):
    vh = my_hash(0, vh, values[i])
    sh = my_hash(0, sh, searchval[i])

"""
Find matches
"""
for i in range(0, lv-ls):
    if sh == vh:
        eq = True
        for j in range(0, ls):
            if values[i+j] != searchval[j]:
                eq = False
                break
        if eq:
            print i
    vh = my_hash(values[i], vh, values[i+ls])

【讨论】:

    【解决方案4】:

    这是使用 where 的直接方法。从找到匹配项的逻辑表达式开始:

    In [670]: values = np.array([0,1,2,1,2,4,5,6,1,2,1])
         ...: searchval = [1,2]
         ...: 
    In [671]: (values[:-1]==searchval[0]) & (values[1:]==searchval[1])
    Out[671]: array([False,  True, False,  True, False, False, False, False,  True, False], dtype=bool)
    In [672]: np.where(_)
    Out[672]: (array([1, 3, 8], dtype=int32),)
    

    这可以概括为一个在多个searchval 上运行的循环。获得正确的切片范围将需要一些摆弄。另一个答案中建议的roll 可能更容易,但我怀疑会慢一些。

    只要searchvalvalues 相比较小,这种通用方法应该是有效的。有一个np.in1d 可以进行这种匹配,但有一个or 测试。所以不适用。但它也使用了这种迭代方法,因为searchval 列表足够小。

    广义切片

    In [716]: values
    Out[716]: array([0, 1, 2, 1, 2, 4, 5, 6, 1, 2, 1])
    In [717]: searchvals=[1,2,1]
    In [718]: idx = [np.s_[i:m-n+1+i] for i in range(n)]
    In [719]: idx
    Out[719]: [slice(0, 9, None), slice(1, 10, None), slice(2, 11, None)]
    In [720]: [values[idx[i]] == searchvals[i] for i in range(n)]
    Out[720]: 
    [array([False,  True, False,  True, False, False, False, False,  True], dtype=bool),
     array([False,  True, False,  True, False, False, False, False,  True], dtype=bool),
     array([False,  True, False, False, False, False,  True, False,  True], dtype=bool)]
    In [721]: np.all(_, axis=0)
    Out[721]: array([False,  True, False, False, False, False, False, False,  True], dtype=bool)
    In [722]: np.where(_)
    Out[722]: (array([1, 8], dtype=int32),)
    

    我使用中间 np.s_ 来查看切片并确保它们看起来合理。

    as_strided

    一个高级技巧是使用as_strided 构造“滚动”数组并对其执行二维== 测试。 as_strided 很简洁但很棘手。要正确使用它,您必须了解步幅,并获得正确的形状。

    In [740]: m,n = len(values), len(searchvals)
    In [741]: values.shape
    Out[741]: (11,)
    In [742]: values.strides
    Out[742]: (4,)
    In [743]: 
    In [743]: M = as_strided(values, shape=(n,m-n+1),strides=(4,4))
    In [744]: M
    Out[744]: 
    array([[0, 1, 2, 1, 2, 4, 5, 6, 1],
           [1, 2, 1, 2, 4, 5, 6, 1, 2],
           [2, 1, 2, 4, 5, 6, 1, 2, 1]])
    In [745]: M == np.array(searchvals)[:,None]
    Out[745]: 
    array([[False,  True, False,  True, False, False, False, False,  True],
           [False,  True, False,  True, False, False, False, False,  True],
           [False,  True, False, False, False, False,  True, False,  True]], dtype=bool)
    In [746]: np.where(np.all(_,axis=0))
    Out[746]: (array([1, 8], dtype=int32),)
    

    【讨论】:

    • 切片的好主意,对于大小合适的图案应该非常有效。
    • 你可以在矩阵上使用 as_strided 吗?
    【解决方案5】:

    紧凑的 straitforward 解决方案将是 as_strided 解决方案的“合法”变体。其他人提到了np.roll。 但这里有一个通用的解决方案,只有一个圆 (132 µs)。

    seq = np.array([0,1,2,1,2,4,5,6,1,2,1])
    patt = np.array([1,2])
    
    Seq = np.vstack(np.roll(seq, shift) for shift in -np.arange(len(patt))).T
    where(all(Seq == patt, axis=1))[0]
    

    小整数序列的另一个选项是转换为字符串。 它每接近 6 倍 (20 µs) 更快。 仅适用于小的正整数!

    import re
    
    def to_string(arr):
        return ''.join(map(chr, arr))
    
    array([m.start() for m in re.finditer(to_string(patt), to_string(seq))])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-22
      • 1970-01-01
      • 2014-02-19
      • 2016-10-02
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 2013-02-21
      相关资源
      最近更新 更多