如何在 R 语言中计算曼哈顿距离:从基础原理到 2026 年生产级实践

在数据科学、机器学习和空间分析的广阔天地里,衡量数据点之间的相似性或差异性是一项核心任务。你是否曾想过,在城市导航中,我们为什么往往不能像鸟儿一样直线飞行?同样,在处理某些类型的高维数据时,欧几里得距离(即直线距离)并不总是最佳选择。这时,曼哈顿距离 便成为了我们的得力助手。

在这篇文章中,我们将不仅仅满足于调用函数,而是会从数学定义出发,亲手编写实现代码,并深入挖掘 R 语言内置工具的强大功能。同时,我们将融合 2026 年最新的技术趋势,探讨如何利用现代开发工具(如 AI 辅助编程)和工程化思维,将这些基础算法应用到生产级的大规模数据处理中。无论你是正在处理分类数据的算法工程师,还是需要进行聚类分析的数据科学家,这篇文章都将为你提供实用的见解和最佳实践。

什么是曼哈顿距离?

在正式写代码之前,让我们先达成共识:曼哈顿距离到底是什么?

想象一下,你身处一个棋盘般的城市街道网格中(正如曼哈顿区的城市规划)。如果你要从坐标 $(x1, y1)$ 的点 A 到达坐标 $(x2, y2)$ 的点 B,由于有建筑物阻挡,你无法沿直线飞行。你必须沿着街道行走:先向东(或西)走一定的距离,再向北(或南)走一定的距离。

这种“直角转弯”的路径长度,就是曼哈顿距离。

数学上,对于 N 维空间中的两个向量 xy,曼哈顿距离定义为对应维度坐标之差的绝对值之和。公式如下:

$$ D(x, y) = \sum{i=1}^{n}

xi – y_i

$$

相较于欧几里得距离(平方和的平方根),曼哈顿距离对异常值不那么敏感,因为它不进行平方放大。这使得它在某些高维数据处理和稀疏数据场景中表现更加稳健。

方法 1:从头实现公式法

最直观的方式是直接根据定义编写函数。这不仅能帮助我们理解其背后的数学原理,还能让我们完全掌控计算过程。在 2026 年的“氛围编程”时代,虽然我们常让 AI 生成初始代码,但理解底层逻辑依然是我们不可或缺的核心能力。

#### 基础实现逻辑

在 R 语言中,我们可以利用向量化运算的特性,避免编写繁琐的循环。核心思路是:

  • 计算差值:对两个向量进行减法运算。
  • 取绝对值:使用 abs() 函数处理负值。
  • 求和:使用 sum() 函数汇总结果。

#### 示例 1:计算两个等长向量的距离

假设我们有两个向量 INLINECODE9e1c0150 和 INLINECODE56160edf。我们需要计算它们在空间中的“街区距离”。

# 定义一个计算曼哈顿距离的函数
# 参数 vect1: 第一个数值向量
# 参数 vect2: 第二个数值向量
manhattanDistance <- function(vect1, vect2){
  # 计算对应元素之差的绝对值
  diff_vector <- abs(vect1 - vect2)
  
  # 对所有差值求和
  dist <- sum(diff_vector)
  
  # 返回计算结果
  return(dist)
}

# 初始化第一个向量
vect1 <- c(3, 6, 8, 9)

# 初始化第二个向量
vect2 <- c(1, 7, 8, 10)

print("vect1 和 vect2 之间的曼哈顿距离是: ")

# 调用我们的函数
result <- manhattanDistance(vect1, vect2)
print(result)

输出结果:

[1] "vect1 和 vect2 之间的曼哈顿距离是: "
[1] 4

代码解读:

在这个例子中,INLINECODEe91d8a38 计算为 INLINECODEda98bc8a。这正是我们期望的结果。通过封装函数,我们可以在项目中任何需要的地方复用这段逻辑。

#### 示例 2:处理不等长向量的情况

在实际开发中,数据清洗往往比计算本身更耗时。如果两个向量的长度不一致,直接相减会发生什么?R 语言的循环补齐规则会介入,但这通常不是我们想要的结果,甚至会引发严重的逻辑错误。

# 使用之前定义的函数
manhattanDistance <- function(vect1, vect2){
  dist <- abs(vect1 - vect2)
  dist <- sum(dist)
  return(dist)
}

# 初始化两个长度不一致的向量
vect1 <- c(14, 13, 24, 18)
vect2 <- c(13, 12, 33, 11, 12)

print("尝试计算不等长向量的曼哈顿距离: ")

# 调用函数
manhattanDistance(vect1, vect2)

输出结果(包含警告):

[1] "尝试计算不等长向量的曼哈顿距离是: "
[1] 35
There were 12 warnings (use warnings() to see them)

实战经验:

你会注意到,虽然 R 返回了一个数值 INLINECODE0528876f,但也给出了警告信息。这是因为 R 将较短的向量进行了重复以匹配较长向量的长度(例如 INLINECODE2093221c 的 INLINECODE22750934 会与 INLINECODEa014a9db 的 INLINECODE8c4ee61a 和 INLINECODE5ade7f6f 相减)。

为了增强代码的健壮性,我们建议在函数中加入长度检查。这就像我们训练 AI 模型前的数据校验一样,是工程化思维的重要体现。

# 优化后的安全版本
safeManhattanDistance <- function(vect1, vect2){
  if (length(vect1) != length(vect2)) {
    stop("错误:两个向量的长度必须相等才能计算曼哈顿距离。")
  }
  return(sum(abs(vect1 - vect2)))
}

方法 2:使用 R 内置的 dist() 函数与矩阵运算

当我们处理的数据量变大,特别是需要计算多个样本点两两之间的距离矩阵时,手动循环计算效率极低。R 语言提供了强大的基础函数 dist(),专门用于处理这类统计计算。在 2026 年,随着数据量的进一步爆炸,这种内置的 C 语言优化函数比以往任何时候都更显得重要。

#### 语法详解与参数说明

dist() 函数非常灵活,其基本语法如下:

dist(x, method = "euclidean", diag = FALSE, upper = FALSE, p = 2)

为了计算曼哈顿距离,我们需要关注以下关键点:

  • x:一个数据矩阵或数据框。每一行代表一个观测样本,每一列代表一个特征维度。
  • method:指定距离度量方式。为了计算曼哈顿距离,我们必须将其设置为字符串 INLINECODEa6e0e001。其他选项包括 INLINECODEab4616cf(默认)、"maximum" 等。
  • diag:逻辑值,控制是否输出对角线上的值(即样本到自身的距离,通常为 0)。
  • upper:逻辑值,控制是否输出上三角矩阵。默认仅输出下三角。

#### 示例 3:计算多向量的距离矩阵

让我们来看一个更接近实际数据集的例子。我们有一组包含 6 个特征的数据,涉及 6 个不同的样本向量。我们想知道每两个样本之间的曼哈顿距离。

# 初始化数据向量
# 模拟 6 个样本,每个样本有 6 个特征
vect1 <- c(1, 16, 8, 10, 100, 20)
vect2 <- c(1, 7, 18, 90, 50, 21)
vect3 <- c(3, 10, 11, 40, 150, 210)
vect4 <- c(2, 1, 4, 7, 8, 10)
vect5 <- c(1, 4, 8, 3, 100, 104)
vect6 <- c(3, 7, 11, 23, 110, 114)

# 使用 rbind 将向量按行组合成矩阵
# 这将创建一个 6行 x 6列 的矩阵
twoDimensionalVect <- rbind(vect1, vect2, vect3, vect4, vect5, vect6)

print("样本数据矩阵的前几行:")
head(twoDimensionalVect)

cat("
计算每对唯一向量之间的曼哈顿距离矩阵:
")

# 使用 dist 函数
# method="manhattan" 是关键
# 结果将存储为一个 'dist' 对象
distance_matrix <- dist(twoDimensionalVect, method="manhattan")

# 打印结果
print(distance_matrix)

输出结果分析:

控制台将输出一个对称的下三角矩阵。数字表示行与行对应样本之间的距离。

      1    2    3    4    5
2   104
3   194  182
4   105   9  201
5    54   86  188   99
6    90  122  124  119   68

例如,INLINECODE3c74c30f 和 INLINECODE8f3d9732 之间的距离是 104。这种矩阵形式是层次聚类和多维尺度分析(MDS)的标准输入格式。

2026 开发者视角:工程化与 AI 协作

作为 2026 年的开发者,我们不仅需要知道“怎么算”,还需要知道“怎么算得更好”、“怎么维护”以及“怎么利用 AI 加速”。让我们将视角从单纯的算法实现提升到现代软件工程的高度。

#### 生产级代码实现:数据清洗与异常处理

在现实世界的项目中,数据从来不会像教科书那样完美。我们经常遇到缺失值(INLINECODEcd70cd6e)或者非数值类型的数据。如果我们直接把脏数据丢给 INLINECODEe5a46f57 或自定义函数,结果往往会报错。

场景:处理包含缺失值的数据

# 模拟包含 NA 的真实数据
vect_a <- c(10, 20, NA, 40)
vect_b <- c(12, 22, 30, 42)

# 尝试使用基础函数计算
# sum(abs(vect_a - vect_b)) 
# 结果会是 NA,因为任何数与 NA 运算都是 NA

# 生产级解决方案:自动处理 NA
robustManhattanDist <- function(x, y, na.rm = TRUE) {
  # 1. 基础校验
  if(length(x) != length(y)) stop("Vector length mismatch.")
  
  # 2. 计算
  diff <- abs(x - y)
  
  # 3. 处理 NA:这里我们不仅求和,还记录有多少数据点被忽略了
  # 这是一个很好的“可观测性”实践
  valid_points <- sum(!is.na(diff))
  total_dist <- sum(diff, na.rm = na.rm)
  
  # 4. 反馈:如果 NA 过多,给出警告(而不是默默失败)
  if(valid_points < length(x) * 0.5) {
    warning(paste("警告:超过50%的数据是缺失值,距离计算可能不可靠。有效点数:", valid_points))
  }
  
  return(total_dist)
}

print(robustManhattanDist(vect_a, vect_b))

解析:

在这个版本中,我们加入了 na.rm 参数,并且当数据质量过差时会主动发出警告。这体现了“安全左移”的理念——在数据处理阶段就发现问题,而不是等到模型训练崩溃时再去调试。

#### 性能优化:向量化与并行计算

在处理数百万行数据时,循环是性能杀手。

场景:大规模矩阵计算

如果我们要计算一个 10,000 x 10,000 的距离矩阵,单线程的 INLINECODEac31f5be 可能会显得吃力。我们可以利用 R 的并行计算包(如 INLINECODEd5ecf32d 或 future)来加速。

# 这是一个演示并行计算理念的伪代码示例
# library(parallel)
# library(future)
# 
# # 开启多核支持
# plan(multisession)
# 
# # 使用 future.apply 替代传统的 apply
# # dist_matrix <- future_apply(large_data, 1, function(row) {
# #   apply(large_data, 1, function(other_row) sum(abs(row - other_row)))
# # })

虽然内置的 INLINECODEd1e11578 已经高度优化(底层是 C),但在自定义复杂距离度量时,理解如何利用 R 的向量化(INLINECODE6c2655d7)或并行库是 2026 年高级 R 用户的必修课。

#### AI 辅助开发实战:与 Cursor/Copilot 共舞

在编写上述代码时,我们完全可以让 AI(如 Cursor, GitHub Copilot, Windsurf)承担脏活累活。

提示词工程最佳实践:

  • 不要说: “帮我写个曼哈顿距离的代码。”(太泛)
  • 试试说: “我们有一个包含 NA 值的长整型向量列表。请写一个高效的 R 函数,计算列表中每两个向量之间的曼哈顿距离,要求自动跳过 NA,并且如果 NA 超过 30% 就打印数据 ID。请使用 INLINECODEa0cd221b 而不是 INLINECODEa409a996 循环以保证性能。”

AI 生成的代码通常需要我们审查的点:

  • 数据类型转换: AI 有时会忽略 as.numeric(),导致因子型数据计算出错。
  • 边界条件: AI 可能会忘记处理空列表输入。
  • 算法选择: 在高维空间中,曼哈顿距离确实比欧氏距离更稳健,这一点 AI 通常能给出正确的建议,但我们需要验证其是否考虑了具体的数据分布。

常见陷阱与替代方案

在我们的项目经验中,总结了一些容易踩的坑:

  • 数据未标准化: 如果你的特征 A 范围是 0-1,特征 B 范围是 0-1000,那么特征 B 会完全主导曼哈顿距离的结果。必须先进行归一化或标准化。
  • 高维稀疏数据: 对于文本数据(TF-IDF 向量),曼哈顿距离计算的是“非重叠词数”的总和。这在某些情况下是有意义的,但要注意计算量。
  • 替代方案——余弦相似度: 在文本分析中,我们有时会发现余弦相似度比曼哈顿距离更能反映语义的相似性。不要因为手里有锤子(曼哈顿距离)就看什么都是钉子。

总结

在这篇文章中,我们一同探索了在 R 语言中计算曼哈顿距离的多种方法,并前瞻了 2026 年的技术环境。

  • 基础扎实:无论是 INLINECODE62dadf8e 还是 INLINECODE0325eb67,核心逻辑不变。
  • 工程思维:加入了 NA 处理、数据校验和性能考量,这才是生产级代码的样子。
  • 拥抱未来:利用 AI 辅助编程(Vibe Coding)可以让我们从繁琐的语法细节中解放出来,将精力集中在业务逻辑的构建数据策略的制定上。

下一步,我们建议你尝试将这个距离计算应用到你自己的数据集中,或者尝试使用 hclust 结合距离矩阵进行一次层次聚类分析。别忘了,让 AI 帮你写好数据加载和可视化的部分,你只需要专注于核心的算法逻辑即可。祝你编码愉快!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/32333.html
点赞
0.00 平均评分 (0% 分数) - 0