先说结论:
通过使用timeit
进行效率测试的结果,我们可以得出关于效率方面的结论:
Method5 (zip, math.sqrt)
> Method1 (numpy.linalg.norm)
> Method2 (scipy.spatial.distance)
> Method3 (sklearn.metrics.pairwise.euclidean_distances )
对于Method4
,由于不适用于一般情况并且通常等同于Method5
,因此我并没有真正测试它。
令人惊讶的是,Method5
实际上是最快的。而使用numpy
的Method1
,由于它在C语言中得到了很好的优化,所以像我们所预期的那样成为了第二快的方法。
对于scipy.spatial.distance
,如果你直接查看函数定义,你会发现它实际上是使用了numpy.linalg.norm
,只不过它会在实际运行numpy.linalg.norm
之前对两个输入向量进行验证。这就是它略慢于numpy.linalg.norm
的原因。
最后对于sklearn
,根据文档:
与其他计算距离的方法相比,这种公式有两个优点。首先,在处理稀疏数据时具有计算效率。其次,如果一个参数变化而另一个参数保持不变,则可以预先计算dot(x, x)和/或dot(y,y)。
然而,这并不是计算距离的最精确方法,并且该函数返回的距离矩阵可能不会完全对称,因为需要。
由于在你的问题中,你想使用一组固定的数据,因此该实现的优势没有得到体现。由于性能和精度之间的权衡,它也给出了所有方法中最差的精度。
关于精度方面,Method5
=Metho1
=Method2
>Method3
效率测试脚本:
import numpy as np
from scipy.spatial import distance
from sklearn.metrics.pairwise import euclidean_distances
import math
def eudis1(v1, v2):
return np.linalg.norm(v1-v2)
def eudis2(v1, v2):
return distance.euclidean(v1, v2)
def eudis3(v1, v2):
return euclidean_distances(v1, v2)
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))
效率测试输出:
t1: 0.654838958307
t2: 1.53977598714
t3: 6.7898791732
t5: 0.422228400305
精准度测试脚本 & 结果:
In [8]: eudis1(v1,v2)
Out[8]: 64.60650122085238
In [9]: eudis2(v1,v2)
Out[9]: 64.60650122085238
In [10]: eudis3(v1,v2)
Out[10]: array([[ 64.60650122]])
In [11]: eudis5(v1,v2)
Out[11]: 64.60650122085238
math.hypot()
。您可以使用timeit
模块轻松测试速度。 - martineaumath.hypot()
的一个可能的注意点是它只能处理二维向量,而你提到的许多其他函数可以处理三维或更高维度的向量。另一方面,如果你只需要处理二维向量,非通用内置函数可能会有速度优势。 - martineau(52, 106, 35, 12)
和(33, 153, 75, 10)
之间的欧几里得距离时,这两个是4D向量吗? - user6167676