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

如何用data.table自身列修改列:统一组内姓名变体的唯一ID

问题描述

我有如下data.table数据:

dt <- data.table(
  group_id = c(1,1,1,2,2,2,3,3,3),
  team_id = c(1,1,2,3,4,4,5,5,6),
  person_id = c(1,2,3,4,4,5,6,7,8),
  person_name = c("Smith, J.", "Areta Franklin", "John Smith", "Robert Mitchum", "Robert Mitchum", "Cary Grant", "John Rambo", "Martin Sheen", "Rambo John")
)

同一人在一个团队中仅出现一次,但在同一组中可能多次出现且姓名存在变体(如group_id=1中的"Smith, J."和"John Smith")。我希望创建新列uniq_person_id,使同组内同一人的所有记录拥有相同值。

我已创建函数simil_names(name1,name2),用于返回姓名相似度得分,完全匹配得分为0,name2可为字符串或字符串向量。

尝试的代码及问题

最初尝试的代码:

return_id <- function(pid,tid,gid) {
  group <- dt[group_id == gid,]
  person <- as.character(group[person_id == pid & team_id == tid,list(person_name)])
  candidates <- group[uniq_person_id != 0,list(person_name,uniq_person_id)]
  new_id <- 0
  if (nrow(candidates) > 0) {
    candidates$score <- simil_names(person, candidates$person_name)
    if (min(candidates$score) < 2) {
      # 得分低于2时,获取最佳匹配人员的ID
      new_id <- as.numeric(head(candidates[score == min(candidates$score), list(uniq_person_id)],1))
    } 
  }
  
  # 否则生成新的递增ID
  if(new_id == 0) {
    new_id <- max(group[group_id == gid & team_id == tid,]$uniq_person_id) + 1
  }
  
  return(new_id)
  
}

dt$uniq_person_id <- 0
dt$uniq_person_id <- return_id(dt$person_id,dt$team_id, dt$group_id)

问题在于uniq_person_id列仅在函数执行完毕后才会修改,无法实时更新,导致逻辑失效。

后续尝试

先通过dt[, uniq_person_id := .N:1, by = .(group_id)]为每组每行定义不同值,再尝试以下代码:

return_id <- function(pid.v,tid.v,gid.v) {
  res.new_ids <- c()
  for(i in c(1:length(gid.v))) {
    gid <- as.numeric(gid.v[i])
    tid <- as.numeric(tid.v[i])
    pid <- as.numeric(pid.v[i])
    
    group <- dt[group_id == gid,]
    person <- as.character(group[person_id == pid & team_id == tid,list(person_name)])
    new_id <- as.numeric(dt[group_id == gid & team_id == tid & person_id == pid,list(uniq_person_id)])
    candidates <- group[uniq_person_id != new_id,list(person_name,uniq_person_id)]
    if (nrow(candidates) > 0) {
      candidates$score <- simil_names(person, candidates$person_name)
      if (min(candidates$score) < 2) {
        # 得分低于2时,替换为最佳匹配候选的ID
        new_id <- as.numeric(head(candidates[score == min(candidates$score), list(uniq_person_id)],1))
      } 
    }
    res.new_ids <- append(res.new_ids,new_id)
  }  
  return(res.new_ids)
}

该方法仍无效,因为无法跟踪uniq_person_id列中已完成的修改。最终只能逐行遍历dt,用另一个data.table存储修改后的ID,希望得到更优的data.table风格解决方案。

解决方案

可以利用连通分量思路处理同组内相似姓名的匹配,结合data.table高效分组操作,实时维护ID映射,避免逐行遍历的低效:

library(data.table)
library(igraph) # 用于计算连通分量,无需依赖可自行实现并查集逻辑

# 模拟simil_names函数(实际使用你自己的函数即可)
simil_names <- function(name1, name2) {
  normalize_name <- function(x) tolower(gsub("[^a-zA-Z]", "", x))
  stringdist::stringdist(normalize_name(name1), normalize_name(name2), method = "lv")
}

# 初始化临时ID
dt[, temp_id := .I]

# 按group_id分组生成uniq_person_id
dt[, uniq_person_id := {
  grp_dt <- .SD
  
  # 生成组内所有姓名两两配对
  name_pairs <- expand.grid(name1 = grp_dt$person_name, name2 = grp_dt$person_name, stringsAsFactors = FALSE)
  name_pairs <- name_pairs[name1 != name2, ]
  
  # 计算相似度得分
  name_pairs$score <- mapply(simil_names, name_pairs$name1, name_pairs$name2)
  
  # 筛选得分<2的配对,视为同一人
  matched_pairs <- name_pairs[score < 2, .(name1, name2)]
  
  # 构建图结构,用连通分量聚类相似姓名
  if (nrow(matched_pairs) > 0) {
    g <- graph_from_data_frame(matched_pairs, directed = FALSE)
    cluster_membership <- components(g)$membership
    name_cluster <- data.table(person_name = names(cluster_membership), cluster_id = cluster_membership)
  } else {
    name_cluster <- data.table(person_name = grp_dt$person_name, cluster_id = seq_len(nrow(grp_dt)))
  }
  
  # 合并聚类结果到组内数据
  grp_dt <- merge(grp_dt, name_cluster, by = "person_name", all.x = TRUE)
  # 为未参与配对的姓名分配独立ID
  grp_dt[is.na(cluster_id), cluster_id := seq_len(sum(is.na(cluster_id)))]
  
  # 转换为连续的组内唯一ID
  grp_dt[, cluster_id := match(cluster_id, unique(cluster_id))]
  
  grp_dt$cluster_id
}, by = group_id]

# 查看最终结果
print(dt)

关键说明:

  • 利用图的连通分量,自动将所有相似姓名归为同一簇,生成统一的uniq_person_id
  • 若不想依赖igraph,可实现**并查集(Union-Find)**数据结构来合并相似ID,逻辑更轻量化
  • 全程基于data.table分组操作,避免逐行修改的性能瓶颈,适合处理大规模数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 01:09:53