最近读《统计学习方法》的时候用到了各种距离,这里做一个小总结,并且用numpy实现一下。
一般的距离度量使用欧氏距离,就是我们生活中最常用的距离概念。但也可以使用其他的度量方式。
-
闵可夫斯基距离( L p L_p Lp距离)(Minkowski distance)
闵可夫斯基距离也叫 L p L_p Lp距离,欧式距离是 L p L_p Lp距离的一种特殊情况。
在这里 p > = 1 p>=1 p>=1
-
当 p = 2 p=2 p=2时,称为欧氏距离(Euclidean distance)
欧氏距离就是我们最常用的距离。相当于棋子和终点在棋盘上的交叉点,棋子只能沿任意直线(不一定是棋盘上的线)走到终点这样的长度。
-
当 p = 1 p=1 p=1时,称为曼哈顿距离(Manhattan distance)
*曼哈顿距离就是每个维度的差值之和。相当于棋子和终点在棋盘上的交叉点,棋子只能沿着棋盘格子的边边走到终点这样的长度。
-
当 p = ∞ p=\infty p=∞时,称为切比雪夫距离(Chebyshev Distance)
切比雪夫距离是各个坐标距离的最大值。相当于棋子和终点在棋盘上的交叉点,棋子每步只能横跳,纵跳或斜跳到邻近的交叉点最终调到终点的步数
下图是在二维空间中取不同的
p
p
p值时,与原点的
L
p
L_p
Lp距离为1的点组成的图形:
从上图可以看到,采用不同的距离度量,最近邻的点也会不同。另外,采用闵可夫斯基距离通常需要数据进行标准化,因为不同维度的量纲会影响距离计算。
-
余弦距离
与闵可夫斯基距离不同,余弦距离表征两个向量角度的。表达式为: d i s t ( A , B ) = 1 − c o s ( A , B ) dist(A,B)=1-cos(A,B) dist(A,B)=1−cos(A,B).因此取值范围为 [ 0 , 2 ] [0,2] [0,2](非负性)。
其中 c o s ( A , B ) = ∑ i = 1 N ( A i × B i ) ( ∑ i = 1 N ( A i 2 ) ) 1 2 × ( ∑ i = 1 N ( B i 2 ) ) 1 2 cos(A,B)=\frac{\sum_{i=1}^{N}(A_i\times{B_i})}{(\sum_{i=1}^{N}(A_i^2))^{\frac{1}{2}}\times{(\sum_{i=1}^{N}(B_i^2))^{\frac{1}{2}}}} cos(A,B)=(∑i=1N(Ai2))21×(∑i=1N(Bi2))21∑i=1N(Ai×Bi)
-
马氏距离(Mahalanobis distance)
马氏距离是一种基于样本分布的距离。打比方:有一个集合和一个数据项,通过这个集合的分布来确定这个数据项距离集合总体的远近程度。
定义:有M个样本向量 { X 1 , . . . , X M } \{X_1,...,X_M\} {X1,...,XM},协方差矩阵记为 S S S,均值记为向量 μ \mu μ,则其中样本向量 x x x到 μ \mu μ的马氏距离为:
D ( x ) = ( ( X − μ ) T S − 1 ( X − μ ) ) 1 2 D(x)=((X-\mu)^TS^{-1}(X-\mu))^\frac{1}{2} D(x)=((X−μ)TS−1(X−μ))21
向量 X i X_i Xi到 X j X_j Xj的马氏距离为:
D ( x ) = ( ( X i − X j ) T S − 1 ( X i − X j ) ) 1 2 D(x)=((X_i-X_j)^TS^{-1}(X_i-X_j))^\frac{1}{2} D(x)=((Xi−Xj)TS−1(Xi−Xj))21
注意:
- 马氏距离与量纲无关,因为每个维度都是互相独立的。
- 样本集合中样本数必须大于样本维度,否则没法算协方差矩阵。
若协方差矩阵是单位矩阵(样本向量独立同分布),则向量 X i X_i Xi到 X j X_j Xj的马氏距离为他们的欧式距离。
各距离的numpy实现:
import numpy as np
def mks_distance(x1,x2,p=2):
'''
闵可夫斯基距离
'''
if len(x1) == len(x2):
ans = sum(abs(x1 - x2) ** p) ** (1./p)
return ans
else:
print('向量维度必须相等!')
def cos_distance(x1,x2):
'''
余弦距离
'''
if len(x1) == len(x2):
a = sum(x1*x2)
b = sum(x1 ** 2)**(0.5) * sum(x2 ** 2)**(0.5)
return 1-a/b
else:
print('向量维度必须相等!')
def cov(x1,x2):
'''
向量协方差
'''
n = len(x1)
if len(x2) == n:
average1 = sum(x1)/len(x1)
average2 = sum(x2)/len(x2)
ans = (1/(n-1)) * sum((x1-average1) * (x2-average2))
return ans
else:
print('向量维度必须相等!')
def ma_distance(m,x):
'''
马氏距离
m为行向量组成的集合
x为一个行向量
'''
a,b = np.shape(m)
mean_m = m.mean(axis=0)
if a>b:
if len(x) == b:
# 计算协方差矩阵
s = np.zeros((b,b))
for i in range(b):
for j in range(b):
s[i][j] = cov(m[:,i],m[:,j])
else:
print('向量维度必须等于矩阵列数')
else:
print('矩阵行数必须大于列数')
aa = np.dot((x-mean_m).T,s.T)
print(s)
ans = np.dot(aa,(x-mean_m))
return ans