既然您已经在使用 numpy,让我们尝试提出一个矢量化解决方案。两点之间的平方欧几里得距离由d^2 = (x2 - x1)^2 + (y2 - y1)^2 给出
A = np.array([[14.0,44],[16,47],[27,79]])
B = np.array([[14.0,46],[16,46],[18,89],[27,79],[45,127]])
A[:, 0, None] 为我们提供A 中所有点的 X 值,作为 (3, 1) 形状数组。
B[:, 0, None].T 为我们提供B 中所有点的 Y 值,作为(1, 5) 形状数组。
Numpy 可以广播这些形状,因此A[:, 0, None] - B[:, 0, None].T 给我们一个(3, 5) 形状数组,其中i, j 元素是A[i, 0] - B[0, j]。元素平方,它给了我们平方欧几里得距离公式的第一项。对A 和B 的1 列(而不是第0 列)做同样的事情,我们得到了第二个术语。
dist_sqr = (A[:, 0, None] - B[:, 0, None].T)**2 + (A[:, 1, None] - B[:, 1, None].T)**2
现在,dist_sqr[i, j] 为您提供A[i, :] 和B[j, :] 点之间的距离。
对于A中的每个点(对于每一行),包含最小距离的列表示距离B最近的点。
为了选择距离最小的列索引,我们使用np.argmin()和axis=1。
min_dist_pt = np.argmin(dist_sqr, axis=1)
这给了我们一个三元素向量array([0, 1, 3], dtype=int64)。
reordered_B = B[min_dist_pt, :]
# array([[14., 46.],
# [16., 46.],
# [27., 79.]])
这是我们想要的顺序。
现在,我们需要填写B中剩余的点。您似乎没有顺序,所以我将按照它们在B中出现的顺序填写它们。为此,我将range(num_pts) 和min_dist_pt 转换为集合,然后取集合差异。
num_pts = B.shape[0]
remaining_indices = list(set(range(num_pts)) - set(min_dist_pt))
remaining_B = B[remaining_indices, :]
# array([[ 18., 89.],
# [ 45., 127.]])
最后,我们堆叠reordered_B 和remaining_B 数组:
np.vstack((reordered_B, remaining_B))
# array([[ 14., 46.],
# [ 16., 46.],
# [ 27., 79.],
# [ 18., 89.],
# [ 45., 127.]])
一起作为一个单一的功能:
def align_by_dist_2(A, B):
A = np.asarray(A, np.float64)
B = np.asarray(B, np.float64)
dist_sqr = (A[:, 0, None] - B[:, 0, None].T)**2 + (A[:, 1, None] - B[:, 1, None].T)**2
min_dist_pt = np.argmin(dist_sqr, axis=1)
reordered_B = B[min_dist_pt, :]
num_pts = B.shape[0]
remaining_indices = list(set(range(num_pts)) - set(min_dist_pt))
remaining_B = B[remaining_indices, :]
return np.vstack((reordered_B, remaining_B))
在您的循环方法 (align_by_dist) 和我的矢量化方法 (align_by_dist_2) 之间进行一些比较:
A = np.array([[14.0,44],[16,47],[27,79]])
B = np.array([[14.0,46],[16,46],[18,89],[27,79],[45,127]])
%timeit align_by_dist(A, B)
210 µs ± 6.96 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit align_by_dist_2(A, B)
45.3 µs ± 6.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
使用矢量化方法显示了约 5 倍的加速
使用更大的数组:
A = np.random.random((100, 2))
B = np.random.random((110, 2))
%timeit align_by_dist(A, B)
189 ms ± 13.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit align_by_dist_2(A, B)
227 µs ± 27.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
使用更大的阵列,加速比更高:~800 倍!