别再只会用欧式距离了Python实战对比5种距离度量方法在数据科学和机器学习领域距离度量是许多算法的核心基础。无论是K近邻分类、聚类分析还是推荐系统选择合适的距离计算方法往往直接影响模型的性能表现。然而大多数开发者习惯性地默认使用欧式距离却忽略了其他可能更适合特定场景的度量方法。本文将带你用Python的NumPy和SciPy库通过实际代码示例对比五种常用的距离度量方法欧式距离、曼哈顿距离、余弦相似度、马氏距离和切比雪夫距离。我们不仅会展示如何计算这些距离还会分析它们在不同数据特征和应用场景下的表现差异帮助你做出更明智的选择。1. 环境准备与数据生成在开始之前我们需要确保已经安装了必要的Python库。如果你使用Anaconda这些库通常已经预装否则可以通过pip安装pip install numpy scipy matplotlib pandas为了进行有意义的对比我们需要创建一些具有不同特征的测试数据。下面生成三种典型的数据分布import numpy as np from scipy.spatial import distance import matplotlib.pyplot as plt # 设置随机种子保证结果可复现 np.random.seed(42) # 生成高维数据 high_dim_data np.random.rand(100, 50) # 100个样本50维 # 生成稀疏数据 sparse_data np.random.rand(100, 30) sparse_data[sparse_data 0.9] 0 # 90%的元素为0 # 生成不同量纲的数据 mixed_scale_data np.random.rand(100, 3) mixed_scale_data[:, 0] * 100 # 第一维范围0-100 mixed_scale_data[:, 1] * 0.1 # 第二维范围0-0.1 mixed_scale_data[:, 2] * 10 # 第三维范围0-10提示在实际项目中数据预处理如标准化对距离计算影响很大。本文为了突出距离度量本身的特性暂时不对数据进行标准化处理。2. 五种距离度量方法详解与实现2.1 欧式距离Euclidean Distance欧式距离是最广为人知的距离度量方法计算两点在多维空间中的直线距离。其数学定义为$$ d(x,y) \sqrt{\sum_{i1}^n (x_i - y_i)^2} $$在Python中计算欧式距离的几种方法# 方法1使用NumPy直接计算 def euclidean_numpy(x, y): return np.sqrt(np.sum((x - y)**2)) # 方法2使用SciPy的distance模块 euclidean_scipy distance.euclidean # 方法3使用SciPy的pdist/cdist函数 pairwise_euclidean distance.cdist(high_dim_data, high_dim_data, euclidean) # 示例计算 point1 np.array([1, 2, 3]) point2 np.array([4, 5, 6]) print(fNumPy实现: {euclidean_numpy(point1, point2):.4f}) print(fSciPy实现: {euclidean_scipy(point1, point2):.4f})适用场景数据各维度量纲一致且重要性相同需要计算直线距离的场景低维空间中的几何距离计算局限性对高维数据效果不佳维度诅咒对量纲差异敏感受异常值影响较大2.2 曼哈顿距离Manhattan Distance曼哈顿距离又称城市街区距离计算各维度绝对差值的和$$ d(x,y) \sum_{i1}^n |x_i - y_i| $$Python实现# 方法1NumPy实现 def manhattan_numpy(x, y): return np.sum(np.abs(x - y)) # 方法2SciPy实现 manhattan_scipy distance.cityblock # SciPy中称为cityblock # 示例计算 print(f曼哈顿距离: {manhattan_scipy(point1, point2)})适用场景数据具有网格状结构如城市街区、棋盘某些离散特征的数据集当需要考虑所有维度上的差异而非整体距离时特性对比特征欧式距离曼哈顿距离计算方式平方和开方绝对值和对异常值敏感度高低几何意义直线距离网格路径距离高维表现较差相对较好2.3 余弦相似度Cosine Similarity余弦相似度测量两个向量方向的相似性而非距离$$ \text{similarity} \cos(\theta) \frac{A \cdot B}{|A| |B|} $$Python实现# 方法1NumPy实现 def cosine_similarity_numpy(x, y): return np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)) # 方法2SciPy实现 cosine_similarity_scipy distance.cosine # 注意返回的是1 - similarity # 示例计算 print(f余弦相似度(NumPy): {cosine_similarity_numpy(point1, point2):.4f}) print(f余弦距离(SciPy): {cosine_similarity_scipy(point1, point2):.4f}) # 1 - similarity适用场景文本数据TF-IDF向量高维稀疏数据方向比大小更重要的情况注意SciPy的distance.cosine返回的是余弦距离1 - 相似度不是相似度本身。2.4 马氏距离Mahalanobis Distance马氏距离考虑了数据各维度之间的相关性$$ d(x,y) \sqrt{(x-y)^T S^{-1} (x-y)} $$其中S是协方差矩阵。Python实现# 计算马氏距离 def mahalanobis_distance(x, y, data): covariance_matrix np.cov(data.T) inv_covariance np.linalg.inv(covariance_matrix) diff x - y return np.sqrt(diff.T inv_covariance diff) # 示例计算 sample_data np.vstack([point1, point2, np.array([7, 8, 9])]) print(f马氏距离: {mahalanobis_distance(point1, point2, sample_data):.4f})适用场景数据维度间存在相关性各维度量纲差异大需要去除特征间相关性的场景2.5 切比雪夫距离Chebyshev Distance切比雪夫距离计算各维度绝对差值的最大值$$ d(x,y) \max_i |x_i - y_i| $$Python实现# 方法1NumPy实现 def chebyshev_numpy(x, y): return np.max(np.abs(x - y)) # 方法2SciPy实现 chebyshev_scipy distance.chebyshev # 示例计算 print(f切比雪夫距离: {chebyshev_scipy(point1, point2)})适用场景棋盘式移动如国际象棋中王的移动任何只需要考虑最大差异维度的场景3. 性能与效果对比实验3.1 计算效率对比让我们比较不同距离度量在计算时间上的差异import time distances { 欧式距离: distance.euclidean, 曼哈顿距离: distance.cityblock, 余弦距离: distance.cosine, 切比雪夫距离: distance.chebyshev } # 马氏距离单独测试因为它需要额外计算 def mahalanobis_wrapper(x, y): return mahalanobis_distance(x, y, high_dim_data) distances[马氏距离] mahalanobis_wrapper # 性能测试 results {} for name, func in distances.items(): start time.time() for i in range(100): func(high_dim_data[i], high_dim_data[(i1)%100]) elapsed time.time() - start results[name] elapsed # 展示结果 print(100次距离计算耗时(秒):) for name, t in sorted(results.items(), keylambda x: x[1]): print(f{name:10}: {t:.4f})典型输出结果可能类似于100次距离计算耗时(秒): 欧式距离 : 0.0010 曼哈顿距离 : 0.0010 切比雪夫距离: 0.0010 余弦距离 : 0.0015 马氏距离 : 0.35003.2 不同数据分布下的表现我们生成三种不同类型的数据分布比较各种距离度量的表现# 创建测试数据 cluster1 np.random.normal(loc0, scale1, size(50, 2)) cluster2 np.random.normal(loc5, scale1, size(50, 2)) outliers np.random.uniform(low-10, high10, size(5, 2)) test_data np.vstack([cluster1, cluster2, outliers]) # 计算并可视化不同距离矩阵 def plot_distance_matrix(data, metric, title): dist_matrix distance.cdist(data, data, metric) plt.figure(figsize(8, 6)) plt.imshow(dist_matrix, cmapviridis) plt.colorbar() plt.title(title) plt.show() plot_distance_matrix(test_data, euclidean, 欧式距离矩阵) plot_distance_matrix(test_data, cityblock, 曼哈顿距离矩阵) plot_distance_matrix(test_data, cosine, 余弦距离矩阵)从可视化结果可以观察到欧式和曼哈顿距离对异常值敏感余弦距离更关注方向而非绝对位置马氏距离未展示能更好地处理相关特征4. 实际应用场景指南4.1 K近邻分类中的距离选择在KNN算法中距离度量直接影响分类结果from sklearn.neighbors import KNeighborsClassifier from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载数据 iris load_iris() X, y iris.data, iris.target X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3) # 测试不同距离度量 metrics [euclidean, manhattan, cosine] results {} for metric in metrics: knn KNeighborsClassifier(n_neighbors5, metricmetric) knn.fit(X_train, y_train) score knn.score(X_test, y_test) results[metric] score print(KNN分类准确率:) for metric, score in results.items(): print(f{metric:10}: {score:.4f})4.2 聚类分析中的距离选择不同距离度量会导致完全不同的聚类结果from sklearn.cluster import KMeans, AgglomerativeClustering from sklearn.preprocessing import StandardScaler # 使用之前生成的mixed_scale_data data mixed_scale_data # 标准化数据 scaler StandardScaler() scaled_data scaler.fit_transform(data) # 比较聚类结果 def evaluate_clustering(data, metric): # KMeans通常使用欧式距离但我们可以通过其他方式模拟 if metric euclidean: model KMeans(n_clusters2) else: model AgglomerativeClustering(n_clusters2, affinitymetric, linkageaverage) labels model.fit_predict(data) return labels # 可视化函数 def plot_clusters(data, labels, title): plt.figure(figsize(8, 6)) plt.scatter(data[:, 0], data[:, 1], clabels, cmapviridis) plt.title(title) plt.show() # 比较不同距离 for metric in [euclidean, manhattan, cosine]: labels evaluate_clustering(scaled_data, metric) plot_clusters(scaled_data[:, :2], labels, f使用{metric}距离的聚类结果)4.3 推荐系统中的相似度计算在推荐系统中用户或物品的相似度计算至关重要# 模拟用户-物品评分矩阵 user_item_matrix np.random.randint(0, 6, size(10, 20)) # 10用户对20物品的评分(0-5) # 计算用户相似度 def user_similarity(matrix, metric): return 1 - distance.cdist(matrix, matrix, metric) # 比较不同相似度度量 similarity_metrics [euclidean, cityblock, cosine] for metric in similarity_metrics: sim_matrix user_similarity(user_item_matrix, metric) print(f\n使用{metric}距离的用户相似度矩阵示例:) print(sim_matrix[:3, :3]) # 展示前3个用户之间的相似度选择指南总结应用场景推荐距离度量原因低维空间几何距离欧式距离符合直观几何距离高维稀疏数据余弦相似度关注方向而非大小网格结构数据曼哈顿距离反映网格路径距离特征相关性强马氏距离考虑特征相关性仅关注最大差异切比雪夫距离取最大维度差异异常值较多曼哈顿距离对异常值不敏感在实际项目中最好的方法是通过交叉验证比较不同距离度量的效果。例如在使用scikit-learn的算法时很多模型都支持通过metric参数指定距离度量方式可以轻松进行对比实验。