Numpy - 加速迭代比较?

时间:2015-10-03 07:22:00

标签: python numpy

以下用例:

我有一个Numpy矩阵/数组,有几千个2d点。称之为A. 例如:

[1 2]
[300 400]
..
[123 242]

我还有另一个Numpy矩阵,上面有几个2d点。称之为B

基本上,我想迭代A,然后遍历B并计算A[i]B[j]之间的距离。然后将其分配回另一个数组。我可以这样做:

for i, (x0, x1) in enumerate(zip(A[:,0],A[:,1])):
    weight_distance = 0
    for j, (p0, p1) in enumerate(zip(A[:,0],A[:,1])):
        weight_distance = weight_distance + distance((p0,p1),(x0,x1))
    weight_array[i] = weight_distance

但这太慢了。什么可能是Numpy方法来解决这个问题?

1 个答案:

答案 0 :(得分:0)

您可能正在寻找的是scipy.spatial.distance中的代码,尤其是cdist函数。这可以有效地计算各种度量的点阵列之间的成对距离。

import numpy as np
from scipy.spatial.distance import cdist

A = np.random.random((1000, 2))
B = np.random.random((100, 2))

D = cdist(A, B, metric='euclidean')
print(D.shape) # (1000, 100)

weights = D.sum(1)
print(weights.shape)  # (1000,)

此处euclidean是您可能习惯的标准根和平方距离,D[i, j]保持A[i]B[j]之间的距离,所以沿轴1求和得到所需的权重。

有很多方法可以通过直接在numpy中进行广播来实现,但是这种方法会使用几个大的临时数组,并且通常比scipy cdist方法慢。

编辑: 我想我也可以在仅限NumPy的方法上添加一个注释。它看起来像这样:

D2 = np.sqrt(((A[:, None, :] - B[None, :, :]) ** 2).sum(-1))
weights2 = D2.sum(1)
np.allclose(weights, weights2)  # True

让我们分解一下:

  • A[:, None, :]A添加了一个新维度,因此其形状现为[1000, 1, 2]。与B[None, :, :]类似,后者变为[1, 100, 2]
  • A[:, None, :] - B[None, :, :]是一种广播操作,会产生一系列差异,形状为[1000, 100, 2]
  • 我们对这个结果的每个元素都进行了调整。
  • 此结果的sum(-1)方法在最后一个维度上求和,从而生成一个形状[1000, 100]的数组
  • 我们取结果的平方根,得到距离矩阵
  • 我们沿轴1求和得到权重

请注意,这种广播方法在此过程中不会创建一个大小为1000 * 100 * 2两个临时数组,这就是为什么它比专用的编译函数(如{)效率低的原因。 {1}}。