R语言模糊去重内存不足问题求解(支持Cluster分组)
解决R中大规模数据集模糊去重的内存问题
示例数据集
address = c("882 4N Road River NY, NY 12345", "882 - River Road NY, ZIP 12345", "123 Fake Road Boston Drive Boston", "123 Fake - Rd Boston 56789") name = c("ABC Center Building", "Cent. Bldg ABC", "BD Home 25 New", "Boarding Direct 25") cluster = c("A", "A", "B", "B") my_data = data.frame(address, name, cluster)
数据集输出:
address name cluster 1 882 4N Road River NY, NY 12345 ABC Center Building A 2 882 - River Road NY, ZIP 12345 Cent. Bldg ABC A 3 123 Fake Road Boston Drive Boston BD Home 25 New B 4 123 Fake - Rd Boston 56789 Boarding Direct 25 B
目标
移除数据集中的模糊重复数据——人工可识别上述数据仅含2条唯一记录,但计算机无法直接判定,需通过模糊匹配技术处理。
此前尝试的方法及内存错误
处理10万行真实数据时,全量对比的方法因内存占用过大报错:
library(dplyr) library(tidyr) library(stringdist) # 方法1 my_data_dists <- my_data %>% mutate(row = row_number()) %>% full_join(., ., by = character()) %>% filter(row.x < row.y) %>% mutate( address.dist = stringdist(address.x, address.y), name.dist = stringdist(name.x, name.y) ) %>% arrange(scale(address.dist) + scale(name.dist)) %>% relocate( row.x, row.y, address.dist, name.dist, address.x, address.y, name.x, name.y ) # 报错:Error: cannot allocate vector of size 237.6 Gb # 方法2 name_dists <- adist(my_data$name) # 报错:Error: cannot allocate vector of size 475.3 Gb
需要完成的测试
- 测试1:基于名称和地址移除模糊重复数据
- 测试2:仅基于地址移除模糊重复数据
注:可借助cluster字段,在每个分组内执行去重以减少计算量(例如从4C2降至2C2)。
可行解决方案
核心思路:利用cluster分组,将大数据集拆分为小分组后再进行模糊匹配,避免全量两两对比导致的内存爆炸。结合字符串标准化提升匹配精度,使用高效的距离算法降低计算成本。
通用预处理(可选)
先对字符串做标准化处理,减少无关字符干扰,提升匹配准确性:
library(dplyr) library(stringdist) library(stringr) # 字符串标准化函数 standardize_str <- function(x) { x %>% str_to_lower() %>% str_remove_all("[^a-zA-Z0-9\\s]") %>% # 移除特殊字符 str_replace_all("\\s+", " ") %>% # 合并多个空格 str_replace_all("rd", "road") %>% # 替换常见缩写 str_replace_all("bldg", "building") %>% str_trim() # 去除首尾空格 } # 应用标准化到数据集 my_data <- my_data %>% mutate( address_std = standardize_str(address), name_std = standardize_str(name) )
测试1:基于名称+地址的分组模糊去重
在每个cluster分组内,结合地址和名称的字符串距离判定重复,保留每组内的第一条有效记录:
# 分组内模糊重复判定函数 find_fuzzy_duplicates <- function(df, addr_thresh = 0.1, name_thresh = 0.1) { n <- nrow(df) if (n <= 1) return(df) # 计算Jaro-Winkler距离(适合短字符串匹配,范围0-1,值越小越相似) addr_dist <- stringdistmatrix(df$address_std, df$address_std, method = "jw") name_dist <- stringdistmatrix(df$name_std, df$name_std, method = "jw") # 合并距离并排除自身对比 combined_dist <- addr_dist + name_dist diag(combined_dist) <- Inf # 标记重复行 duplicates <- apply(combined_dist, 1, function(x) any(x < (addr_thresh + name_thresh))) # 保留非重复行(可根据需求调整保留规则,比如保留最完整的记录) df %>% filter(!duplicates) %>% slice(1) } # 按cluster分组处理 result_test1 <- my_data %>% group_by(cluster) %>% group_modify(~find_fuzzy_duplicates(.x)) %>% ungroup() %>% select(address, name, cluster) # 保留原字段 print(result_test1)
测试2:仅基于地址的分组模糊去重
仅使用地址字段进行模糊匹配,逻辑与测试1一致:
# 基于地址的重复判定函数 find_address_duplicates <- function(df, addr_thresh = 0.1) { n <- nrow(df) if (n <= 1) return(df) addr_dist <- stringdistmatrix(df$address_std, df$address_std, method = "jw") diag(addr_dist) <- Inf duplicates <- apply(addr_dist, 1, function(x) any(x < addr_thresh)) df %>% filter(!duplicates) %>% slice(1) } # 按cluster分组处理 result_test2 <- my_data %>% group_by(cluster) %>% group_modify(~find_address_duplicates(.x)) %>% ungroup() %>% select(address, name, cluster) print(result_test2)
关键优化点
- 分组计算:利用
cluster拆分数据集,每个分组内的两两对比量大幅降低,彻底避免内存溢出。 - 字符串标准化:预处理消除无关字符、统一格式,提升匹配准确性的同时降低计算复杂度。
- 高效距离算法:Jaro-Winkler距离相比传统编辑距离,更适合地址、名称这类短字符串的模糊匹配,计算速度更快。
- 阈值可调:根据实际数据调整距离阈值,Jaro-Winkler距离0表示完全匹配,0.1以内通常可判定为高度相似的模糊重复。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

