首页 文章

两个不同Numpy阵列中点之间的最小欧几里德距离,而不是在

提问于
浏览
36

我有两个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×n距离矩阵,我可以从中获取一个m×p子矩阵 . 有没有办法有效地生成这个子矩阵?

5 回答

  • 4

    (几个月后) scipy.spatial.distance.cdist( X, Y ) 给出所有距离对,X和Y 2 dim,3 dim ......
    它还有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
    
  • 5

    要计算m乘以距离的矩阵,这应该工作:

    >>> 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 调用将它们变成相同形状的矩阵(标量欧氏距离) .

  • 2

    接受的答案并没有完全解决这个问题,它要求找到两组点之间的距离,而不是两组中的点之间的距离 . {1844518_} .

    尽管原始问题的直接解决方案确实包括计算 every 对之间的距离并随后找到最小值,但如果只对 minimum 距离感兴趣,则不需要这样做 . 对于后一个问题,存在更快的解决方案 .

    所有提出的解决方案都有一个运行时间,可以扩展为 m*p = len(xy1)*len(xy2) . 对于小型数据集来说这是可以的,但是可以编写一个最佳解决方案,可以扩展为 m*log(p) ,从而为大型数据集节省了大量资金 .

    可以使用scipy.spatial.cKDTree如下实现该最佳执行时间缩放

    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.cKDTree(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 中的点集之间的最小距离

  • 37

    对于你想要做的事情:

    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)
    

    编辑:您可以使用 numpy.hypot 代替调用 sqrt ,做方格等 .

    dists = numpy.hypot(xy1[:, 0, numpy.newaxis]-xy2[:, 0], xy1[:, 1, numpy.newaxis]-xy2[:, 1])
    
  • 21
    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)
    

相关问题