这是一个技巧:
import numpy as np
pat = np.array(Pattern)
data = np.array(SampleTarget)
n = len(data)
m = len(pat)
k = data.strides[0] # typically 8 for float64
# data2d is a view to the original data,
# with data_2d[:-m, 6] == data_2d[1:1-m, 5] == ... == data_2d[6:, 0]
data_2d = np.lib.stride_tricks.as_strided(data, shape=(n-m+1, m), strides=(k, k))
# So you can check for matches on data[i, :] for all i
print(np.all(np.isclose(data_2d, pat), axis=1))
Output:
array([False, False, False, False, False, False, False, True, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False])
您可以使用np.where
or np.argwhere
获取匹配的索引。您可以调整atol
and rtol
的参数np.isclose
设置近似匹配的阈值。
澄清:如果您执行以下操作as_strided
欺骗data=np.arange(30)
,
then data2d
将:
array([[ 0, 1, 2, 3, 4, 5, 6],
[ 1, 2, 3, 4, 5, 6, 7],
[ 2, 3, 4, 5, 6, 7, 8],
...
[21, 22, 23, 24, 25, 26, 27],
[22, 23, 24, 25, 26, 27, 28],
[23, 24, 25, 26, 27, 28, 29]])
编辑:这是使用滑动窗口创建相同数据的视图的有效方法,而不需要额外的内存。 numpy 数组查找a[i, j]
找到内存地址为start_address + a.strides[0]*i + a.strides[1]*j
;通过设定步幅(8, 8)
,其中 8 是浮点值的大小,您可以实现滑动窗口效果。由于不同的数组元素引用相同的内存,因此最好将以此方式构造的数组视为只读。
编辑:如果您想获得比赛质量的“分数”指标,您可以这样做:
>>> np.linalg.norm(data_2d - pat, axis=1)
array([17.5, 17.4, 13.3, 20.5, 12.9, 14.9, 19.7, 0. , 17.4, 13.8, 16.9,
13.7, 19. , 10.3, 18.3, 15.2, 10.9, 22.3, 13. , 21.8, 15.2, 24.5,
14.9, 20.7])
# (numbers rounded to reduce clutter)
接近于零意味着更好的匹配。这里,norm
取差异向量的长度d=data-pat
, i.e., sqrt(d[0]**2 + ... + d[m-1]**2)
.
编辑:如果您对具有相同形状但缩放到更大或更小的值的图案感兴趣,您可以执行以下操作:
# New dataset with two occurrences of the pattern: one scaled by a factor 1.1,
# one scaled 0.5 with a bit of noise added
data_mod = data*1.1
np.random.seed(1)
data_mod[16:16+m] = pat*0.5 + np.random.uniform(-0.5, 0.5, size=m)
data_2d_mod = np.lib.stride_tricks.as_strided(
data_mod, shape=(n-m+1, m), strides=(k, k))
# pat_inv: pseudoinverse of pat vector
pat_inv = 1/(pat @ pat) * pat
# cofs: fit coefficients, shape (n1,)
cofs = data_2d_mod @ pat_inv # fit coefficients, shape (n1,)
# sum of squared residuals, shape (n1,) - zero means perfect fit
ssqr = ((data_2d_mod - cofs.reshape(-1, 1) * pat)**2).sum(axis=1)
print(f'cofs:\n{np.around(cofs, 2)}')
print(f'ssqr:\n{np.around(ssqr, 1)}')
Result:
cofs:
[-0.38 -0.14 0.4 -0.54 0.59 0.36 -0.48 1.1 -0.33 0.12 -0.06 0.18
-0.21 0.23 0.22 -0.33 0.52 -0.2 0.22 -0.35 0.6 -0.91 0.92 0.01]
ssqr:
[ 81.6 161.8 147.4 155.1 167.3 196.1 138.6 0. 97.8 103.5 85.9 59.3
57.1 54.9 58.3 29.2 0.7 198.7 217.4 201.9 266.3 235.1 242.8 361.9]
你看到了cofs[7] == 1.1
,这意味着该模式必须在相应的数据窗口上按因子 1.1 缩放才能获得最佳拟合。合身度非常完美,这一点你可以看到ssqr[7] == 0
。它还找到了另一个,cofs[16] == 0.52
(接近预期 0.5 值)和ssqr[16] == 0.7
.
其他例子:cofs[21]==-0.91
and ssqr[12]==235.1
。这意味着data_mod[12:19]
有点类似于该模式,但相反(正负交换)。这取决于您想如何处理数据;您很可能想看看cofs
0.5 到 2 范围内的值:您的搜索模式允许在数据中出现 2 倍以上或更少的情况。这应该与足够小的ssqr
values.
您可以在图中看到三个潜在的匹配项:
如果你使用ssqr
作为分数指标,请注意输入中的一系列零将导致cofs=0
and ssqr=0
.
考虑使用np.sqrt(ssqr/m)/np.abs(cofs)
作为一个指标,有两个原因。 (1) 根据相对误差进行匹配,结果为NaN
零输入情况下的值。 (2)更加直观;如果该值为 0.5,则表示数据点偏离模式值约 0.5。以下是该指标的值,使用相同的示例数据:
[ 9.1 35.3 11.6 8.8 8.3 14.8 9.4 0. 11.4 33.3 55.9 16.4
13.9 12.1 12.9 6.2 0.6 27.2 25.4 15.2 10.4 6.4 6.4 482.5]
对于比赛data_mod[21:28]
,差异度量为 6.4,大致对应于图中所示的差异。