经过一些在线调查(1,2,numpy,scipy,scikit,math),我找到了几种方法用于计算Python中的欧几里德距离:
# 1
numpy.linalg.norm(a-b)
# 2
distance.euclidean(vector1, vector2)
# 3
sklearn.metrics.pairwise.euclidean_distances
# 4
sqrt((xa-xb)^2 + (ya-yb)^2 + (za-zb)^2)
# 5
dist = [(a - b)**2 for a, b in zip(vector1, vector2)]
dist = math.sqrt(sum(dist))
# 6
math.hypot(x, y)
我想知道是否有人可以提供有关上述哪一项(或我未找到的任何其他)在效率方面被认为是最佳的洞察力的精度即可。如果有人知道任何资源,它会讨论那个也很棒的主题。
context 我感兴趣的是计算数字元组对之间的欧几里德距离,例如: (52, 106, 35, 12)
和(33, 153, 75, 10)
之间的距离。
答案 0 :(得分:12)
从使用timeit
进行效率测试的测试结果中,我们可以得出结论关于效率:
Method5 (zip, math.sqrt)
> Method1 (numpy.linalg.norm)
> Method2 (scipy.spatial.distance)
> Method3 (sklearn.metrics.pairwise.euclidean_distances )
强>
虽然我没有真正测试你的Method4
,因为它不适用于一般情况,而且通常等同于Method5
。
对于其他人,非常令人惊讶的是,Method5
是最快的。对于使用Method1
的{{1}},正如我们所期望的那样,在C中进行了大量优化,是第二快的。
对于numpy
,如果直接进入函数定义,您将看到它实际上正在使用scipy.spatial.distance
,除了它将在实际{{1}之前对两个输入向量执行验证}}。这就是为什么它比numpy.linalg.norm
略慢一些。
最后针对numpy.linalg.norm
,根据文档:
与其他计算距离的方法相比,此公式有两个优点。首先,它在处理稀疏数据时具有计算效率。其次,如果一个参数变化但另一个参数保持不变,则可以预先计算点(x,x)和/或点(y,y)。 但是,这不是进行此计算的最精确方法,此函数返回的距离矩阵可能并不是完全对称的
由于在您的问题中您希望使用一组固定的数据,因此不会反映此实现的优势。由于性能和精度之间的权衡,它在所有方法中也提供了最差的精度。
关于精确度, numpy.linalg.norm
= sklearn
= Method5
> Metho1
Method2
Method3
import numpy as np
from scipy.spatial import distance
from sklearn.metrics.pairwise import euclidean_distances
import math
# 1
def eudis1(v1, v2):
return np.linalg.norm(v1-v2)
# 2
def eudis2(v1, v2):
return distance.euclidean(v1, v2)
# 3
def eudis3(v1, v2):
return euclidean_distances(v1, v2)
# 5
def eudis5(v1, v2):
dist = [(a - b)**2 for a, b in zip(v1, v2)]
dist = math.sqrt(sum(dist))
return dist
dis1 = (52, 106, 35, 12)
dis2 = (33, 153, 75, 10)
v1, v2 = np.array(dis1), np.array(dis2)
import timeit
def wrapper(func, *args, **kwargs):
def wrapped():
return func(*args, **kwargs)
return wrapped
wrappered1 = wrapper(eudis1, v1, v2)
wrappered2 = wrapper(eudis2, v1, v2)
wrappered3 = wrapper(eudis3, v1, v2)
wrappered5 = wrapper(eudis5, v1, v2)
t1 = timeit.repeat(wrappered1, repeat=3, number=100000)
t2 = timeit.repeat(wrappered2, repeat=3, number=100000)
t3 = timeit.repeat(wrappered3, repeat=3, number=100000)
t5 = timeit.repeat(wrappered5, repeat=3, number=100000)
print('\n')
print('t1: ', sum(t1)/len(t1))
print('t2: ', sum(t2)/len(t2))
print('t3: ', sum(t3)/len(t3))
print('t5: ', sum(t5)/len(t5))
答案 1 :(得分:2)
这并不能完全回答问题,但是值得一提的是,如果您对实际的欧几里德距离不感兴趣,而只是想将欧几里德距离相互比较,则平方根就是单调函数,即x * *(1/2) 因此,如果您不希望显式距离,例如,只想知道vector1的欧几里德距离是否更接近向量列表,称为vectorlist,则可以避免使用昂贵的方法(就精度和精度而言,时间)平方根,但可以解决类似问题 min(vectorlist, key = lambda compare: sum([(a - b)**2 for a, b in zip(vector1, compare)])
答案 2 :(得分:1)
我不知道精度和速度与您提到的其他库相比如何,但您可以使用内置的math.hypot()
函数为2D矢量执行此操作:
from math import hypot
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = iter(iterable), iter(iterable)
next(b, None)
return zip(a, b)
a = (52, 106, 35, 12)
b = (33, 153, 75, 10)
dist = [hypot(p2[0]-p1[0], p2[1]-p1[1]) for p1, p2 in pairwise(tuple(zip(a, b)))]
print(dist) # -> [131.59027319676787, 105.47511554864494, 68.94925670375281]
答案 3 :(得分:0)
作为一般经验法则,请尽可能坚持scipy
和numpy
实现,因为它们会进行矢量化并且比本机Python代码快得多。 (主要原因是:在C中实现,向量化消除了循环所做的类型检查开销。)
(旁白:我的回答并不包括这里的精确度,但我认为同样的原则适用于效率的精确度。)
作为一个额外的奖励,我将提供一些关于如何分析代码,衡量效率的信息。如果您正在使用IPython解释器,那么秘诀就是使用%prun
行魔法。
In [1]: import numpy
In [2]: from scipy.spatial import distance
In [3]: c1 = numpy.array((52, 106, 35, 12))
In [4]: c2 = numpy.array((33, 153, 75, 10))
In [5]: %prun distance.euclidean(c1, c2)
35 function calls in 0.000 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 linalg.py:1976(norm)
1 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.dot}
6 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.array}
4 0.000 0.000 0.000 0.000 numeric.py:406(asarray)
1 0.000 0.000 0.000 0.000 distance.py:232(euclidean)
2 0.000 0.000 0.000 0.000 distance.py:152(_validate_vector)
2 0.000 0.000 0.000 0.000 shape_base.py:9(atleast_1d)
1 0.000 0.000 0.000 0.000 misc.py:11(norm)
1 0.000 0.000 0.000 0.000 function_base.py:605(asarray_chkfinite)
2 0.000 0.000 0.000 0.000 numeric.py:476(asanyarray)
1 0.000 0.000 0.000 0.000 {method 'ravel' of 'numpy.ndarray' objects}
1 0.000 0.000 0.000 0.000 linalg.py:111(isComplexType)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
2 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass}
4 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {method 'squeeze' of 'numpy.ndarray' objects}
In [6]: %prun numpy.linalg.norm(c1 - c2)
10 function calls in 0.000 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 linalg.py:1976(norm)
1 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.dot}
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 numeric.py:406(asarray)
1 0.000 0.000 0.000 0.000 {method 'ravel' of 'numpy.ndarray' objects}
1 0.000 0.000 0.000 0.000 linalg.py:111(isComplexType)
1 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass}
1 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.array}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
%prun
的作用是告诉你函数调用运行了多长时间,包括一些跟踪以找出瓶颈可能的位置。在这种情况下,scipy.spatial.distance.euclidean
和numpy.linalg.norm
实现都非常快。假设您定义了一个函数dist(vect1, vect2)
,您可以使用相同的IPython魔术调用进行概要分析。作为另一个额外的奖励,%prun
也适用于Jupyter笔记本,你可以%%prun
来分析整个代码单元,而不仅仅是一个函数,只需将%%prun
作为第一个那个细胞的一行。
答案 4 :(得分:0)
这里是有关如何仅使用numpy的示例。
import numpy as np
a = np.array([3, 0])
b = np.array([0, 4])
c = np.sqrt(np.sum(((a - b) ** 2)))
# c == 5.0