![](/img/trans.png)
[英]What is the Most Efficient Way to Compute the (euclidean) Distance of the Nearest Neighbor in a List of (x,y,z) points?
[英]What is the most efficient way to compute the square euclidean distance between N samples and clusters centroids?
我正在寻找一种有效的方法( 没有循环 )来计算一组样本和一组聚类质心之间的欧氏距离。
例:
import numpy as np
X = np.array([[1,2,3],[1, 1, 1],[0, 2, 0]])
y = np.array([[1,2,3], [0, 1, 0]])
预期产量:
array([[ 0., 11.],
[ 5., 2.],
[10., 1.]])
这是X中每个样本与y中每个质心之间的欧氏距离平方。
我想出了两个解决方案:
解决方案1:
def dist_2(X,y):
X_square_sum = np.sum(np.square(X), axis = 1)
y_square_sum = np.sum(np.square(y), axis = 1)
dot_xy = np.dot(X, y.T)
X_square_sum_tile = np.tile(X_square_sum.reshape(-1, 1), (1, y.shape[0]))
y_square_sum_tile = np.tile(y_square_sum.reshape(1, -1), (X.shape[0], 1))
dist = X_square_sum_tile + y_square_sum_tile - (2 * dot_xy)
return dist
dist = dist_2(X, y)
解决方案2:
import scipy
dist = scipy.spatial.distance.cdist(X,y)**2
两种解决方案的性能(挂钟时间)
import time
X = np.random.random((100000, 50))
y = np.random.random((100, 50))
start = time.time()
dist = scipy.spatial.distance.cdist(X,y)**2
end = time.time()
print (end - start)
平均经过的挂钟时间= 0.7秒
start = time.time()
dist = dist_2(X,y)
end = time.time()
print (end - start)
平均经过的挂钟时间= 0.3秒
测试大量质心
X = np.random.random((100000, 50))
y = np.random.random((1000, 50))
“解决方案1”的平均耗时时间= 50秒(+内存问题)
“解决方案2”平均经过的挂钟时间= 6秒!!!
结论
似乎“解决方案1相对于平均经过的挂钟时间(在小数据集上)比”解决方案2“更有效,但是相对于存储器效率低。
有什么建议么?
这个问题经常与nereast-neighbor搜索结合使用。 如果是这种情况,请看一下kdtree方法 。 这将比计算欧几里德距离(内存消耗和性能)更有效。
如果不是这种情况,这里有一些可能的方法。 前两个来自Divakar的回答 。 第三个使用Numba
进行jit编译。 与前两个版本的主要区别在于他避免使用临时数组。
计算欧几里德距离的三个方法
import numpy as np
import numba as nb
# @Paul Panzer
#https://stackoverflow.com/a/42994680/4045774
def outer_sum_dot_app(A,B):
return np.add.outer((A*A).sum(axis=-1), (B*B).sum(axis=-1)) - 2*np.dot(A,B.T)
# @Divakar
#https://stackoverflow.com/a/42994680/4045774
def outer_einsum_dot_app(A,B):
return np.einsum('ij,ij->i',A,A)[:,None] + np.einsum('ij,ij->i',B,B) - 2*np.dot(A,B.T)
@nb.njit()
def calc_dist(A,B,sqrt=False):
dist=np.dot(A,B.T)
TMP_A=np.empty(A.shape[0],dtype=A.dtype)
for i in range(A.shape[0]):
sum=0.
for j in range(A.shape[1]):
sum+=A[i,j]**2
TMP_A[i]=sum
TMP_B=np.empty(B.shape[0],dtype=A.dtype)
for i in range(B.shape[0]):
sum=0.
for j in range(B.shape[1]):
sum+=B[i,j]**2
TMP_B[i]=sum
if sqrt==True:
for i in range(A.shape[0]):
for j in range(B.shape[0]):
dist[i,j]=np.sqrt(-2.*dist[i,j]+TMP_A[i]+TMP_B[j])
else:
for i in range(A.shape[0]):
for j in range(B.shape[0]):
dist[i,j]=-2.*dist[i,j]+TMP_A[i]+TMP_B[j]
return dist
计时
A = np.random.randn(10000,3)
B = np.random.randn(10000,3)
#calc_dist: 360ms first call excluded due to compilation overhead
#outer_einsum_dot_app (Divakar): 1150ms
#outer_sum_dot_app (Paul Panzer):1590ms
#dist_2: 1840ms
A = np.random.randn(1000,100)
B = np.random.randn(1000,100)
#calc_dist: 4.3 ms first call excluded due to compilation overhead
#outer_einsum_dot_app (Divakar): 13.12ms
#outer_sum_dot_app (Paul Panzer):13.2 ms
#dist_2: 21.3ms
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.