python 在另一个序列中找到最相似的子序列,当它们都是数值型的和巨大的

wfveoks0  于 2023-04-10  发布在  Python
关注(0)|答案(2)|浏览(158)

我有两个巨大的数字np.array(让我们称它们为S1S2,这样len(S1)>>len(S2)>>N,其中N是一个非常大的数字)。我希望找到S1最有可能的候选部分等于S2
简单的方法是计算S2S1的部分之间的运行差异,这将花费太长的时间(一次比较大约需要170个小时)。
我想到的另一种方法是手动创建一个窗口矩阵M,其中M的每一行iS1[i:(i+len(S2)]。然后,在这种方法下,我们可以广播一个差值运算。这也是不可行的,因为它需要很长时间(比最简单的方法要少,但仍然),而且它使用了我所有的RAM。
我们可以使用卷积来并行化它吗?我们可以使用torch/keras来做类似的事情吗?请记住,我正在寻找最佳候选者,因此某些卷积的值只需保持顺序,因此最可能的候选者将具有最小的值。

4smxwvx5

4smxwvx51#

由于您正在处理一个大型数据集,因此算法的效率远比使用更多的计算资源(例如核心)来更快地计算结果更重要。
看起来你想在更大的1D阵列中检测1D图案,同时可能具有良好的抗噪性。该操作通常可以使用相关来完成,更具体地说是cross-correlation。(FFT)。其思想是计算FFT^-1(FFT(a) * FFT(b))。这种方法也经常用于高效地计算卷积。对于高效地检测模式非常有用。事实上,傅立叶变换是冷战时期为探测核武器试验而发展起来的地震仪,自从它被应用于包括引力波探测在内的许多科学项目以来(更具体地说,检测两个黑洞的合并,这在非常巨大的数据集中留下特定的事件信号形状)。(与O(n²)中的朴素相关性相反)。
手动执行此处理相当麻烦。希望Scipy提供可以使用FFT的相关函数:scipy.signal.correlate。信号需要进行归一化以获得相关性,以提供有趣的结果。在实践中,减去平均值通常就足够了。下面是一个例子:

import scipy.signal
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# What needs to be found
needle = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1])

# The "large" dataset containing the searched pattern,but with some small noise
haystack = np.hstack([np.random.rand(3000), needle + np.random.rand(len(needle))*0.001, np.random.rand(2000)])

# Actual correlation (with a simple pseudo-normalization)
mean = haystack.mean()
corr = scipy.signal.correlate(haystack-mean, needle-mean, method='fft')

# Clean the results to make the interesting part more visible in plots
plotted = np.choose(corr<0, [corr**2, 0])

# Print the 5 best candidates
print(argpartition(corr, -5)[-5:])

plt.plot(plotted)
plt.show()

以下是结果输出:

[2331 3017 2743 2742 3016]

在这里,我们可以看到,尽管有噪声,相关性成功地在5个最佳候选中找到了模式。它的位置是3016。它实际上是这里的最佳候选,但不能保证相关性直接找到最佳候选。您需要先取K并检查每个候选以找到正确的位置。数据集越大,K越大。如果数据集很大,并且搜索的模式与数据集中的值相比可以清楚地识别,则可以将K设置为数据集的1%。可以对K优先值进行排序,以首先检查最佳候选检查可以使用L2范数np.sum((haystack_block - needle)**2)**0.5来完成。具有最佳L2范数的候选者是最佳候选者。相关性是避免计算所有可能候选者的L2范数的提示(5与上述示例中的数千相比)。

mjqavswn

mjqavswn2#

我假设你这样做是为了找到完美的匹配
我假设这一点的理由是你说:
我希望找到S1的最有可能的候选部分等于S2。

  • small 数组中的第一个值开始。
  • 创建一个 big 数组的所有索引的列表,这些索引与小数组的第一个值相匹配。这应该很快吧?让我们称这个数组为indices,它的值可能是[2,123,457,513,...]
  • 现在看看小数组中的 second 值。搜索大数组中的所有位置indices+1,并测试与第二个值的匹配。这可能更快,因为需要进行的比较相对较少。将成功的匹配写入新的较小的indices数组。
  • 现在查看小数组中的 * 第三个 * 值,依此类推。最终,当您找到单个匹配位置时,indices数组将缩小到大小1。

如果每个数组中的单个数值是0-255,你可能想把它们“聚集”成,比如说,一次4个值,以加快速度。但是如果它们是浮点数,你就不能这样做。
通常,这种方法的前几步会很慢,因为它要检查很多位置。但是(假设数字是相当随机的),每一个连续的步骤都会变得快得多。因此,决定它需要多长时间的因素是通过小数组的前几步。
这将需要与indices的最大合理尺子一样大的内存大小(您可以用下一个版本覆盖每个indices列表,因此您只需要一个副本)。

你可以并行化:

你可以给予每个并行进程一个大数组(s1)的块。你可以让块重叠len(s2)-1,但你只需要在第一次迭代中搜索每个块的前len(s1)个元素:最后几个元素只是用来检测在那里结束(但不是在那里开始)的序列。

但书

正如@Kelly Bundy在下面指出的那样,如果你没有在最终找到完美匹配的旅程中结束,这对你没有帮助。

相关问题