在数据科学、机器学习和人工智能领域中,"距离"这个概念扮演着至关重要的角色。它不仅是我们理解数据之间关系的基础,也是许多算法的核心组成部分。从最简单的k-近邻算法到复杂的聚类分析,从推荐系统到异常检测,距离度量无处不在。
但是,什么是"距离"?在数学和计算机科学中,距离并不仅仅是我们日常生活中所理解的物理距离。它是一种量化两个数据点之间差异或相似度的方法。根据数据的性质和分析的目的,我们可以选择不同的距离度量方法。
本文将深入探讨十种常用的距离度量方法:欧氏距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、标准化欧氏距离、马氏距离、余弦距离、汉明距离、杰卡德距离和相关距离。我们将详细解释每种方法的数学原理,并通过直观的二维可视化来帮助读者理解这些概念。
1. 欧氏距离 (Euclidean Distance)
欧氏距离是最常用的距离度量方法之一,它衡量的是多维空间中两点之间的直线距离。
原理
对于二维平面上的两点 和 ,欧氏距离定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def euclidean_distance(p1, p2):
return np.sqrt(np.sum((p1 - p2)**2))
# 创建数据点
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([2, 2])
p2 = np.array([8, 8])
# 计算到p1的距离
Z1 = np.sqrt((X - p1[0])**2 + (Y - p1[1])**2)
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z1, levels=20, cmap='viridis')
plt.colorbar(label='Distance from p1')
plt.plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
plt.plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'r--', linewidth=2)
plt.title('Euclidean Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
print(f"Euclidean distance between p1 and p2: {euclidean_distance(p1, p2):.2f}")
这段代码创建了一个等高线图,展示了平面上所有点到点p1的欧氏距离。颜色越深表示距离越近。红点表示p1,蓝点表示p2,红色虚线表示它们之间的欧氏距离。
2. 曼哈顿距离 (Manhattan Distance)
曼哈顿距离也称为城市街区距离,它计算的是沿坐标轴方向的距离总和。
原理
对于二维平面上的两点 和 ,曼哈顿距离定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def manhattan_distance(p1, p2):
return np.sum(np.abs(p1 - p2))
# 创建数据点
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([2, 2])
p2 = np.array([8, 8])
# 计算到p1的距离
Z1 = np.abs(X - p1[0]) + np.abs(Y - p1[1])
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z1, levels=20, cmap='viridis')
plt.colorbar(label='Distance from p1')
plt.plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
plt.plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
plt.plot([p1[0], p2[0], p2[0]], [p1[1], p1[1], p2[1]], 'r--', linewidth=2)
plt.title('Manhattan Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
print(f"Manhattan distance between p1 and p2: {manhattan_distance(p1, p2):.2f}")
这个可视化展示了曼哈顿距离的特征。等高线呈现菱形形状,表示到p1的曼哈顿距离相等的点。红色虚线表示从p1到p2的曼哈顿路径。
3. 切比雪夫距离 (Chebyshev Distance)
切比雪夫距离也称为棋盘距离,它衡量的是两点之间的最大坐标差。
原理
对于二维平面上的两点 和 ,切比雪夫距离定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def chebyshev_distance(p1, p2):
return np.max(np.abs(p1 - p2))
# 创建数据点
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([2, 2])
p2 = np.array([8, 8])
# 计算到p1的距离
Z1 = np.maximum(np.abs(X - p1[0]), np.abs(Y - p1[1]))
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z1, levels=20, cmap='viridis')
plt.colorbar(label='Distance from p1')
plt.plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
plt.plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'r--', linewidth=2)
plt.title('Chebyshev Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
print(f"Chebyshev distance between p1 and p2: {chebyshev_distance(p1, p2):.2f}")
这个可视化展示了切比雪夫距离的特征。等高线呈现正方形形状,表示到p1的切比雪夫距离相等的点。红色虚线表示p1和p2之间的直线距离,但实际的切比雪夫距离是坐标差的最大值。
4. 闵可夫斯基距离 (Minkowski Distance)
闵可夫斯基距离是欧氏距离、曼哈顿距离和切比雪夫距离的一般化形式。
原理
对于二维平面上的两点 和 ,闵可夫斯基距离定义为:
其中p是参数。当p=1时,它等同于曼哈顿距离;当p=2时,它等同于欧氏距离;当p趋近于无穷大时,它等同于切比雪夫距离。
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def minkowski_distance(p1, p2, p):
return np.sum(np.abs(p1 - p2)**p)**(1/p)
# 创建数据点
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([2, 2])
p2 = np.array([8, 8])
# 计算不同p值的距离
p_values = [1, 2, 10]
fig, axs = plt.subplots(1, 3, figsize=(20, 6))
for i, p in enumerate(p_values):
Z = ((np.abs(X - p1[0])**p + np.abs(Y - p1[1])**p)**(1/p))
axs[i].contourf(X, Y, Z, levels=20, cmap='viridis')
axs[i].plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
axs[i].plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
axs[i].plot([p1[0], p2[0]], [p1[1], p2[1]], 'r--', linewidth=2)
axs[i].set_title(f'Minkowski Distance (p={p})')
axs[i].set_xlabel('X')
axs[i].set_ylabel('Y')
axs[i].legend()
plt.tight_layout()
plt.show()
for p in p_values:
print(f"Minkowski distance (p={p}) between p1 and p2: {minkowski_distance(p1, p2, p):.2f}")
这个可视化展示了不同p值下的闵可夫斯基距离。你可以看到当p=1时,等高线呈菱形(曼哈顿距离);当p=2时,等高线呈圆形(欧氏距离);当p很大时,等高线接近正方形(切比雪夫距离)。
5. 标准化欧氏距离 (Standardized Euclidean Distance)
标准化欧氏距离考虑了各个特征的尺度差异,通过标准差进行归一化。
原理
对于二维平面上的两点 和 ,标准化欧氏距离定义为:
其中 和 分别是 x 和 y 维度的标准差。
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def standardized_euclidean_distance(p1, p2, std_dev):
return np.sqrt(np.sum(((p1 - p2) / std_dev)**2))
# 创建数据点
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([2, 2])
p2 = np.array([8, 8])
# 假设的标准差
std_dev = np.array([2, 1])
# 计算到p1的距离
Z = np.sqrt(((X - p1[0]) / std_dev[0])**2 + ((Y - p1[1]) / std_dev[1])**2)
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(label='Standardized Distance from p1')
plt.plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
plt.plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'r--', linewidth=2)
plt.title('Standardized Euclidean Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
print(f"Standardized Euclidean distance between p1 and p2: {standardized_euclidean_distance(p1, p2, std_dev):.2f}")
这个可视化展示了标准化欧氏距离如何考虑特征的尺度差异。等高线呈现椭圆形,反映了x和y维度的不同标准差。
6. 马氏距离 (Mahalanobis Distance)
马氏距离考虑了特征之间的相关性,是标准化欧氏距离的进一步推广。
原理
对于二维向量 和 ,马氏距离定义为:
其中 是协方差矩阵。
可视化代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
def mahalanobis_distance(p1, p2, cov):
diff = p1 - p2
return np.sqrt(diff.T @ np.linalg.inv(cov) @ diff)
# 创建数据点
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
# 选择两个点
p1 = np.array([0, 0])
p2 = np.array([2, 2])
# 定义协方差矩阵
cov = np.array([[2, 1], [1, 2]])
# 计算马氏距离
pos = np.dstack((X, Y))
rv = multivariate_normal(p1, cov)
Z = rv.pdf(pos)
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(label='Probability Density')
plt.plot(p1[0], p1[1], 'ro', markersize=10, label='p1')
plt.plot(p2[0], p2[1], 'bo', markersize=10, label='p2')
plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'r--', linewidth=2)
plt.title('Mahalanobis Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
print(f"Mahalanobis distance between p1 and p2: {mahalanobis_distance(p1, p2, cov):.2f}")
这个可视化展示了马氏距离如何考虑特征之间的相关性。等高线表示概率密度,反映了数据的分布特征。
7. 余弦距离 (Cosine Distance)
余弦距离衡量的是两个向量之间的夹角,常用于文本分析和推荐系统。
原理
对于两个向量 和 ,余弦相似度定义为:
余弦距离则定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def cosine_similarity(p1, p2):
return np.dot(p1, p2) / (np.linalg.norm(p1) * np.linalg.norm(p2))
def cosine_distance(p1, p2):
return 1 - cosine_similarity(p1, p2)
# 创建数据点
theta = np.linspace(0, 2*np.pi, 100)
r = np.linspace(0, 1, 50)
R, Theta = np.meshgrid(r, theta)
X = R * np.cos(Theta)
Y = R * np.sin(Theta)
# 选择两个向量
v1 = np.array([0.8, 0.6])
v2 = np.array([0.6, -0.8])
# 计算余弦距离
Z = np.zeros_like(X)
for i in range(X.shape[0]):
for j in range(X.shape[1]):
p = np.array([X[i,j], Y[i,j]])
Z[i,j] = cosine_distance(p, v1)
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(label='Cosine Distance from v1')
plt.quiver(0, 0, v1[0], v1[1], color='r', scale=5, label='v1')
plt.quiver(0, 0, v2[0], v2[1], color='b', scale=5, label='v2')
plt.title('Cosine Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.axis('equal')
plt.show()
print(f"Cosine distance between v1 and v2: {cosine_distance(v1, v2):.2f}")
这个可视化展示了余弦距离如何衡量向量之间的角度差异。等高线表示到v1的余弦距离,箭头表示两个向量的方向。
8. 汉明距离 (Hamming Distance)
汉明距离用于衡量两个等长字符串之间对应位置的不同字符的个数。虽然它通常用于字符串,但我们可以用二维平面上的二进制网格来可视化它。
原理
对于两个等长字符串 和 ,汉明距离定义为:
其中 表示当 时为1,否则为0。
可视化代码
import numpy as np
import matplotlib.pyplot as plt
def hamming_distance(s1, s2):
return sum(c1 != c2 for c1, c2 in zip(s1, s2))
# 创建4x4的二进制网格
grid_size = 4
reference = np.random.randint(2, size=(grid_size, grid_size))
# 计算汉明距离
distances = np.zeros((grid_size, grid_size))
for i in range(grid_size):
for j in range(grid_size):
current = np.random.randint(2, size=(grid_size, grid_size))
distances[i, j] = hamming_distance(reference.flatten(), current.flatten())
# 绘制热力图
plt.figure(figsize=(10, 8))
plt.imshow(distances, cmap='viridis')
plt.colorbar(label='Hamming Distance')
plt.title('Hamming Distance Visualization')
plt.xlabel('X')
plt.ylabel('Y')
# 在每个格子中显示二进制值
for i in range(grid_size):
for j in range(grid_size):
plt.text(j, i, f"{reference[i, j]}", ha='center', va='center', color='w')
plt.show()
print(f"Example: Hamming distance between '1010' and '0110': {hamming_distance('1010', '0110')}")
这个可视化创建了一个4x4的二进制网格,每个格子的颜色表示它与参考格子(左上角)的汉明距离。格子中的数字表示二进制值。
9. 杰卡德距离 (Jaccard Distance)
杰卡德距离用于衡量两个集合的相似度。
原理
对于两个集合 和 ,杰卡德相似系数定义为:
杰卡德距离则定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib_venn import venn2
def jaccard_similarity(set1, set2):
intersection = len(set1.intersection(set2))
union = len(set1.union(set2))
return intersection / union
def jaccard_distance(set1, set2):
return 1 - jaccard_similarity(set1, set2)
# 创建两个集合
set1 = set(['A', 'B', 'C', 'D'])
set2 = set(['C', 'D', 'E', 'F'])
# 计算杰卡德距离
distance = jaccard_distance(set1, set2)
# 绘制韦恩图
plt.figure(figsize=(10, 6))
venn2([set1, set2], set_labels=('Set 1', 'Set 2'))
plt.title(f'Jaccard Distance Visualization\nDistance = {distance:.2f}')
plt.show()
print(f"Jaccard distance between set1 and set2: {distance:.2f}")
这个可视化使用韦恩图展示了两个集合的关系,并计算了它们之间的杰卡德距离。
10. 相关距离 (Correlation Distance)
相关距离基于皮尔逊相关系数,用于衡量两个变量之间的线性关系。
原理
对于两个变量 和 ,皮尔逊相关系数定义为:
相关距离则定义为:
可视化代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
def correlation_distance(x, y):
corr, _ = pearsonr(x, y)
return 1 - abs(corr)
# 创建数据
np.random.seed(0)
x = np.linspace(0, 10, 100)
y1 = x + np.random.normal(0, 1, 100) # 正相关
y2 = -x + np.random.normal(0, 1, 100) # 负相关
y3 = np.random.normal(0, 1, 100) # 无相关
# 计算相关距离
d1 = correlation_distance(x, y1)
d2 = correlation_distance(x, y2)
d3 = correlation_distance(x, y3)
# 绘制散点图
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 6))
ax1.scatter(x, y1)
ax1.set_title(f'Positive Correlation\nDistance = {d1:.2f}')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax2.scatter(x, y2)
ax2.set_title(f'Negative Correlation\nDistance = {d2:.2f}')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax3.scatter(x, y3)
ax3.set_title(f'No Correlation\nDistance = {d3:.2f}')
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
plt.tight_layout()
plt.show()
这个可视化展示了三种不同相关性情况下的散点图,并计算了相应的相关距离。