交叉匹配两个二维数组并返回匹配的索引

Cross-matching two 2D arrays and returning the indexes of the matches

我有两个数组,AB,形状分别为 (m, 2)(n, 2)n >> m。在我的例子中,n = 8013m = 71。每个数组的每一行(x, y)表示一个点源在一张天文图像中的坐标,以像素为单位。 A 中的所有行都与 B 中的某些行具有非常接近的值,但不完全相同。在某些情况下,差异是一些小数,在其他情况下,可能是一两个整数,例如A 中的一行是 (1158, 1304.8974),其在 B 中的对应行是 (1160, 1304.6578).

我的问题是:如何找到 B 中与 A 中的元素最接近的元素的索引?

我的第一次尝试是:

matched = []
indexes = []
for k in np.arange(0, len(A)):
    idx = np.where((B[:, 0].astype(int) == A[k, 0].astype(int)) & 
                   (B[:, 1].astype(int) == A[k, 1].astype(int)))
    matched.append(B[idx])
    indexes.append(idx)

但它只返回A中行的索引,其整数与B中的元素完全相同,因此不会匹配所有项目。如果我删除 astype(int),我的匹配项会更少。

我为单行尝试的第二次尝试是:

value = A[0]
X = np.abs(B - value)
idx = np.where(X == X.min())
B[idx[0]]

但它只计算 xy 列中最接近的值,而不是两者。这意味着如果我要匹配 A 中的一行,例如 (1230, 980),以及 B 中的两行,例如 (3450, 981)(1233, 975),后者是正确匹配和预期输出,第二个实现 returns 元素 (3450, 981) 作为 (1230, 980) 的正确匹配,因为点 y = 980 更接近 y = 981y = 975.

您可以将 idx 定义的大小写更改为可接受的范围,如下所示:

idx = np.where((B[:, 0].astype(int) >= A[k, 0].astype(int) - 3) & (B[:, 0].astype(int) <= a(k,0].astype(int + 3)) & (B[:, 1].astype(int) == A[k, 1].astype(int)))

您正在寻找最常规形式的距离:欧氏。

由于您的数字是 ~10k x ~100,计算所有数字之间的距离在任何相当现代的硬件上的时间或内存方面都不会特别昂贵。如果它很昂贵,我会推荐像 scipy.spatial.KDTree 这样的东西,它可以有效地实现正确的空间排序。

获取每组点之间距离的最简单方法是使用 scipy.spatial.distance.cdist。这并不总是像“手动”计算距离那么快,但也足够快了:

dist = cdist(A, B)

dist 是一个 (m, n) 数组。您可以使用 np.argmin:

在每一行中找到最小距离的索引
idx = np.argmin(dist, axis=1)

B对应的元素是

matches = B[idx, :]

这假设您的输入数组 AB 是 numpy 数组。如果不是这种情况,请先将它们制成数组:

A = np.array(A)
B = np.array(B)

如果您想“手动”实现距离,这样会更快,您可以使用 broadcasting 来计算平方差之和的平方根:

dist = np.sqrt(sum((A.reshape(-1, 1, 2) - B.reshape(1, -1, 2))**2, axis=-1))

如果你只想找到最小值,则不需要最后的平方根,因为平方根单调递增,平方距离的最小值出现在最小距离处。