影刀RPA 数据去重的七种方法:从简单到复杂的选型
作者:林焱
什么情况用这个
你从三个平台采集了商品数据,合并后发现有重复。Excel里的订单记录导入了两次,客户名单里有同名同姓不同人、也有同一人多条记录。
数据去重听起来简单——不就drop_duplicates()嘛?实际远不止。不同场景需要不同的去重策略:有时候全字段相同才是重复,有时候只要ID相同就算重复;有时候要保留第一条,有时候要合并两条记录的信息。这篇文章从简单到复杂,把七种去重方法的使用场景和选择逻辑讲清楚。
怎么做
方法一:集合去重(最简单)
适合:单列数据、全字段重复的场景。
# 字符串列表去重names=["张三","李四","张三","王五","李四"]unique_names=list(set(names))print(unique_names)# ['张三', '李四', '王五'](顺序不保证)# 保持原顺序的去重seen=set()unique_ordered=[]fornameinnames:ifnamenotinseen:seen.add(name)unique_ordered.append(name)print(unique_ordered)# ['张三', '李四', '王五']方法二:字典键去重(按关键字段)
适合:多个字段中只有某个字段决定是否重复(如ID)。
records=[{"id":"001","name":"商品A","price":99},{"id":"002","name":"商品B","price":199},{"id":"001","name":"商品A(新)","price":109},# 同ID,信息更新了]# 按ID去重,保留第一次出现的seen={}forrecordinrecords:key=record["id"][video(video-owL94VaT-1784132827886)(type-csdn)(url-https://live.csdn.net/v/embed/525010)(image-https://v-blog.csdnimg.cn/asset/f4faa587144cb7070f19e8b36813806b/cover/Cover0.jpg)(title-店群矩阵自动化突破运营极限!)]ifkeynotinseen:seen[key]=record unique_records=list(seen.values())# 如果需要保留最后出现的(更新的数据)forrecordinrecords:seen[record["id"]]=record# 直接覆盖方法三:多字段组合键去重
适合:需要多个字段组合才能判断重复。
# 按"姓名+手机号"组合去重contacts=[{"name":"张三","phone":"13800138000","email":"a@x.com"},{"name":"张三","phone":"13900139000","email":"b@x.com"},# 同名人不同手机{"name":"张三","phone":"13800138000","email":"c@x.com"},# 重复(同名同手机)]defdedup_by_keys(records,keys):"""按多个字段组合去重"""seen=set()result=[]forrecordinrecords:# 生成组合键composite_key=tuple(record.get(k,"")forkinkeys)ifcomposite_keynotinseen:seen.add(composite_key)result.append(record)returnresult unique=dedup_by_keys(contacts,["name","phone"])# 结果:张三+138... 和 张三+139... 两条方法四:模糊匹配去重
适合:文本不完全一样但基本是同一内容的场景。
importdifflibdeffuzzy_dedup(strings,threshold=0.85):"""模糊去重——相似度超过阈值的视为重复"""unique=[]forsinstrings:is_dup=Falseforexistinginunique:similarity=difflib.SequenceMatcher(None,s,existing).ratio()ifsimilarity>=threshold:is_dup=Truebreakifnotis_dup:unique.append(s)returnunique# 示例:公司名稍有不同的记录names=["深圳市腾讯计算机系统有限公司","深圳腾讯计算机系统有限公司",# 少了"市""深圳市腾讯科技公司",# 不同]unique_names=fuzzy_dedup(names,threshold=0.8)print(unique_names)# 前两条会去重注意:模糊匹配去重计算量大,数据量>1000条时建议先做精确去重缩小范围。
方法五:合并去重(取并集)
适合:两条重复记录互补的信息要合并。
defmerge_duplicates(records,key_field):"""合并重复记录——取所有字段的非空并集"""merged={}forrecordinrecords:key=record[key_field]ifkeynotinmerged:merged[key]=record.copy()else:# 合并非空字段existing=merged[key]forfield,valueinrecord.items():ifvalueandnotexisting.get(field):existing[field]=value# 用新值填充空字段returnlist(merged.values())# 示例records=[{"id":"001","name":"张三","phone":"13800138000","email":""},{"id":"002","name":"李四","phone":"","email":"lisi@qq.com"},{"id":"001","name":"张三","phone":"","email":"zhangsan@qq.com"},]merged=merge_duplicates(records,"id")# 结果:张三的信息现在有完整的phone和email方法六:时间优先去重
适合:同一ID有多条历史记录,保留最新的。
fromdatetimeimportdatetimedefkeep_latest(records,key_field,time_field):"""按关键字段去重,保留时间最新的"""latest={}forrecordinrecords:key=record[key_field]record_time=record.get(time_field)# 解析时间ifisinstance(record_time,str):try:record_time=datetime.strptime(record_time,"%Y-%m-%d %H:%M:%S")except:record_time=datetime.minifkeynotinlatest:latest[key]=recordelse:existing_time=latest[key].get(time_field)ifisinstance(existing_time,str):try:existing_time=datetime.strptime(existing_time,"%Y-%m-%d %H:%M:%S")except:existing_time=datetime.minifrecord_time>existing_time:latest[key]=recordreturnlist(latest.values())方法七:权重优先去重
适合:多源数据去重,不同来源的可信度不同。
defkeep_highest_quality(records,key_field,source_weights):""" 按来源权重去重——保留可信度最高的 source_weights: {"官方数据库": 10, "用户提交": 3, "公开采集": 1} """best={}forrecordinrecords:key=record[key_field]source=record.get("source","未知")weight=source_weights.get(source,0)ifkeynotinbest:best[key]=(weight,record)else:existing_weight,_=best[key]ifweight>existing_weight:best[key]=(weight,record)return[recordfor_,recordinbest.values()]有什么坑
坑一:set去重后顺序乱了
现象:用set()去重后列表顺序和原来不一样了(Python 3.7+的dict保持插入顺序但set不保证)。
解决:保持顺序用dict.fromkeys():
unique_ordered=list(dict.fromkeys(items))坑二:忽略了None和空字符串的区别
现象:None和""被判定为不同的key,导致没去掉重复。
解决:生成组合键时统一规范化空值:
temu店群自动化报活动案例
defnormalize_key(value):"""规范化键值——把各种空值统一"""ifvalueisNoneorstr(value).strip()=="":return"_EMPTY_"returnstr(value).strip()坑三:模糊去重在大数据集上超慢
现象:5000条数据做两两模糊比较,O(n²)复杂度,流程卡死。
解决:先精确去重,再按首字母或拼音分组缩小候选范围。或者用距离算法先聚类。
坑四:去重决策没有记录
现象:去重后不知道删了什么,出了问题没法回溯。
解决:记录去重日志:
defdedup_with_log(records,key_func):"""带去重日志"""seen={}duplicates=[]forrecordinrecords:key=key_func(record)ifkeyinseen:duplicates.append({"kept":seen[key],"removed":record})else:seen[key]=recordprint(f"去重:{len(records)}→{len(seen)}条, 移除{len(duplicates)}条重复")returnlist(seen.values()),duplicates坑五:业务上的"重复"和技术上的"重复"不一致
现象:程序认为商品ID不同就不是重复,但业务上同一个商品在不同平台有不同ID。
解决:这种情况需要建立"商品映射表"——先做技术去重(按字段),再做业务匹配(按规则),两者分开处理。
总结:去重方法的选择顺序——先用集合/dict按关键字段精确去重(最快),再根据业务需求选择保留策略(第一条、最新、合并、权重),最后才考虑模糊匹配(最慢)。大部分场景用方法一、二、三就够了。每做一次去重,把去重原因和数量记到日志里。