假设我有一个numpy数组[5,7,2,3,4,6],我选择子序列的长度为3.
我希望得到这些子序列的欧氏距离。
可能的后续步骤是:
子序列1和3之间的距离将被计算为(5-2)^ 2 +(7-3)^ 2 +(2-4)^ 2。我希望对所有子序列都这样做。
有没有办法避免循环?
我的真实阵列很长,所以解决方案也应该是内存效率。
编辑>
详细说明:我有一个大小为10 ^ 5到10 ^ 8个元素的时间序列
时间序列正在增长。每次添加新点时,我需要获取L个最新点,并在数据集的过去点中找到与这些点最接近的匹配。 (但我希望所有距离值不仅能找到最接近的匹配)
重复整个计算是不必要的。可以更新“先前最新的L点”的距离,并且仅通过减去年龄L + 1并添加年龄0(最新的)点来修改。
E.g。假设时间序列的大小目前是100而L = 10。我计算子序列A [90:100]到所有先前子序列的距离。当第101个点到达时,我可以重复使用距离,只通过添加距离时间序列第101个点的距离和第90个点的减去正方形来更新它们。
编辑2>
非常感谢这些想法,看起来像魔术。我还有一个想法可能是有效的,尤其是在添加tiem系列的新元素时的在线时间序列中。
我正在考虑这种更新距离的方法。为了计算长度L = 4的第一子序列到矩阵的距离,我们需要具有以下矩阵的前4列(顶部和底部的三角形可以被省略)。然后将距离平方并用颜色显示求和。
为了获得L = 4的第二个子序列的距离,我们实际上可以重复使用先前计算的距离并从它们中减去第一列(平方)并添加第4列(平方)。对于L = 4,它可能没有意义但是对于L = 100它可能。必须从头开始计算一个距离。 (实际上,如果时间序列的大小增加,则必须计算2个。)
这样我就可以在内存中保留一个子序列的距离并更新它们以获得下一个子序列的距离。
你认为这会对numpy有效吗?有没有一种简单的方法来实现它?
答案 0 :(得分:4)
假设A
为输入数组,L
为子序列的长度,您可以使用broadcasting
获得A
的滑动2D数组版本,然后使用{{ 3}},就像这样 -
# Get sliding 2D array version of input array
A2D = A[np.arange(A.size-L+1)[:,None] + np.arange(L)]
# Get pairwise distances with pdist
pairwise_dist = pdist(A2D,'sqeuclidean')
请注意,如果您指的是欧几里德距离,则需要将'sqeuclidean'
替换为'euclidean'
,或者忽略该参数,因为它是默认参数。
示例运行 -
In [209]: # Inputs
...: A = np.array([5,7,2,3,4,6])
...: L = 3
...:
In [210]: A2D = A[np.arange(A.size-L+1)[:,None] + np.arange(L)]
In [211]: A2D
Out[211]:
array([[5, 7, 2],
[7, 2, 3],
[2, 3, 4],
[3, 4, 6]])
In [212]: pdist(A2D,'sqeuclidean')
Out[212]: array([ 30., 29., 29., 27., 29., 6.])
# [1] element (= 29) is (5-2)^2 + (7-3)^2 + (2-4)^2
要获取相应的ID,您可以使用pdist
from scipy.spatial.distance,如此 -
idx1,idx2 = np.triu_indices(A2D.shape[0],1)
最后,像这样的距离显示ID -
ID_dist = np.column_stack((idx1,idx2,pairwise_dist))
示例运行 -
In [201]: idx1,idx2
Out[201]: (array([0, 0, 0, 1, 1, 2]), array([1, 2, 3, 2, 3, 3]))
In [202]: np.column_stack((idx1,idx2,pairwise_dist))
Out[202]:
array([[ 0., 1., 30.],
[ 0., 2., 29.], # This was your (5-2)^2 + (7-3)^2 + (2-4)^2
[ 0., 3., 29.],
[ 1., 2., 27.],
[ 1., 3., 29.],
[ 2., 3., 6.]])
对于案例,当您在A
和L
中处理数百万个元素时,数百个,对于循环中此类子序列的每个成对差异执行计算可能更好一点,就像这样 -
# Get pairiwise IDs
idx1,idx2 = np.triu_indices(A.size-L+1,1)
# Store range array for L as would be used frequently in loop
R = np.arange(L)
# Initialize output array and start computing
pairwise_dist = np.empty(len(idx1))
for i in range(len(idx1)):
pairwise_dist[i] = ((A[R+idx2[i]] - A[R+idx1[i]])**2).sum()
您还可以使用np.einsum
在每次迭代时获取平方的求和值,如下所示 -
diffs = A[R+idx2[i]] - A[R+idx1[i]]
pairwise_dist[i] = np.einsum('i,i->',diffs,diffs)