☰
Orange3 距离度量模块(Orange.distance)完全指南:从欧氏距离到马氏距离的实战用法
2026/10/12 2:03:46 网站建设 项目流程
  • 人工智能
  • 机器学习
  • 数据分析
  • 数据可视化

【免费下载链接】orange3

🍊 :bar_chart: :bulb: Orange: Interactive data analysis

项目地址:https://gitcode.com/gh_mirrors/or/orange3
点击查看免费下载

本文基于 Orange3 数据挖掘库官方参考文档 distance.rst,系统讲解Orange.distance模块中全部距离度量(Euclidean、Manhattan、Cosine、Jaccard、四种相关系数距离、Mahalanobis 等)的编程接口、归一化机制与缺失值处理原理。读完本文,你将掌握如何在 Python 中快速计算行间/列间距离、如何通过fit训练距离模型并复用于新数据、如何求解最近邻,以及每种距离在离散/缺失数据下的精确行为,可直接用于 Orange3 数据挖掘脚本与自有实验。

模块概览与快速上手

Orange.distance是 Orange3 数据挖掘库(参考文档位于 doc/data-mining-library/source/reference/distance.rst)中负责计算数据行、列之间距离的核心模块,其公开 API 定义在 Orange/distance/init.py:

from Orange.distance import (Distance, DistanceModel, Euclidean, Manhattan, Cosine, Jaccard, SpearmanR, SpearmanRAbsolute, PearsonR, PearsonRAbsolute, Mahalanobis, MahalanobisDistance, Hamming, Bhattacharyya)

模块的完整实现位于 Orange/distance/distance.py 与 Orange/distance/base.py,其中核心数值计算由 Cython 实现(Orange/distance/_distance.pyx)。

下面以 Iris 数据集演示最基本的三类用法(与官方文档示例一致):

>>> from Orange.data import Table >>> from Orange.distance import Euclidean >>> iris = Table('iris') >>> dist_matrix = Euclidean(iris) # 计算所有行两两之间的距离 >>> # 前两个样本之间的距离 >>> dist_matrix.X[0, 1] 0.53851648

要计算所有列(属性)之间的距离,把axis设为 0:

>>> Euclidean(iris, axis=0) DistMatrix([[ 0. , 36.17927584, 28.9542743 , 57.1913455 ], [ 36.17927584, 0. , 25.73382987, 25.81259383], [ 28.9542743 , 25.73382987, 0. , 33.87270287], [ 57.1913455 , 25.81259383, 33.87270287, 0. ]])

还可以计算两张表之间所有行对的距离:

>>> iris1 = iris[:100] >>> iris2 = iris[100:] >>> dist = Euclidean(iris1, iris2) # 注意:原文档此处笔误写为 iris_even/iris_odd >>> dist.shape (100, 50)

按切片逻辑,iris1含 100 行、iris2含 50 行,因此结果矩阵形状应为 (100, 50)(原文档示例输出 (75, 100) 与切片定义不一致,属文档笔误)。

通用接口:Distance、DistanceModel 与 fit 训练模式

所有距离度量共享同一套接口,其抽象基类定义在 Orange/distance/base.py。Distance基类支持两种用法:

  1. 一次性用法(魔数构造函数):构造时直接传入一个或两个数据表,内部自动完成fit并计算距离矩阵;
  2. 两阶段用法(推荐,尤其针对新数据):先用无数据参数构造,再调用fit(data)得到DistanceModel,之后用模型对(新的)数据计算距离。
>>> dist_model = Euclidean(normalize=True).fit(iris1) # 在训练数据上拟合 >>> dist = dist_model(iris2[:3]) # 对前 3 行新数据计算距离 >>> dist DistMatrix([[ 0. , 1.36778277, 1.11352233], [ 1.36778277, 0. , 1.57810546], [ 1.11352233, 1.57810546, 0. ]])

上面的距离是在iris2的前三行上计算的,归一化所用的均值与方差来自iris1(拟合阶段的数据)。fit的底层流程在 Orange/distance/base.py:将数据转为 numpy 数组,根据axis选择调用fit_rows或fit_cols,生成对应的FittedDistanceModel;FittedDistanceModel会保存属性列表、离散/连续列掩码、归一化统计量(均值/方差或中位数/MAD)以及缺失值补偿表,详见 Orange/distance/base.py。

构造函数参数

Distance的通用构造参数(Orange/distance/base.py):

参数类型默认值说明
e1Table/Instance/np.ndarray/NoneNone用于拟合模型并计算距离的数据
e2同上None若给定,则计算两张表/两组数据行对之间的距离
axisint1计算距离的轴,1表示行、0表示列
imputeboolFalse为True时,结果矩阵中的 nan 被替换为 0,inf 被替换为极大数
callbackcallableNone计算过程中的进度回调函数
similarityboolFalse为True时计算相似度而非距离(仅supports_similarity的度量支持)

此外,Euclidean与Manhattan还支持normalize参数(见下文)。

能力标志位

每个度量类通过类属性声明其能力(Orange/distance/base.py):

  • supports_discrete:是否支持离散属性参与距离计算;
  • supports_missing:是否原生支持缺失值(支持时,优先让度量自行处理,而不是预先插补);
  • supports_normalization:是否接受normalize=True参数;
  • supports_sparse:是否支持稀疏数据(目前支持稀疏的类依赖 sklearn 距离作为回退实现,此时不支持离散值与缺失值);
  • supports_similarity:是否支持similarity=True计算相似度。

这些标志不仅被文档引用,也是 GUI 组件(如 Distances 控件)启用/禁用选项的依据,见 Orange/widgets/unsupervised/owdistances.py 中的refresh_radios。

返回类型 DistMatrix

距离计算统一返回Orange.misc.DistMatrix(Orange/misc/distmatrix.py),它是numpy.ndarray的子类,额外携带row_items(行对应的数据)、col_items(列对应的数据)与axis属性。因此可以直接使用 numpy 的切片、np.argsort等操作。它还提供submatrix切片、save/from_file持久化(支持.dst文本格式与.xlsx)、is_symmetric与auto_symmetricized等方法。

最近邻查找实战

两阶段用法天然适合"在训练集上拟合、对查询点找最近邻"的场景。文档给出了找到iris2[0]在iris1中五个最近邻居的示例:

>>> import numpy as np >>> dist0 = dist_model(iris1, iris2[0]) # 与 iris1 所有行的距离 >>> neigh_idx = np.argsort(dist0.flatten())[:5] # 最小的 5 个距离对应下标 >>> iris1[neigh_idx] [[5.900, 3.200, 4.800, 1.800 | Iris-versicolor], [6.700, 3.000, 5.000, 1.700 | Iris-versicolor], [6.300, 3.300, 4.700, 1.600 | Iris-versicolor], [6.000, 3.400, 4.500, 1.600 | Iris-versicolor], [6.400, 3.200, 4.500, 1.500 | Iris-versicolor] ]

这里dist_model(iris1, iris2[0])计算的是查询实例与iris1全部行之间的距离向量(内部会把RowInstance包装成二维数组),np.argsort(...)[:5]取出距离最小的五个下标,再用下标对iris1取行即可得到最近邻。

离散数据与缺失数据的处理原则

文档明确了两条核心原则,也是 Orange3 距离模块区别于朴素实现的关键:

  1. 离散值按度量类型处理:欧氏/曼哈顿距离把一对离散值视为"相同或不同",贡献 0 或 1 的平方差/绝对差;而 Jaccard、余弦距离把离散值视为"零或非零",见 Orange/distance/distance.py 的discrete_to_indicators(将所有非零离散值置 1)。

  2. 缺失值不做简单插补:假设每个变量的值服从某个未知分布,在不假设分布形状的前提下计算"期望距离"。以欧氏距离为例,已知值与缺失值的期望平方距离等于该已知值到缺失变量均值的距离平方、加上该变量的方差。

欧氏距离的缺失值公式(含推导依据)

官方设计文档 Orange/distance/distances.md 给出了完整的数学推导:

  • 数值特征归一化:$x' = \frac{x - \mu}{\sqrt{2\sigma^2}}$,归一化后均值为 0、方差为 1/2;
  • 一个已知值 $v$ 与一个缺失值之间的期望平方差:$(v - \mu)^2 + \sigma^2$;
  • 两个缺失值之间的期望平方差:$2\sigma^2$(同行、同分布假设下);
  • 列间距离时两个缺失值来自不同分布:$\sigma_x^2 + \sigma_y^2 + (\mu_x - \mu_y)^2$;
  • 归一化数据下:已知值与缺失值之差为 $v'^2 + 1/2$,两个缺失值之差为 1;
  • 离散特征:已知值与缺失值的期望差为 $1 - p(v)$(已知值取到的概率),两个都缺失时为 $1 - \sum_x p(x)^2$,即 Gini 指数。

这些公式在代码中有精确对应:连续列统计量由Euclidean.get_continuous_stats计算(Orange/distance/distance.py,归一化时dist_missing2_cont = 1,否则为2 * var);离散列统计量由FittedDistance.get_discrete_stats计算(Orange/distance/base.py)。测试用例 Orange/distance/tests/test_distance.py 用人工手算/Excel 核对的方式验证了缺失离散值的期望距离表。

支持的八种距离度量详解

1. Euclidean 欧氏距离

对数值型数据,欧氏距离是行/列两两平方差的平方根;对离散值,两值不同则加 1。在处理数值与离散混合数据、或希望所有数值属性同量纲时,文档强烈推荐开启normalize=True:

>>> dist = Euclidean(iris, normalize=True)

归一化时数值被减去均值、除以"偏差乘以根号 2"(即 $\sqrt{2\sigma^2}$)。均值与偏差在调用fit时取自训练数据;当直接对两张表计算、未显式调用fit时,均值和方差只从第一张表计算。均值与方差始终按列计算,与计算距离的轴无关,因为列代表变量、来自某个分布。

Euclidean的实现类为 Orange/distance/distance.py:行模型EuclideanRowsModel借鉴 sklearn 的x² − 2xy + y²技巧用row_norms+safe_sparse_dot加速,再调用 Cython 的fix_euclidean_rows(_normalized)修正含 nan 的行对并叠加离散列贡献;列模型EuclideanColumnsModel类似。零方差列在归一化下会被剔除(无贡献),全缺失或常数列会触发ValueError,见测试 test_distance.py。

2. Manhattan 曼哈顿距离

曼哈顿距离是绝对值差的累加。归一化与缺失值处理逻辑与欧氏距离类似,区别在于**使用中位数(median)和中位数绝对偏差(MAD)**代替均值和方差,归一化公式为 $x' = (x - m)/(2a)$。离散列上因为"相同/不同"仍只贡献 0 或 1,所以曼哈顿距离与欧氏距离对离散列的结果一致(代码中直接复用euclidean_rows_discrete,见 Orange/distance/distance.py)。

>>> dist = Manhattan(iris, axis=1, normalize=True) >>> model = Manhattan(normalize=True).fit(iris1)

实现位于 Orange/distance/distance.py,统计量计算见get_continuous_stats(L355-L371)。

3. Cosine 余弦距离

余弦相似度为点积除以长度乘积(长度为某行/列与自身的点积的平方),余弦距离 = 1 − 相似度。关键行为:

  • 缺失值用均值替换(点积层面近似插补,理论上有微小偏差,见 distances.md 的讨论);
  • 非零离散值一律替换为 1,由此引入"基值"概念(取值列表中的第一个值);这通常在指示变量(二值/布尔属性)上才有意义;
  • 不支持任何列归一化(supports_normalization为 False,但支持similarity=True直接输出相似度)。
>>> dist = Cosine(iris, axis=1) >>> sim = Cosine(iris, similarity=True) # 直接得到相似度矩阵

实现见 Orange/distance/distance.py:fit_rows计算各列均值用于插补,CosineModel.compute_distances插补缺失值、按行/列归一化为单位长度后做点积,最后裁剪到 [-1, 1] 并输出1 - dist或dist。

4. Jaccard 杰卡德距离

Jaccard 相似度 = 两个集合交集大小 / 并集大小,距离 = 1 − 相似度。在 Orange 中,属性值被解释为集合成员指示:

  • 按行计算时,列被解释为集合,行中非零值(包括数值特征的负值)表示该行属于相应集合;
  • 按列计算时,行是集合,值表示该列属于哪些集合。

缺失值处理:用训练数据中的相对频率作为"属于集合"的概率。按行计算时,统计每列非零值的相对频率;按列计算时反之。计算集合交集(并集)时,用"两者都(任一)属于"的概率替换 0/1 贡献。fit_rows通过p_nonzero统计每列非零值频率(Orange/distance/distance.py),稠密/稀疏分别走jaccard_rows/jaccard_cols或_compute_sparse的集合运算实现(L486-L556)。注意 Jaccard 不支持归一化,但支持稀疏数据与similarity=True。

>>> dist = Jaccard(data) # data 为 0/1 或含负值的指示数据 >>> sim = Jaccard(data, similarity=True)

5. SpearmanR / AbsoluteSpearmanR / PearsonR / AbsolutePearsonR

四种基于相关系数的距离度量统一等于 (1 − 相关系数) / 2;AbsoluteSpearmanR与AbsolutePearsonR取系数的绝对值,即距离 = 1 − |ρ|。它们不支持缺失值与离散值(supports_missing = False,基类 Orange/distance/distance.py)。

>>> dist = PearsonR(data, axis=1) # 距离 = (1 - ρ) / 2 >>> dist = SpearmanRAbsolute(data, axis=0) # 距离 = 1 - |ρ|

实现上PearsonModel用 numpy 的corrcoef或自实现的_corrcoef2求两表行/列两两相关系数;SpearmanModel先对数据做stats.rankdata秩变换再求相关(_spearmanr2,Orange/distance/distance.py)。GUI 中的工具提示也明确写出了公式,见 Orange/widgets/unsupervised/utils.py。

6. Mahalanobis 马氏距离

马氏距离与余弦距离形式相似,但数据先被投影到 PCA 空间——实现上等价于使用训练数据协方差矩阵的逆(fit中计算np.cov并求逆,Orange/distance/distance.py),然后调用 sklearn 的pairwise_distances(metric='mahalanobis', VI=vi)。它不支持缺失值与离散值(supports_missing = False),且要求特征数量与协方差矩阵维度一致,否则抛出ValueError('Incorrect number of features.')(L843-L845)。由于需要计算协方差逆矩阵,文档与控件均提示大数据集上代价较高——Distances 控件对马氏距离限定了规模(约 1000 行/列以内),见 owdistances.py。

>>> model = Mahalanobis().fit(train_data) # 存储协方差逆矩阵 >>> dist = model(test_data)

另有一个为向后兼容保留的旧类MahalanobisDistance(Orange/distance/distance.py),新代码请直接使用Mahalanobis。

7. Hamming 汉明距离

汉明距离在 Orange/distance/distance.py 中实现,基于 sklearn 的pairwise_distances(metric='hamming'),支持离散值、不支持稀疏与缺失值。测试见 test_distance.py。

8. Bhattacharyya 巴氏距离

Bhattacharyya 距离(Orange/distance/distance.py)将输入归一化为概率分布后计算 $-\log\sum\sqrt{p_i q_i}$(经_prob_dist归一化),支持稀疏数据、不支持离散值;它并非严格度量(不满足三角不等式),代码注释中明确说明。

距离计算与 GUI 的集成:Distances 控件

Orange.distance也是图形界面中Distances 控件(Orange.widgets.unsupervised.owdistances.OWDistances)的后端。该控件允许用户在"行/列"之间切换axis、选择 12 种度量(含归一化变体),并输出DistMatrix。其调用方式是理解库接口的最佳实践示例(Orange/widgets/unsupervised/owdistances.py):

kwargs = {"axis": 1 - axis, "impute": True, "callback": callback} if metric.supports_normalization and normalized_dist: kwargs["normalize"] = True return metric(data, **kwargs)

控件还会根据度量能力做数据预处理:对不支持离散值的度量调用distance.remove_discrete_features(data, to_metas=True)把离散列移到 meta 区、对 Jaccard 只保留二值属性(remove_nonbinary_features)、对不支持缺失值的度量调用distance.impute(data)插补,并在界面上给出相应警告。这些辅助函数同样通过 Orange/distance/init.py 导出,可直接在脚本中复用。

性能与实现细节

  • 可中断计算:Euclidean、Manhattan等大规模矩阵运算通过_interruptible_dot、_interruptible_sqrt按块(默认每 100 列)执行并周期调用callback上报进度(Orange/distance/distance.py),GUI 的进度条与"取消"能力正依赖于此(StepwiseCallbacks按各阶段权重划分进度,见 L67-L77)。
  • Cython 加速:欧氏/曼哈顿的 nan 修正、离散列贡献、Jaccard 行/列计算均落在 Orange/distance/_distance.pyx 的 Cython 函数中(如fix_euclidean_rows、fix_manhattan_rows_normalized、jaccard_rows、jaccard_cols),无缺失值的快循环与含缺失值的慢循环分离,兼顾正确性与速度。
  • 稀疏数据回退:Euclidean/Manhattan/Cosine/Jaccard声明supports_sparse = True,实际通过SklDistance(Orange/distance/base.py)回退到 sklearn 的pairwise_distances;这些回退不支持离散与缺失值,使用时应自行预处理。

测试验证

模块的数值正确性由 Orange/distance/tests/test_distance.py 覆盖,值得关注的点:

  • CommonTests.test_sparse:验证稠密/稀疏输入结果一致(L39-L57);
  • CommonFittedTests.test_mismatching_attributes:拟合所用属性与预测数据属性不一致时抛ValueError(L62-L71);
  • CommonNormalizedTests.test_zero_variance:零方差列在归一化下不影响行距离,列距离归一化遇常数列/全缺失列抛ValueError(L77-L116);
  • 各距离的稠密数值断言标注为"手动或 Excel 计算,非回归测试"(L173-L174),即结果是经过独立核算的基准值;
  • 辅助函数remove_discrete_features、remove_nonbinary_features的域变换行为也有专门测试(L1071-L1105)。

小结

Orange.distance提供了一套统一、可训练、对缺失值有严谨期望距离处理的距离计算接口:Euclidean与Manhattan支持归一化与离散/缺失数据,Cosine与Jaccard提供相似度模式并对离散值做"零/非零"解释,四种相关系数距离与Mahalanobis面向纯数值数据,Hamming、Bhattacharyya补充了更多场景。配合DistMatrix的 numpy 兼容能力与 Cython 加速实现,既可支撑脚本化的最近邻、聚类预处理,也是 Orange3 可视化工作流(Distances 控件等)的底层引擎。深入阅读 Orange/distance/distances.md 可了解全部归一化与缺失值公式的数学推导,Orange/distance/tests/test_distance.py 则是核对具体数值结果的最佳参考。

  • 人工智能
  • 机器学习
  • 数据分析
  • 数据可视化

【免费下载链接】orange3

🍊 :bar_chart: :bulb: Orange: Interactive data analysis

项目地址:https://gitcode.com/gh_mirrors/or/orange3
点击查看免费下载

相关推荐

上一篇:Minecraft PCL启动器终极指南:10分钟打造个性化游戏世界
下一篇:思源宋体CN终极使用指南:7种字重免费开源字体快速入门

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询