两个不同Numpy数组中的点之间的最小欧几里德距离,不在

7bsow1i6  于 2022-11-10  发布在  其他
关注(0)|答案(9)|浏览(150)

我有两个x-y坐标数组,我想找出一个数组中的每个点与另一个数组中的所有点之间的最小欧几里得距离。这些数组的大小不必相同。例如:

xy1=numpy.array(
[[  243,  3173],
[  525,  2997]])

xy2=numpy.array(
[[ 682, 2644],
[ 277, 2651],
[ 396, 2640]])

我当前的方法遍历xy1中的每个坐标xy,并计算该坐标与其他坐标之间的距离。

mindist=numpy.zeros(len(xy1))
minid=numpy.zeros(len(xy1))

for i,xy in enumerate(xy1):
    dists=numpy.sqrt(numpy.sum((xy-xy2)**2,axis=1))
    mindist[i],minid[i]=dists.min(),dists.argmin()

有没有办法消除for循环,并以某种方式在两个数组之间进行逐个元素的计算?我设想生成一个距离矩阵,它可以找到每行或每列中的最小元素。
另一种看待这个问题的方式。假设我将xy1(长度m)和xy2(长度p)连接成xy(长度n),然后存储原始数组的长度。从理论上讲,我应该能够从这些坐标生成一个n x n距离矩阵,从中我可以获得一个m x p子矩阵。有没有一种方法可以有效地生成这个子矩阵?

p5fdfcr1

p5fdfcr11#

(几个月后)scipy.spatial.distance.cdist( X, Y )给出了所有的距离对,对于X和Y,2暗,3暗...
它还做了22种不同的规范,详细的here


# cdist example: (nx,dim) (ny,dim) -> (nx,ny)

from __future__ import division
import sys
import numpy as np
from scipy.spatial.distance import cdist

# ...............................................................................

dim = 10
nx = 1000
ny = 100
metric = "euclidean"
seed = 1

    # change these params in sh or ipython: run this.py dim=3 ...
for arg in sys.argv[1:]:
    exec( arg )
np.random.seed(seed)
np.set_printoptions( 2, threshold=100, edgeitems=10, suppress=True )

title = "%s  dim %d  nx %d  ny %d  metric %s" % (
        __file__, dim, nx, ny, metric )
print "\n", title

# ...............................................................................

X = np.random.uniform( 0, 1, size=(nx,dim) )
Y = np.random.uniform( 0, 1, size=(ny,dim) )
dist = cdist( X, Y, metric=metric )  # -> (nx, ny) distances

# ...............................................................................

print "scipy.spatial.distance.cdist: X %s Y %s -> %s" % (
        X.shape, Y.shape, dist.shape )
print "dist average %.3g +- %.2g" % (dist.mean(), dist.std())
print "check: dist[0,3] %.3g == cdist( [X[0]], [Y[3]] ) %.3g" % (
        dist[0,3], cdist( [X[0]], [Y[3]] ))

# (trivia: how do pairwise distances between uniform-random points in the unit cube

# depend on the metric ? With the right scaling, not much at all:

# L1 / dim      ~ .33 +- .2/sqrt dim

# L2 / sqrt dim ~ .4 +- .2/sqrt dim

# Lmax / 2      ~ .4 +- .2/sqrt dim
l3zydbqr

l3zydbqr2#

要计算距离的m乘p矩阵,应该是这样的:

>>> def distances(xy1, xy2):
...   d0 = numpy.subtract.outer(xy1[:,0], xy2[:,0])
...   d1 = numpy.subtract.outer(xy1[:,1], xy2[:,1])
...   return numpy.hypot(d0, d1)

.outer调用生成两个这样的矩阵(沿两个轴的标量差),.hypot调用将它们转换成一个形状相同的矩阵(标量欧几里得距离)。

ohfgkhjo

ohfgkhjo3#

被接受的答案没有完全解决这个问题,该问题要求找出两个点集合之间的最小距离,而不是两个集合中每个点之间的距离。
虽然原始问题的直接解决方案确实包括计算每一对之间的距离,然后找到最小的一对,但如果一个人只对最小距离感兴趣,那么这并不是必要的。对于后一个问题,存在一个更快的解决方案。
所有建议的解决方案的运行时间都可以扩展为m*p = len(xy1)*len(xy2)。这对于小型数据集是可以的,但是可以编写可扩展为m*log(p)的最佳解决方案,从而为大型xy2数据集带来巨大的节省。
使用scipy.spatial.KDTree可以实现这种最佳的执行时间扩展,如下所示

import numpy as np
from scipy import spatial

xy1 = np.array(
    [[243,  3173],
     [525,  2997]])

xy2 = np.array(
    [[682, 2644],
     [277, 2651],
     [396, 2640]])

# This solution is optimal when xy2 is very large

tree = spatial.KDTree(xy2)
mindist, minid = tree.query(xy1)
print(mindist)

# This solution by @denis is OK for small xy2

mindist = np.min(spatial.distance.cdist(xy1, xy2), axis=1)
print(mindist)

其中mindistxy1中的每个点与xy2中的点集之间的最小距离

62lalag4

62lalag44#

为了你想要做的事:

dists = numpy.sqrt((xy1[:, 0, numpy.newaxis] - xy2[:, 0])**2 + (xy1[:, 1, numpy.newaxis - xy2[:, 1])**2)
mindist = numpy.min(dists, axis=1)
minid = numpy.argmin(dists, axis=1)
  • 编辑*:不调用sqrt、做正方形等,可以使用numpy.hypot
dists = numpy.hypot(xy1[:, 0, numpy.newaxis]-xy2[:, 0], xy1[:, 1, numpy.newaxis]-xy2[:, 1])
unftdfkk

unftdfkk5#

import numpy as np
P = np.add.outer(np.sum(xy1**2, axis=1), np.sum(xy2**2, axis=1))
N = np.dot(xy1, xy2.T)
dists = np.sqrt(P - 2*N)
jaql4c8m

jaql4c8m6#

我认为下面的功能也适用。

import numpy as np
from typing import Optional
def pairwise_dist(X: np.ndarray, Y: Optional[np.ndarray] = None) -> np.ndarray:
    Y = X if Y is None else Y
    xx = (X**2).sum(axis = 1)[:, None]
    yy = (Y**2).sum(axis = 1)[:, None]
    return xx + yy.T - 2 * (X @ Y.T)

说明

假设XY的每一行都是两组点的坐标。
让它们的大小分别为m X pp X n
结果将产生一个大小为m X n的数值数组,其中(i, j)第x项分别是XYi行和j行之间的距离。

qeeaahzv

qeeaahzv7#

我强烈推荐使用一些内置的方法来计算平方和根,因为它们是为优化计算方式而定制的,并且非常安全,防止溢出。
@Alex Answer下面是溢出方面最安全的,也应该是非常快的。此外,对于单点,您可以使用math.supt,它现在支持2个以上的维度。

>>> def distances(xy1, xy2):
...   d0 = numpy.subtract.outer(xy1[:,0], xy2[:,0])
...   d1 = numpy.subtract.outer(xy1[:,1], xy2[:,1])
...   return numpy.hypot(d0, d1)

安全隐患

i, j, k = 1e+200, 1e+200, 1e+200
math.hypot(i, j, k)

# np.hypot for 2d points

# 1.7320508075688773e+200
np.sqrt(np.sum((np.array([i, j, k]))**2))

# RuntimeWarning: overflow encountered in square

overflow/underflow/speeds

zed5wv10

zed5wv108#

我认为最直接高效**的解决方案是这样做:

distances = np.linalg.norm(xy1, xy2) # calculate the euclidean distances between the test point and the training features.
min_dist = numpy.min(dists, axis=1) # get the minimum distance 
min_id = np.argmi(distances) # get the index of the class with the minimum distance, i.e., the minimum difference.
68bkxrlz

68bkxrlz9#

虽然这里的许多答案都很棒,但还有一种方法没有在这里提到,它使用numpy矢量化/广播属性来计算两个不同长度的不同数组的每个点之间的距离(如果需要,还可以计算最接近的匹配)。我在这里发布它是因为它可以非常方便地掌握广播,并且在保持非常高效的同时也有效地解决了这个问题。
假设您有两个如下所示的数组:


# two arrays of different length, but with the same dimension

a = np.random.randn(6,2)
b = np.random.randn(4,2)

您不能执行操作a-b:NumPy使用operands could not be broadcast together with shapes (6,2) (4,2)抱怨。允许广播的诀窍是手动为NumPy添加一个要一起广播的维度。通过将维度2保留在两个重塑后的数组中,NumPy知道它必须在该维度上执行操作。

deltas = a.reshape(6, 1, 2) - b.reshape(1, 4, 2)

# contains the distance between each points

distance_matrix = (deltas**2).sum(axis=2)

distance_matrix的形状为(6,4):对于a中的每个点,计算到b中所有点的距离。然后,如果您想要“一个数组中的每个点与另一个数组中的所有点之间的最小欧几里得距离”,您可以这样做:

distance_matrix.argmin(axis=1)

这将返回b中最接近a的每个点的点的索引。

相关问题