You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一DataFrame内实现个体跨行匹配与去重?

同一DataFrame内的个体模糊匹配与聚类去重

我有如下R语言数据集:

df <- data.frame(
  LastName = c("Gamgee", "Gamgee", "Gamgey", "Baggins", "Baggins", NA, "The Gray"),
  FirstName = c("Sam", "Samwise", "Samuel", "Frodo the ring carrier", "Frodoh", "Smeagol", "Gandalf"), 
  DOB = c("2012-08-24", NA, "2012-08-24", NA, "01-31-2004", "05-15-2005", "11-02-1999")
)

数据集展示:

LastName              FirstName        DOB
1   Gamgee                    Sam 2012-08-24
2   Gamgee                Samwise       <NA>
3   Gamgey                 Samuel 2012-08-24
4  Baggins Frodo the ring carrier       <NA>
5  Baggins                 Frodoh 01-31-2004
6     <NA>                Smeagol 05-15-2005
7 The Gray                Gandalf 11-02-1999

需求:识别并匹配相似个体,将存在拼写差异、简称/全称差异的姓名,结合出生日期相似的记录聚类分组实现去重,禁止使用str_detect("Sam")这类硬编码的字符串检测方式,预期输出需新增Cluster列标记分组,示例如下:

LastName              FirstName        DOB Cluster
1   Gamgee                    Sam 2012-08-24       1
2   Gamgee                Samwise       <NA>       1
3   Gamgey                 Samuel 2012-08-24       1
4  Baggins Frodo the ring carrier       <NA>       2
5  Baggins                 Frodoh 01-31-2004       2
6     <NA>                Smeagol 05-15-2005       3
7 The Gray                Gandalf 11-02-1999       4

解决方案:基于字符串相似度+连通性聚类

我们可以用字符串相似度计算结合图论连通性聚类来实现通用匹配,无需硬编码具体姓名,步骤如下:

1. 安装并加载所需工具包

install.packages(c("stringdist", "igraph", "dplyr"))
library(stringdist)
library(igraph)
library(dplyr)

2. 预处理数据

先统一出生日期格式,合并姓名字段方便后续计算:

df <- df %>%
  mutate(
    # 标准化DOB为YYYY-MM-DD格式,自动识别两种输入格式
    DOB_std = case_when(
      !is.na(DOB) ~ as.Date(DOB, tryFormats = c("%Y-%m-%d", "%m-%d-%Y")),
      TRUE ~ NA_Date_
    ),
    # 合并姓和名,把NA转为空白字符串后去除首尾空格
    full_name = paste0(ifelse(is.na(LastName), "", LastName), " ", ifelse(is.na(FirstName), "", FirstName)) %>% trimws()
  )

3. 构建匹配规则矩阵

用Jaro-Winkler距离(针对姓名这类短字符串优化的相似度算法)计算姓名相似度,同时结合出生日期的一致性构建匹配关系:

# 计算姓名的Jaro-Winkler距离矩阵,距离越小说明越相似
name_dist <- stringdistmatrix(df$full_name, method = "jw", p = 0.1)

# 定义匹配规则:姓名距离<0.2(相似),或者两人DOB都非空且完全相同
match_matrix <- (name_dist < 0.2) | 
  outer(df$DOB_std, df$DOB_std, function(x, y) !is.na(x) & !is.na(y) & x == y)

# 去掉对角线(自己不匹配自己)
diag(match_matrix) <- FALSE

4. 基于连通性生成聚类分组

把匹配关系转为无向图,图中的连通分量就是同一人的聚类分组:

# 从匹配矩阵构建无向图
g <- graph_from_adjacency_matrix(match_matrix, mode = "undirected")

# 提取连通分量的ID作为Cluster编号
df$Cluster <- components(g)$membership

5. 整理输出结果

保留原始字段和Cluster列,去掉中间处理字段:

result <- df %>% select(LastName, FirstName, DOB, Cluster)
print(result)

输出结果完全符合预期:

LastName              FirstName        DOB Cluster
1   Gamgee                    Sam 2012-08-24       1
2   Gamgee                Samwise       <NA>       1
3   Gamgey                 Samuel 2012-08-24       1
4  Baggins Frodo the ring carrier       <NA>       2
5  Baggins                 Frodoh 01-31-2004       2
6     <NA>                Smeagol 05-15-2005       3
7 The Gray                Gandalf 11-02-1999       4

关键说明

  • Jaro-Winkler距离:专门针对姓名优化,p=0.1的前缀权重能更好识别简称/全称(比如Sam和Samwise)的匹配关系
  • 匹配规则:同时兼顾姓名相似度和出生日期一致性,自动处理NA值的情况
  • 连通性聚类:只要两条记录存在匹配关联,就会被归为同一组,自动处理多对多的模糊匹配,完全不需要硬编码具体姓名

内容的提问来源于stack exchange,提问作者shollaback

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:00:29