HOWTO · NumPy

使用 NumPy 计算欧几里得距离

本教程演示了如何在 python 中计算欧几里得距离

本页内容

对于表示相同坐标数点的两个 NumPy 数组,请使用 np.linalg.norm(point_a - point_b) 计算欧几里得距离。减法生成坐标差,而向量的 L2 范数将这些差缩减为一个非负距离。

理解欧几里得距离公式

对于点 (a=(a_1,\ldots,a_n)) 和 (b=(b_1,\ldots,b_n)),欧几里得距离是坐标差平方和的平方根:

欧几里得距离公式:坐标差平方和的平方根。

在 NumPy 中,np.linalg.norm(a - b) 直接表达此定义。对于一维差值数组且未给出 ord 参数时,np.linalg.norm 会计算其 2-范数。两个输入必须描述维度兼容的点,减法才具有预期含义。

准备输入坐标

坐标位置必须按相同顺序描述相同的轴:将一个点的第一坐标与另一个点的第一坐标比较,依此类推。将一个点表示为形状为 (coordinates,) 的一维数组。例如,两个形状为 (3,) 的数组表示同一三维坐标系中的两个点,而不是三个独立距离。

元组和列表可在减法前用 np.asarray(values, dtype=float) 转换。浮点类型接受整数或小数坐标,并在一个坐标小于对应坐标时避免无符号整数回绕。如果输入已经是合适的浮点 NumPy 数组,则无需转换。不要只为匹配形状而展平任意多维数据,因为这会掩盖而非修正输入结构错误。

以下示例使用 Python 3.14.7、NumPy 2.5.3 和 SciPy 1.18.1 执行。这些 API 是成熟接口,但应用程序仍应使用与其支持的 Python 环境兼容的版本。除非使用 SciPy 方法或成对距离工具,否则 SciPy 是可选的。

计算两点之间的距离

以下已验证的示例比较推荐的范数表达式、显式公式、点积形式、标准库 math.dist 和 SciPy 的 scipy.spatial.distance.euclidean。这五种计算对该点对产生相同结果。

"""Verify equivalent Euclidean-distance APIs for one pair of points."""

import math

import numpy as np
from scipy.spatial import distance

point_a = np.array([1.0, 2.0, 3.0])
point_b = np.array([4.0, 5.0, 6.0])

print(f"np.linalg.norm: {np.linalg.norm(point_a - point_b)}")
print(f"formula: {np.sqrt(np.sum((point_a - point_b) ** 2))}")
delta = point_a - point_b
print(f"dot product: {np.sqrt(np.dot(delta, delta))}")
print(f"math.dist: {math.dist(point_a, point_b)}")
print(f"distance.euclidean: {distance.euclidean(point_a, point_b)}")
np.linalg.norm: 5.196152422706632
formula: 5.196152422706632
dot product: 5.196152422706632
math.dist: 5.196152422706632
distance.euclidean: 5.196152422706632

显式公式在概念上使用 NumPy 逐元素平方,随后求和并取 平方根。示例中的幂形式将完整计算保留在一个表达式中。点积变体也计算相同的平方和,因为 np.dot(delta, delta) 会将对应差值相乘并相加。

当点已经是数组或属于更大的 NumPy 计算时,请使用 np.linalg.norm。它无需展开归约过程即可清晰表达向量操作。

使用 axis=1 计算逐行距离

对于存储为二维数组行的多个点,减去一个参考点并设置 axis=1。NumPy 会将参考数组广播到各行,而 axis=1 告诉 np.linalg.norm 每行返回一个范数,而不是为整个矩阵返回一个范数。

"""Verify row-wise distances from several points to one reference point."""

import numpy as np

points = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
reference = np.array([1.0, 2.0, 3.0])

print(np.linalg.norm(points - reference, axis=1))
[0.         5.19615242]

第一行等于参考点,因此距离为零。第二个结果是单点对示例中计算的距离。此模式涵盖一对多距离;对于两个集合之间的所有成对距离,应使用 scipy.spatial.distance.cdist 等专用例程,而不是在不考虑内存使用的情况下创建大型广播中间数组。

在 NumPy、math.dist 和 SciPy 之间选择

最佳方法取决于周围代码,而不是普遍的速度断言:

  • 对 NumPy 数组、向量化工作流以及带 axis 的逐行计算使用 np.linalg.norm(a - b)
  • 当其他地方不需要 NumPy 时,对一对普通 Python 坐标可迭代对象使用 math.dist(a, b)
  • 当 SciPy 已是依赖项或计算属于 SciPy 更广泛的距离工具时,使用 scipy.spatial.distance.euclidean(a, b)
  • 在教学、审查或调整底层计算时使用显式平方和公式或点积。它们不会使结果比范数表达式更“欧几里得”。

Python 列表不支持逐元素减法,因此在 NumPy 减法前用 np.asarray(..., dtype=float) 转换很有用。math.dist 可直接接受等长坐标可迭代对象,而 NumPy 和 SciPy 选项需要安装相应包。

处理不兼容的形状和数值数据类型

两个单独的点必须有相同数量的坐标。不兼容的一维形状无法一起广播,因此 NumPy 会在计算距离前引发 ValueError

"""Capture the diagnostic for points with incompatible dimensions."""

import numpy as np

point_a = np.array([1.0, 2.0])
point_b = np.array([3.0, 4.0, 5.0])

try:
    np.linalg.norm(point_a - point_b)
except ValueError as error:
    print(f"ValueError: {error}".rstrip())
ValueError: operands could not be broadcast together with shapes (2,) (3,)

当维度来自用户输入或外部数据时,请在减法前检查形状。对于逐行距离,形状为 (rows, coordinates) 的点数组与形状为 (coordinates,) 的参考点兼容;其他形状可能以非预期方式广播或失败。

还应在减法前将无符号整数坐标转换为有符号或浮点类型。无符号减法可能回绕而不是表示负坐标差,使后续范数不正确。示例中的浮点数组避免了此问题并支持非整数坐标。

对于一对维度相等的 NumPy 点,np.linalg.norm(point_a - point_b) 是简洁的默认选择。逐行距离请添加 axis=1,并在输入结构未得到保证时验证形状和数据类型。