常用的相似度和距离计算方法详解(python版)

目录

  • Jaccard相关系数/Jaccard 距离
    • 定义
    • 适用场景
    • 例子
    • 代码
  • 余弦相似度/余弦距离
    • 定义
    • 适用场景
    • 代码
  • 皮尔森相关系数/Pearson
    • 定义
    • 适用场景
    • 代码
  • 欧式距离
    • 定义
    • 适用场景
    • 代码
  • 曼哈顿距离
    • 定义
    • 代码
  • 汉明距离(Hamming distance)
    • 定义
    • 代码
  • 编辑距离
    • 定义
    • 代码
  • 马氏距离
    • 定义
    • 适用场景
    • 代码
  • 参考

Jaccard相关系数/Jaccard 距离

定义

给定两个集合X、Y,Jaccard 系数定义为X与Y交集的大小与X与Y并集的大小的比值,定义如下:
J a c c a r d ( X , Y ) = X ∩ Y X ∪ Y Jaccard\left ( X,Y \right )= \frac{X\cap Y}{X\cup Y} Jaccard(X,Y)=XYXY
当集合X、Y都为空时,Jaccard(X,Y)定义为1。与Jaccard 系数相关的指标叫做Jaccard 距离,用于描述集合之间的不相似度。Jaccard 距离越大,样本相似度越低。公式定义如下:
d J a c c a r d ( X , Y ) = 1 − J a c c a r d ( X , Y ) d_{Jaccard}\left ( X,Y \right )=1-Jaccard\left ( X,Y \right ) dJaccard(X,Y)=1Jaccard(X,Y)
其 中 , J a c c a r d ( X , Y ) ⊂ [ 0 , 1 ] 其中,Jaccard\left ( X,Y \right )\subset [0,1] Jaccard(X,Y)[0,1]

适用场景

主要用于计算符号度量或布尔值度量的集合间的相似度,因为特征属性都是由符号度量或者布尔值标识,因此无法衡量差异具体值的大小,只能获得“是否相同”这个结果。所以Jaccard系数适合只关心集合中特征存不存在,不关心具体值大小。

例子

1、如果A、B是两个集合,A={1,2,3,4};B={3,4,5,6};
那么他们的J(X,Y) = {3,4}个数 / {1,2,3,4,5,6}个数 = 1/3。
2、假设样本A与样本B是两个n维向量,而且所有维度的取值都是0或1。例如:A(0111)和B(1011)。我们将样本看成是一个集合,1表示集合包含该元素,0表示集合不包含该元素。
p:样本A与B都是1的维度的个数
q:样本A是1,样本B是0的维度的个数
r:样本A是0,样本B是1的维度的个数
s:样本A与B都是0的维度的个数
那么样本A与B的杰卡德相似系数可以表示为:
J a c c a r d = p p + q + r Jaccard= \frac{p}{p+q+r} Jaccard=p+q+rp

代码

from numpy import *
import scipy.spatial.distance as dist
data = mat([[1,1,0,1,0,1,0,0,1],[0,1,1,0,0,0,1,1,1]])
print ("dist.jaccard:", dist.pdist(data,'jaccard'))
#结果: dist.jaccard: [0.75]

余弦相似度/余弦距离

定义

余弦相似度是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。
c o s ( X , Y ) = x 1 x 2 + y 1 y 2 x 1 2 + y 1 2 + x 2 2 + y 2 2 cos\left (X,Y \right )=\frac{x_{1}x_{2}+y_{1}y_{2}}{\sqrt{x_{1}^{2}+y_{1}^{2}}+\sqrt{x_{2}^{2}+y_{2}^{2}}} cos(X,Y)=x12+y12 +x22+y22 x1x2+y1y2
余弦取值范围为[-1,1]。夹角越小,越趋近于0度,余弦值越接近于1,它们的方向更加吻合,则越相似。当两个向量的方向完全相反夹角余弦取最小值-1。当余弦值为0时,两向量正交,夹角为90度。因此可以看出,余弦相似度与向量的幅值无关,只与向量的方向相关。与余弦相似度相关的指标叫做余弦距离,定义如下:
d c o s ( X , Y ) = 1 − c o s ( X , Y ) d_{cos}\left ( X,Y \right )=1-cos\left ( X,Y \right ) dcos(X,Y)=1cos(X,Y)

适用场景

这里和欧式距离比较着来说,欧式距离体现数值上的据对差异,而余弦距离体现方向上的相对差异。例如:分析两个用户对于不同视频的偏好,更关注相对差异,显然应当使用余弦距离;而当我们分析用户活跃程度,以登录次数和平均观看时长作为特征时,余弦会人为(1,10)和(10,100)两个用户关系很近,但显然两个用户的活跃程度有着极大差异,因此我们更关注数值绝对差异,应当使用欧式距离。

代码

使用sklearn库求解:

from sklearn.metrics.pairwise import cosine_similarity
data = [[1, 3, 2], [2, 2, 1]]
print(cosine_similarity(data))
#结果:[[1.         0.89087081]
#     [0.89087081 1.        ]]
#计算过程:(2+6+2)/sqrt(1+9+4)/sqrt(4+4+1)=0.89087081

自己写公式求解:

import numpy as np
X=np.random.random(10)
Y=np.random.random(10)
d1=np.dot(X,Y)/(np.linalg.norm(X)*np.linalg.norm(Y))
# 输出结果:0.8484677465149598

使用scipy库求解

from scipy.spatial.distance import pdist
data=np.vstack([X,Y])
d2=1-pdist(data,'cosine')
# 输出结果:[0.84846775]

皮尔森相关系数/Pearson

定义

两个连续变量(X,Y)的pearson相关性系数等于它们之间的协方差cov(X,Y)除以它们各自标准差的乘积(σX,σY)。
在这里插入图片描述
常用的相似度和距离计算方法详解(python版)_第1张图片
皮尔森相关系数取值范围为[-1,1],接近0的变量被成为无相关性,接近1或者-1被称为具有强相关性。

适用场景

这里又要和欧式距离一起说了,欧式距离无法考虑不同量纲所带来的差异,而Pearson相关性系数可以看作是升级版的欧式距离,因为它提供了对于变量取值范围不同的处理步骤,因此对不同变量间的取值范围没有要求。在数据标准化( [公式] )后,Pearson相关性系数、Cosine相似度、欧式距离的平方可认为是等价的。

代码

def pearson(X, Y):
    n = len(X)
    sum1 = sum(X)
    sum2 = sum(Y)
    sum1_pow = sum([pow(v, 2.0) for v in X])
    sum2_pow = sum([pow(v, 2.0) for v in Y])
    p_sum = sum(np.multiply(X,Y))
    num = p_sum - (sum1*sum2/n)
    den = math.sqrt((sum1_pow-pow(sum1, 2)/n)*(sum2_pow-pow(sum2, 2)/n))
    if den == 0:
        return 0.0
    return num/den
    
X=[1,2,3]
Y=[2,4,6]
print(pearson(X,Y))
# 结果:1.0

欧式距离

定义

两个n维向量X与 Y间的欧氏距离定义如下:
d E u c l i d e a n = ∑ k = 1 n ( x 1 k − x 2 k ) 2 d_{Euclidean} = \sqrt{ \sum_{k=1}^{n} \left ( x_{1k} - x_{2k} \right )^{2} } dEuclidean=k=1n(x1kx2k)2
欧几里德距离计算多维空间各个点的绝对距离,又称L2范数距离。同类型的还有曼哈顿距离,明可夫斯距离等。

适用场景

使用欧式距离计算距离的时候,需要消除量纲问题。因为计算是基于各维度特征的绝对数值,所以欧氏度量需要保证各维度指标在相同的刻度级别,比如对身高(cm)和体重(kg)两个单位不同的指标使用欧式距离可能使结果失效。
同时参考余弦距离的适用场景。

代码

import math
def eucliDist(X,Y):
    return math.sqrt(sum([(x - y)**2 for (x,y) in zip(X,Y)]))
X = [1,2,3,4]
Y = [0,1,2,3]
print(eucliDist(X,Y))
# 结果:2.0
import numpy as np
def eucliDist(X,Y):
    return np.sqrt(sum(np.power((X - Y), 2)))
X = np.array([1,2,3,4])
Y = np.array([0,1,2,3])
print(eucliDist(X,Y))
# 结果:2.0
import numpy as np
X = np.array([1,2,3,4])
Y = np.array([0,1,2,3])
 
#方法一:根据公式求解
d1=np.sqrt(np.sum(np.square(X-Y)))

#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
data=np.vstack([X,Y])
d2=pdist(data)
# d2结果:array([2.])

曼哈顿距离

定义

在曼哈顿要从一个十字路口开车到另外一个十字路口,驾驶距离是两点间的直线距离吗?显然不是,因为有大楼的阻碍。实际驾驶距离就是这个“曼哈顿距离”。而这也是曼哈顿距离名称的来源, 曼哈顿距离也称为城市街区距离(City Block distance)。曼哈顿距离又称L1范数距离,它与欧式距离(L2范数距离)的差别就像直角三角形两边之和与斜边的差别。在n维欧式空间中,任意两点X,Y,曼哈顿距离为:
m a n h a t t a n ( X , Y ) = ∑ i = 1 n ∣ x i − y i ∣ manhattan\left ( X,Y \right )=\sum_{i=1}^{n} \left | x_{i}- y_{i} \right | manhattan(X,Y)=i=1nxiyi

代码

import numpy as np
X = np.array([1,2,3,4])
Y = np.array([0,1,2,3])

#方法一:根据公式求解
d1=np.sum(np.abs(X-Y))

#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
X=np.vstack([X,Y])
d2=pdist(X,'cityblock')

汉明距离(Hamming distance)

定义

汉明距离又称编辑距离,其定义: 两个等长字符串s1与s2之间的汉明距离定义为将其中一个变为另外一个所需要作的最小替换次数。例如字符串“1111”与“1001”之间的汉明距离为2。

代码

# 字符串的汉明距离
def hmd4str(s1, s2):
    if len(s1) != len(s2):
        raise ValueError("False")
    return sum(s1_ != s2_ for s1_, s2_ in zip(s1, s2))
    
s1='qwer'
s2='qwar'
hmd4str(s1, s2)

编辑距离

定义

编辑距离(Edit Distance),又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大。

Levenshtein.distance(str1, str2)
计算编辑距离(也称Levenshtein距离)。是描述由一个字串转化成另一个字串最少的操作次数,在其中的操作包括插入、删除、替换。算法实现:动态规划。

Levenshtein.hamming(str1, str2)
计算汉明距离。要求str1和str2必须长度一致。是描述两个等长字串之间对应位置上不同字符的个数。

Levenshtein.ratio(str1, str2)
计算莱文斯坦比。计算公式 r = (sum – ldist) / sum, 其中sum是指str1 和 str2 字串的长度总和,ldist是类编辑距离。注意这里是类编辑距离,在类编辑距离中删除、插入依然+1,但是替换+2。

代码

Levenshtein包的安装: pip install python-Levenshtein

# -*- coding:utf-8 -*-
import Levenshtein
text1 = u'艾伦 图灵传'
text2 = u'艾伦•图灵传'
print(Levenshtein.distance(text1,text2))
# 结果:1

注:text = ‘艾伦 图灵传’, string类型,utf-8 编码下,一个中文字符用三个字节来表示,返回3。
text = u’艾伦 图灵传’,unicode格式,返回1。

马氏距离

定义

方差:方差是标准差的平方,而标准差的意义是数据集中各个点到均值点距离的平均值。反应的是数据的离散程度。
协方差:标准差与方差是描述一维数据的,当存在多维数据时,我们通常需要知道每个维数的变量中间是否存在关联。协方差就是衡量多维数据集中,变量之间相关性的统计量。比如说,一个人的身高与他的体重的关系,这就需要用协方差来衡量。如果两个变量之间的协方差为正值,则这两个变量之间存在正相关,若为负值,则为负相关。
协方差矩阵:当变量多了,超过两个变量了。那么,就用协方差矩阵来衡量这么多变量之间的相关性。
马氏距离:表示数据的协方差距离。它是一种有效的计算两个未知样本集的相似度的方法。与欧氏距离不同的是它考虑到各种特性之间的联系并且是尺度无关的(scale-invariant),即独立于测量尺度。对于一个均值为 μ = ( μ 1 , μ 2 , μ 3 , ⋯   , μ P ) T \mu =(\mu_{1}, \mu_{2}, \mu_{3}, \cdots, \mu_{P})^{T} μ=(μ1,μ2,μ3,,μP)T,协方差矩阵为S的多变量矢量 x = ( x 1 , x 2 , x 3 , ⋯   , x P ) T x =(x_{1}, x_{2}, x_{3}, \cdots, x_{P})^{T} x=(x1,x2,x3,,xP)T,其马氏距离为:
D M ( x ) = ( x − μ ) T S − 1 ( x − u ) D_{M}(x)=\sqrt{(x-\mu)^{T}S^{-1}(x-u) } DM(x)=(xμ)TS1(xu)
马氏距离也可以定义为两个服从同一分布并且其协方差矩阵为Σ的随机变量间的差异程度:
d ( x → , y → ) = ( x → − y → ) T S − 1 ( x → − y → ) d(\overrightarrow{x},\overrightarrow{y})=\sqrt{(\overrightarrow{x}-\overrightarrow{y})^{T}S^{-1}(\overrightarrow{x}-\overrightarrow{y})} d(x ,y )=(x y )TS1(x y )
如果协方差矩阵为单位矩阵,马氏距离就简化为欧式距离;如果协方差矩阵为对角阵,其也可称为正规化的马氏距离。

适用场景

马氏距离不受量纲的影响,两点之间的马氏距离与原始数据的测量单位无关;由标准化数据和中心化数据(即原始数据与均值之差)计算出的二点之间的马氏距离相同。马氏距离还可以排除变量之间的相关性的干扰。马氏距离要求样本数要大于维数,否则无法求协方差矩阵。

代码

import numpy as np
X = np.random.random(10)
Y = np.random.random(10)

data=np.vstack([X,Y])
dataT=data.T

#方法一:根据公式求解
S = np.cov(data) #两个维度之间协方差矩阵
SI = np.linalg.inv(S) #协方差矩阵的逆矩阵
n = dataT.shape[0]
d1 = []
for i in range(n):
    for j in range(i+1,n):
        delta = dataT[i]-dataT[j]
        d = np.sqrt(np.dot(np.dot(delta,SI),delta.T))
        d1.append(d)
        
#方法二:根据scipy库求解
from scipy.spatial.distance import pdist
d2 = pdist(dataT,'mahalanobis')
# 结果:
'''
array([0.88588354, 3.17697476, 1.14853538, 2.31376067, 3.10868282,
       1.65281438, 0.41092008, 1.8294644 , 0.32635925, 3.07166615,
       1.24669743, 1.4321262 , 2.29740284, 0.78296738, 0.71685516,
       0.94804578, 0.6817357 , 2.02862312, 3.57530498, 2.90275894,
       2.97256499, 2.80115097, 3.10767781, 3.34079427, 2.3738113 ,
       2.60834585, 1.59963877, 0.78082085, 1.80055314, 1.32801188,
       1.39069812, 0.77508498, 2.11985161, 0.57325816, 2.06659457,
       1.53957598, 2.77174104, 1.44149768, 2.97275917, 1.39513141,
       0.20355844, 1.46422708, 1.58856773, 0.54789011, 1.62266974])
'''

参考

马氏距离:https://blog.csdn.net/bluesliuf/article/details/88862918

你可能感兴趣的