欧式距离可解释为连接两个点的线段的长度。欧式距离公式非常简单,使用勾股定理从这些点的笛卡尔坐标计算距离。
代码实现:
import numpy as np
x=np.random.random(10)
y=np.random.random(10)
#方法一:根据公式求解
d1=np.sqrt(np.sum(np.square(x-y)))
#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
X=np.vstack([x,y])
d2=pdist(X)
缺点:尽管这是一种常用的距离度量,但欧式距离并不是尺度不变的,这意味着所计算的距离可能会根据特征的单位发生倾斜。通常,在使用欧式距离度量之前,需要对数据进行归一化处理。
此外,随着数据维数的增加,欧氏距离的作用也就越小。这与维数灾难(curse of dimensionality)有关。
用例:当你拥有低维数据且向量的大小非常重要时,欧式距离的效果非常好。如果在低维数据上使用欧式距离,则如 k-NN 和 HDBSCAN 之类的方法可达到开箱即用的效果。
余弦相似度经常被用作抵消高维欧式距离问题。余弦相似度是指两个向量夹角的余弦。如果将向量归一化为长度均为 1 的向量,则向量的点积也相同。
两个方向完全相同的向量的余弦相似度为 1,而两个彼此相对的向量的余弦相似度为 - 1。注意,它们的大小并不重要,因为这是在方向上的度量。
代码实现:
import numpy as np
def bit_product_sum(x, y):
return sum([item[0] * item[1] for item in zip(x, y)])
def cosine_similarity(x, y, norm=False):
""" 计算两个向量x和y的余弦相似度 """
assert len(x) == len(y), "len(x) != len(y)"
zero_list = [0] * len(x)
if x == zero_list or y == zero_list:
return float(1) if x == y else float(0)
# method 1
res = np.array([[x[i] * y[i], x[i] * x[i], y[i] * y[i]] for i in range(len(x))])
cos = sum(res[:, 0]) / (np.sqrt(sum(res[:, 1])) * np.sqrt(sum(res[:, 2])))
# method 2
# cos = bit_product_sum(x, y) / (np.sqrt(bit_product_sum(x, x)) * np.sqrt(bit_product_sum(y, y)))
# method 3
# dot_product, square_sum_x, square_sum_y = 0, 0, 0
# for i in range(len(x)):
# dot_product += x[i] * y[i]
# square_sum_x += x[i] * x[i]
# square_sum_y += y[i] * y[i]
# cos = dot_product / (np.sqrt(square_sum_x) * np.sqrt(square_sum_y))
return 0.5 * cos + 0.5 if norm else cos # 归一化到[0, 1]区间内
if __name__ == '__main__':
print cosine_similarity([0, 0], [0, 0]) # 1.0
print cosine_similarity([1, 1], [0, 0]) # 0.0
print cosine_similarity([1, 1], [-1, -1]) # -1.0
print cosine_similarity([1, 1], [2, 2]) # 1.0
print cosine_similarity([3, 3], [4, 4]) # 1.0
print cosine_similarity([1, 2, 2, 1, 1, 1, 0], [1, 2, 2, 1, 1, 2, 1]
)
缺点:余弦相似度的一个主要缺点是没有考虑向量的大小,而只考虑它们的方向。以推荐系统为例,余弦相似度就没有考虑到不同用户之间评分尺度的差异。
用例:当我们对高维数据向量的大小不关注时,可以使用余弦相似度。对于文本分析,当数据以单词计数表示时,经常使用此度量。例如,当一个单词在一个文档中比另一个单词更频繁出现时,这并不一定意味着文档与该单词更相关。可能是文件长度不均匀或者计数的重要性不太重要。我们最好使用忽略幅度的余弦相似度。
3、汉明距离(Hamming Distance)
代码实现:
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
def dec2bin(x):
"""
将十进制数转换为二进制数,以从低到高位的列表形式
例如输入:6,输出:[0, 1, 1]
:param x: 输入十进制数
:return: 逆序二进制各位列表
"""
res = []
while x:
r, x = x % 2, x // 2
res.append(r)
return res
def hamming(a: list, b: list):
"""
计算两个二进制数的汉明距离
:param a: 输入两个二进制各位逆序列表
:param b:
:return:
"""
count = 0 # 初始化计数器
for i in range(max(len(a), len(b))):
a_bit = a[i] if i < len(a) else 0 # 提取a列表当前位
b_bit = b[i] if i < len(b) else 0 # 提取b列表当前位
count = count if a_bit == b_bit else count + 1 # 如果提取出的两个数字不相等,计数器+1
return count
return hamming(dec2bin(x), dec2bin(y))
汉明距离是两个向量之间不同值的个数。它通常用于比较两个相同长度的二进制字符串。它还可以用于字符串,通过计算不同字符的数量来比较它们之间的相似程度。
缺点:当两个向量长度不相等时,汉明距离使用起来很麻烦。当幅度是重要指标时,建议不要使用此距离指标。
用例:典型的用例包括数据通过计算机网络传输时的错误纠正 / 检测。它可以用来确定二进制字中失真的数目,作为估计误差的一种方法。此外,你还可以使用汉明距离来度量分类变量之间的距离。
4、曼哈顿距离(Manhattan Distance)
曼哈顿距离通常称为出租车距离或城市街区距离,用来计算实值向量之间的距离。想象一下均匀网格棋盘上的物体,如果它们只能移动直角,曼哈顿距离是指两个向量之间的距离,在计算距离时不涉及对角线移动。
代码实现:
import numpy as np
x=np.random.random(10)
y=np.random.random(10)
#方法一:根据公式求解
d1=np.sum(np.abs(x-y))
print('d1:',d1)
#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
X=np.vstack([x,y])
d2=pdist(X,'cityblock')[0]
print('d2:',d2)
缺点:尽管曼哈顿距离在高维数据中似乎可以工作,但它比欧式距离直观性差,尤其是在高维数据中使用时。此外,由于它可能不是最短路径,有可能比欧氏距离给出一个更高的距离值。
用例:当数据集具有离散或二进制属性时,曼哈顿距离似乎工作得很好,因为它考虑了在这些属性的值中实际可以采用的路径。以欧式距离为例,它会在两个向量之间形成一条直线,但实际上这是不可能的。
5、切比雪夫距离(Chebyshev Distance)
切比雪夫距离定义为两个向量在任意坐标维度上的最大差值。换句话说,它就是沿着一个轴的最大距离。切比雪夫距离通常被称为棋盘距离,因为国际象棋的国王从一个方格到另一个方格的最小步数等于切比雪夫距离。
代码实现:
import numpy as np
x=np.random.random(10)
y=np.random.random(10)
#方法一:根据公式求解
d1=np.max(np.abs(x-y))
#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
X=np.vstack([x,y])
d2=pdist(X,'chebyshev')
缺点:切比雪夫距离通常用于特定的用例,这使得它很难像欧氏距离或余弦相似度那样作为通用的距离度量。因此,在确定适合用例时才使用它。
用例:切比雪夫距离用于提取从一个方块移动到另一个方块所需的最小移动次数。此外,在允许无限制八向移动的游戏中,这可能是有用的方法。在实践中,切比雪夫距离经常用于仓库物流,因为它非常类似于起重机移动一个物体的时间。
6、闵氏距离(Minkowski)
闵氏距离比大多数距离度量更复杂。它是在范数向量空间(n 维实数空间)中使用的度量,这意味着它可以在一个空间中使用,在这个空间中,距离可以用一个有长度的向量来表示。
import numpy as np
x=np.random.random(10)
y=np.random.random(10)
#方法一:根据公式求解,p=2
d1=np.sqrt(np.sum(np.square(x-y)))
#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
X=np.vstack([x,y])
d2=pdist(X,'minkowski',p=2)
最有趣的一点是,我们可以使用参数 p 来操纵距离度量,使其与其他度量非常相似。常见的 p 值有:
p=1:曼哈顿距离
p=2:欧氏距离
p=∞:切比雪夫距离
缺点:闵氏距离与它们所代表的距离度量有相同的缺点,因此,对哈顿距离、欧几里得距离和切比雪夫距离等度量标准有个好的理解非常重要。此外,参数 p 的使用可能很麻烦,因为根据用例,查找正确的 p 值在计算上效率低。
用例:p 的积极一面是可迭代,并找到最适合用例的距离度量。它允许在距离度量上有很大的灵活性,如果你非常熟悉 p 和许多距离度量,将会获益多多。
7、雅卡尔指数(Jaccard Index)
雅卡尔指数(交并比)是用于比较样本集相似性与多样性的统计量。雅卡尔系数能够量度有限样本集合的相似度,其定义为两个集合交集大小与并集大小之间的比例。
例如,如果两个集合有 1 个共同的实体,而有 5 个不同的实体,那么雅卡尔指数为 1/5 = 0.2。要计算雅卡尔距离,我们只需从 1 中减去雅卡尔指数:
代码实现:
from sklearn.metrics import jaccard_score
A = [1, 1, 1, 0]
B = [1, 1, 0, 1]
jacc = jaccard_score(A,B)
print(‘Jaccard similarity: %.3f’ % jacc)
缺点:雅卡尔指数的一个主要缺点是它受数据大小的影响很大。大数据集对指数有很大影响,因为它可以显著增加并集,同时保持交集相似。
用例:雅卡尔指数通常用于使用二进制或二进制数据的应用程序中。当你有一个深度学习模型来预测图像分割时,比如一辆汽车,雅卡尔指数可以用来计算给定真实标签的预测分割的准确度。
类似地,它可以用于文本相似性分析,以测量文档之间有多少词语重叠。因此,它可以用来比较模式集合。
8、半正矢(Haversine)
半正矢距离是指球面上的两点在给定经纬度条件下的距离。它与欧几里得距离非常相似,因为它可以计算两点之间的最短连线。主要区别在于半正矢距离不可能有直线,因为这里的假设是两个点都在一个球面上。
代码实现:
import math
def geo_distance(origin, destination):
'''
>>> origin = (48.1372, 11.5756) # Munich
>>> destination = (52.5186, 13.4083) # Berlin
>>> round(distance(origin, destination), 1)
504.2
'''
lat1, lon1 = origin
lat2, lon2 = destination
radius = 6371 # km
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = (math.sin(dlat / 2) * math.sin(dlat / 2) +
math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
math.sin(dlon / 2) * math.sin(dlon / 2))
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
d = radius * c
return d
缺点:这种距离测量的一个缺点是,假定这些点位于一个球体上。实际上,这种情况很少出现,例如,地球不是完美的圆形,在某些情况下可能使计算变得困难。相反,如果假定是椭球,使用 Vincenty 距离比较好。
用例:半正矢距离通常用于导航。例如,你可以使用它来计算两个国家之间的飞行距离。请注意,如果距离本身不那么大,则不太适合。
9、Sørensen-Dice 系数
Sørensen-Dice 系数与雅卡尔指数非常相似,都是度量样本集的相似性和多样性。尽管它们的计算方法相似,但是 Sørensen-Dice 系数更直观一些,因为它可以被视为两个集合之间重叠的百分比,这个值在 0 到 1 之间:
代码实现:
import numpy as np
def dice_coef(y_true, y_pred, epsilon=1e-6):
"""Altered Sorensen–Dice coefficient with epsilon for smoothing."""
y_true_flatten = np.asarray(y_true).astype(np.bool)
y_pred_flatten = np.asarray(y_pred).astype(np.bool)
if not np.sum(y_true_flatten) + np.sum(y_pred_flatten):
return 1.0
return (2. * np.sum(y_true_flatten * y_pred_flatten)) /\
(np.sum(y_true_flatten) + np.sum(y_pred_flatten) + epsilon)
缺点:正如雅卡尔指数,Sørensen-Dice 系数也夸大了很少或没有真值的集合的重要性,因此,它可以控制多集合的平均得分,还可以控制多组平均得分并按相关集合的大小成反比地加权每个项目,而不是平等对待它们。
用例:用例与雅卡尔指数相似,它通常用于图像分割任务或文本相似性分析