python中numpy的欧氏距离计算

bttbmeg0  于 2021-07-09  发布在  Python
关注(0)|答案(4)|浏览(212)

我是Python新手,所以这个问题看起来很琐碎。但是,我没有找到与我类似的情况。我有一个20个节点的坐标矩阵。我想计算这个集合中所有节点对之间的欧氏距离,并将它们存储在一个成对矩阵中。例如,如果我有20个节点,我希望最终结果是一个矩阵(20,20),每对节点之间的欧氏距离值,我尝试使用for循环遍历坐标集的每个元素,并计算欧氏距离,如下所示:

ncoord=numpy.matrix('3225   318;2387    989;1228    2335;57      1569;2288  8138;3514   2350;7936   314;9888    4683;6901   1834;7515   8231;709   3701;1321    8881;2290   2350;5687   5034;760    9868;2378   7521;9025   5385;4819   5943;2917   9418;3928   9770')
n=20 
c=numpy.zeros((n,n))
for i in range(0,n):
    for j in range(i+1,n):
        c[i][j]=math.sqrt((ncoord[i][0]-ncoord[j][0])**2+(ncoord[i][1]-ncoord[j][1])**2)

然而,我得到了一个错误的“输入必须是一个方阵“。我想知道是否有人知道这里发生了什么。谢谢

5jdjgkvh

5jdjgkvh1#

除了嵌套的for循环,还有很多更快的替代方法,我将向您展示两种不同的方法--第一种是更通用的方法,它将向您介绍广播和矢量化,第二种使用更方便的scipy库函数。
1.一般方法,使用广播和矢量化
我建议做的第一件事是改用np.array而不是np.matrix。数组之所以更受欢迎有很多原因,最重要的是因为它们可以有〉2维,而且它们使元素乘法变得不那么笨拙。

import numpy as np

ncoord = np.array(ncoord)

对于数组,我们可以通过插入一个新的单元素维度和broadcasting对它进行减法运算来消除嵌套的for循环:

# indexing with None (or np.newaxis) inserts a new dimension of size 1
print(ncoord[:, :, None].shape)
# (20, 2, 1)

# by making the 'inner' dimensions equal to 1, i.e. (20, 2, 1) - (1, 2, 20),
# the subtraction is 'broadcast' over every pair of rows in ncoord
xydiff = ncoord[:, :, None] - ncoord[:, :, None].T

print(xydiff.shape)
# (20, 2, 20)

这相当于使用嵌套的for循环遍历每对行,但是要快得多!

xydiff2 = np.zeros((20, 2, 20), dtype=xydiff.dtype)
for ii in range(20):
    for jj in range(20):
        for kk in range(2):
            xydiff[ii, kk, jj] = ncoords[ii, kk] - ncoords[jj, kk]

# check that these give the same result
print(np.all(xydiff == xydiff2))
# True

剩下的我们也可以使用矢量化操作来完成:

# we square the differences and sum over the 'middle' axis, equivalent to
# computing (x_i - x_j) ** 2 + (y_i - y_j) ** 2
ssdiff = (xydiff * xydiff).sum(1)

# finally we take the square root
D = np.sqrt(ssdiff)

整件事可以在一行中完成,就像这样:

D = np.sqrt(((ncoord[:, :, None] - ncoord[:, :, None].T) ** 2).sum(1))

1.使用pdist的惰性方法
事实证明,已经有一个快速方便的函数可以用来计算所有两两之间的距离:scipy.spatial.distance.pdist.

from scipy.spatial.distance import pdist, squareform

d = pdist(ncoord)

# pdist just returns the upper triangle of the pairwise distance matrix. to get
# the whole (20, 20) array we can use squareform:

print(d.shape)
# (190,)

D2 = squareform(d)
print(D2.shape)
# (20, 20)

# check that the two methods are equivalent
print np.all(D == D2)
# True
bqjvbblv

bqjvbblv2#

for i in range(0, n):
    for j in range(i+1, n):
        c[i, j] = math.sqrt((ncoord[i, 0] - ncoord[j, 0])**2 
        + (ncoord[i, 1] - ncoord[j, 1])**2)
    • 注意**:ncoord[i, j]与Numpy * 矩阵 * 的ncoord[i][j]不同。这似乎是混淆的来源。如果ncoord是Numpy * 数组 *,则它们将给出相同的结果。

对于Numpy * matrix *,ncoord[i]返回ncoord的第i行,ncoord本身是一个形状为1 x 2的Numpy * matrix * 对象。因此,ncoord[i][j]实际上意味着:取ncoord * 的第i行,并取1 x 2矩阵的第j行,这就是j〉0时索引问题出现的地方。
关于你对c[i][j]赋值的评论,它不应该工作,至少在我构建的Numpy1.9.1中,如果你的索引ij迭代到n,它不应该工作。
顺便说一句,记得把矩阵c的转置加到它自己上。
建议使用Numpy数组而不是矩阵。请参见this post
如果您的坐标存储为Numpy数组,则成对距离可以计算为:

from scipy.spatial.distance import pdist

pairwise_distances = pdist(ncoord, metric="euclidean", p=2)

或者干脆

pairwise_distances = pdist(ncoord)

因为默认度量是"欧几里德",并且默认"p"是2。
在下面的注解中我错误地提到pdist的结果是一个n x n矩阵。要得到一个n x n矩阵,您需要执行以下操作:

from scipy.spatial.distance import pdist, squareform

pairwise_distances = squareform(pdist(ncoord))

from scipy.spatial.distance import cdist

pairwise_distances = cdist(ncoord, ncoord)
2sbarzqh

2sbarzqh3#

我想你想做的是:你说你想要一个20乘20的矩阵...但是你编码的是三角形的。
因此,我编写了一个完整的20x20矩阵。

distances = []
for i in range(len(ncoord)):
    given_i = []
    for j in range(len(ncoord)):
        d_val = math.sqrt((ncoord[i, 0]-ncoord[j,0])**2+(ncoord[i,1]-ncoord[j,1])**2)
        given_i.append(d_val)

    distances.append(given_i)

    # distances[i][j] = distance from i to j

SciPy方式:

from scipy.spatial.distance import cdist
# Isn't scipy nice - can also use pdist... works in the same way but different recall method.
distances = cdist(ncoord, ncoord, 'euclidean')
vcirk6k6

vcirk6k64#

使用你自己定制的sqrt和sqaures并不总是安全的,它们可能上溢或下溢。速度方面它们是一样的

np.hypot(
    np.subtract.outer(x, x),
    np.subtract.outer(y, y)
)

下溢

i, j = 1e-200, 1e-200
np.sqrt(i**2+j**2)
# 0.0

溢出

i, j = 1e+200, 1e+200
np.sqrt(i**2+j**2)
# inf

无下溢

i, j = 1e-200, 1e-200
np.hypot(i, j)
# 1.414213562373095e-200

无溢出

i, j = 1e+200, 1e+200
np.hypot(i, j)
# 1.414213562373095e+200

Refer

相关问题