机器学习中10种距离介绍(附python代码)

文摘   2024-10-25 08:35   湖北  

在数据科学、机器学习和人工智能领域中,"距离"这个概念扮演着至关重要的角色。它不仅是我们理解数据之间关系的基础,也是许多算法的核心组成部分。从最简单的k-近邻算法到复杂的聚类分析,从推荐系统到异常检测,距离度量无处不在。

但是,什么是"距离"?在数学和计算机科学中,距离并不仅仅是我们日常生活中所理解的物理距离。它是一种量化两个数据点之间差异或相似度的方法。根据数据的性质和分析的目的,我们可以选择不同的距离度量方法。

本文将深入探讨十种常用的距离度量方法:欧氏距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、标准化欧氏距离、马氏距离、余弦距离、汉明距离、杰卡德距离和相关距离。我们将详细解释每种方法的数学原理,并通过直观的二维可视化来帮助读者理解这些概念。

1. 欧氏距离 (Euclidean Distance)unsetunset

欧氏距离是最常用的距离度量方法之一,它衡量的是多维空间中两点之间的直线距离。

原理

对于二维平面上的两点 ,欧氏距离定义为:

可视化代码

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(010100)
y = np.linspace(010100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([22])
p2 = np.array([88])

# 计算到p1的距离
Z1 = np.sqrt((X - p1[0])**2 + (Y - p1[1])**2)

# 绘制等高线图
plt.figure(figsize=(108))
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,红色虚线表示它们之间的欧氏距离。

unsetunset2. 曼哈顿距离 (Manhattan Distance)unsetunset

曼哈顿距离也称为城市街区距离,它计算的是沿坐标轴方向的距离总和。

原理

对于二维平面上的两点 ,曼哈顿距离定义为:

可视化代码

import numpy as np
import matplotlib.pyplot as plt

def manhattan_distance(p1, p2):
    return np.sum(np.abs(p1 - p2))

# 创建数据点
x = np.linspace(010100)
y = np.linspace(010100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([22])
p2 = np.array([88])

# 计算到p1的距离
Z1 = np.abs(X - p1[0]) + np.abs(Y - p1[1])

# 绘制等高线图
plt.figure(figsize=(108))
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的曼哈顿路径。

unsetunset3. 切比雪夫距离 (Chebyshev Distance)unsetunset

切比雪夫距离也称为棋盘距离,它衡量的是两点之间的最大坐标差。

原理

对于二维平面上的两点 ,切比雪夫距离定义为:

可视化代码

import numpy as np
import matplotlib.pyplot as plt

def chebyshev_distance(p1, p2):
    return np.max(np.abs(p1 - p2))

# 创建数据点
x = np.linspace(010100)
y = np.linspace(010100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([22])
p2 = np.array([88])

# 计算到p1的距离
Z1 = np.maximum(np.abs(X - p1[0]), np.abs(Y - p1[1]))

# 绘制等高线图
plt.figure(figsize=(108))
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之间的直线距离,但实际的切比雪夫距离是坐标差的最大值。

unsetunset4. 闵可夫斯基距离 (Minkowski Distance)unsetunset

闵可夫斯基距离是欧氏距离、曼哈顿距离和切比雪夫距离的一般化形式。

原理

对于二维平面上的两点 ,闵可夫斯基距离定义为:

其中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(010100)
y = np.linspace(010100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([22])
p2 = np.array([88])

# 计算不同p值的距离
p_values = [1210]

fig, axs = plt.subplots(13, figsize=(206))

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很大时,等高线接近正方形(切比雪夫距离)。

unsetunset5. 标准化欧氏距离 (Standardized Euclidean Distance)unsetunset

标准化欧氏距离考虑了各个特征的尺度差异,通过标准差进行归一化。

原理

对于二维平面上的两点 ,标准化欧氏距离定义为:

其中 分别是 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(010100)
y = np.linspace(010100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([22])
p2 = np.array([88])

# 假设的标准差
std_dev = np.array([21])

# 计算到p1的距离
Z = np.sqrt(((X - p1[0]) / std_dev[0])**2 + ((Y - p1[1]) / std_dev[1])**2)

# 绘制等高线图
plt.figure(figsize=(108))
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维度的不同标准差。

unsetunset6. 马氏距离 (Mahalanobis Distance)unsetunset

马氏距离考虑了特征之间的相关性,是标准化欧氏距离的进一步推广。

原理

对于二维向量 ,马氏距离定义为:

其中 是协方差矩阵。

可视化代码

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(-55100)
y = np.linspace(-55100)
X, Y = np.meshgrid(x, y)

# 选择两个点
p1 = np.array([00])
p2 = np.array([22])

# 定义协方差矩阵
cov = np.array([[21], [12]])

# 计算马氏距离
pos = np.dstack((X, Y))
rv = multivariate_normal(p1, cov)
Z = rv.pdf(pos)

# 绘制等高线图
plt.figure(figsize=(108))
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}")

这个可视化展示了马氏距离如何考虑特征之间的相关性。等高线表示概率密度,反映了数据的分布特征。

unsetunset7. 余弦距离 (Cosine Distance)unsetunset

余弦距离衡量的是两个向量之间的夹角,常用于文本分析和推荐系统。

原理

对于两个向量 ,余弦相似度定义为:

余弦距离则定义为:

可视化代码

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(02*np.pi, 100)
r = np.linspace(0150)
R, Theta = np.meshgrid(r, theta)
X = R * np.cos(Theta)
Y = R * np.sin(Theta)

# 选择两个向量
v1 = np.array([0.80.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=(108))
plt.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(label='Cosine Distance from v1')
plt.quiver(00, v1[0], v1[1], color='r', scale=5, label='v1')
plt.quiver(00, 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的余弦距离,箭头表示两个向量的方向。

unsetunset8. 汉明距离 (Hamming Distance)unsetunset

汉明距离用于衡量两个等长字符串之间对应位置的不同字符的个数。虽然它通常用于字符串,但我们可以用二维平面上的二进制网格来可视化它。

原理

对于两个等长字符串 ,汉明距离定义为:

其中 表示当 时为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=(108))
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的二进制网格,每个格子的颜色表示它与参考格子(左上角)的汉明距离。格子中的数字表示二进制值。

unsetunset9. 杰卡德距离 (Jaccard Distance)unsetunset

杰卡德距离用于衡量两个集合的相似度。

原理

对于两个集合 ,杰卡德相似系数定义为:

杰卡德距离则定义为:

可视化代码

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=(106))
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}")

这个可视化使用韦恩图展示了两个集合的关系,并计算了它们之间的杰卡德距离。

unsetunset10. 相关距离 (Correlation Distance)unsetunset

相关距离基于皮尔逊相关系数,用于衡量两个变量之间的线性关系。

原理

对于两个变量 ,皮尔逊相关系数定义为:

相关距离则定义为:

可视化代码

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(010100)
y1 = x + np.random.normal(01100)  # 正相关
y2 = -x + np.random.normal(01100)  # 负相关
y3 = np.random.normal(01100)  # 无相关

# 计算相关距离
d1 = correlation_distance(x, y1)
d2 = correlation_distance(x, y2)
d3 = correlation_distance(x, y3)

# 绘制散点图
fig, (ax1, ax2, ax3) = plt.subplots(13, figsize=(186))

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()

这个可视化展示了三种不同相关性情况下的散点图,并计算了相应的相关距离。

Python学习杂记
数据分析与挖掘、运筹优化、机器学习、AI 、数据可视化等。
 最新文章