优先使用scipy.spatial.distance.pdist+squareform计算距离矩阵,要求输入为(n_samples, n_features)的float型数组,可自动处理数值稳定性和对称性;纯NumPy广播法仅适用于小规模数据以防内存爆炸。

用 scipy.spatial.distance.pdist + squareform 最省事
直接调 pdist 算成对距离再转方阵,比手写循环或广播快得多,还自动处理数值稳定性问题。它底层用 C 实现,对 10k 点以内数据基本是秒级。
常见错误是传入 shape 为 (n,) 的一维数组——pdist 要求输入是 (n_samples, n_features)。比如你有 5 个二维点,得是 (5, 2),不是 (5,) 或 (2, 5)。
- 先确保数据是 float 类型(
astype(float)),整数可能触发意外的整数除法或溢出 - 若需欧氏距离以外的度量(如曼哈顿),传
metric='cityblock' - 结果是压缩向量(长度
n*(n-1)//2),用squareform转成对称方阵
import numpy as np from scipy.spatial.distance import pdist, squareform <p>X = np.array([[0, 0], [1, 0], [0, 1]], dtype=float) dists = squareform(pdist(X, metric='euclidean'))</p><h1>输出:[[0. 1. 1.]</h1><h1>[1. 0. 1.414...]</h1><h1>[1. 1.414... 0.]]
纯 NumPy 广播法:适合小规模且需避免 SciPy 依赖
当不能引入 scipy,或者想理解底层原理时,用 np.newaxis 或 None 做广播是标准解法。核心是把 (n, d) 的点集扩展成 (n, 1, d) 和 (1, n, d),再逐元素差、平方、求和、开方。
容易踩的坑是内存爆炸:对 10k 点,中间张量会占约 800MB(float64),而 pdist 是 O(n) 空间。所以这方法只建议用于 n
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
立即学习“Python免费学习笔记(深入)”;
- 用
np.sqrt(np.sum((X[:, None] - X[None, :])**2, axis=-1))是最直白写法 - 更省内存可改用
np.float32,但注意精度损失(尤其坐标值很大时) - 若 X 已经是行标准化(unit norm),可用
2 - 2 * X @ X.T加开方,跳过减法和平方
遇到 MemoryError 或速度慢,优先检查数据形状和 dtype
90% 的“NumPy 算不动”问题其实不是算法问题,而是输入没对齐。比如误把列向量当行向量传入,导致广播生成 (n, n, n) 而非 (n, n, d) 张量;或用 int64 存坐标却没意识到平方后溢出。
- 运行前加断言:
assert X.ndim == 2 and X.dtype in (np.float32, np.float64) - 用
X = np.ascontiguousarray(X)避免因切片导致的内存不连续,影响广播性能 - 如果点集维度 d 很高(>1000),考虑先 PCA 降维——高维下欧氏距离区分度趋近于常数,算了也意义不大
自定义函数封装时,别忽略对角线和对称性
距离矩阵主对角线必须全为 0,且严格对称。手写函数若用单循环只算上三角,后续使用(如聚类、MDS)可能因浮点误差导致 d[i,j] != d[j,i],引发难以调试的 bug。
- 强制对称:
D = (D + D.T) / 2;清对角:np.fill_diagonal(D, 0) - 不要用
np.triu后直接返回——它保留上三角其余为 0,不是对称矩阵 - 如果下游要用
scipy.cluster.hierarchy,必须传squareform(D, checks=False),否则它会额外验证对称性,拖慢速度
实际用的时候,优先走 pdist 路线;真要纯 NumPy,就盯紧 shape 和 dtype,别的都是次要的。

















