Pandas处理160万行数据时如何高效归并拼写变体重复业主名
160万行业主实体归并性能优化方案
问题背景
- 处理对象为160万行建筑规范违规罚单数据集,核心字段为
owner_name(业主姓名),其余字段包含开单机构、建筑地址、建筑邮编、业主邮寄地址、业主邮编、违规详情、罚金信息等 - 数据存在大量同一主体的姓名拼写变体:例如Wells Fargo、Mortgage Electronic Registration Systems等实体均存在语序差异、标点错误、拼写偏差等多种记录形式
- 核心需求:无需将姓名修正为标准拼写,仅需归并同一主体,统计各业主对应的罚单总数量
- 现有方案匹配准确率符合预期,但全量数据清理耗时长达3小时,需提速
现有实现逻辑
当前方案基于「邮编字段错误率远低于业主姓名字段」的假设做分层处理:
- 首先按
zip_code字段分组,共生成3000余个邮编组,单组数据量区间为1~7000行,分组代码:groups = trandf01.groupby('zip_code', sort=False) - 逐组扫描,采用双层循环做组内两两比对:每条姓名记录与其后所有记录计算相似度,当
fuzz.token_set_ratio(aa,bb) > 74时判定为同一主体,将后续记录的姓名替换为靠前的基准姓名。
完整现有代码:
import numpy as np from fuzzywuzzy import fuzz import pandas as pd trandf01 = pd.read_csv('data.csv') groups = trandf01.groupby('zip_code', sort=False) keys = groups.groups.keys() df2 = pd.DataFrame() for k in keys: df=groups.get_group(k) if len(df.index)>2: df.reset_index(inplace=True) for i in range(len(df.index)): aa=df.iat[i,4] for j in range(i+1,len(df.index)): bb=df.iat[j,4] ratio=fuzz.token_set_ratio(aa,bb) if ratio > 74: df.iat[j,4]=aa df2=pd.concat([df2,df]) df2.to_excel("TextCorrectedNEW.xlsx")
现有方案核心性能瓶颈为单组O(n²)的全量两两比对逻辑,加上纯Python实现的模糊匹配库、循环内反复操作DataFrame、循环内反复拼接DataFrame等额外开销,最终导致整体耗时过长。
可落地的提速方案
按收益从高到低排序,落地后整体耗时可以从3小时压缩到10分钟以内:
- 替换模糊匹配底层库:将纯Python实现的
fuzzywuzzy替换为C++实现、接口完全兼容的rapidfuzz,单条token_set_ratio计算速度可提升5~10倍,无需修改核心比对逻辑即可拿到第一波提速 - 砍掉无效的全量两两比对:
- 比对前先做姓名标准化预处理:统一转小写、移除所有标点和多余空格、剥离无区分度的通用后缀(如Inc、LLC、Corp、NA、Trust等),生成标准化比对字符串
- 基于倒排索引召回候选:对每个标准化后的姓名按词切分,建立「分词 -> 对应姓名索引列表」的倒排表,比对时仅召回和当前姓名有至少1个共同分词的记录计算相似度,直接过滤掉完全不可能匹配的候选,单组比对量可从O(n²)降到接近线性水平
- 用并查集(DSU)做实体归并:替换现有「匹配到就直接覆盖姓名」的逻辑,比对时只要两个姓名相似度达标,就把二者的索引并入同一个集合,所有比对完成后再统一为每个集合分配同一个实体标签,避免重复比对已经归并过的记录
- 消除Pandas循环开销:组内处理时先将
owner_name列转为普通Python列表再做循环,避免循环内反复调用iat读写DataFrame;所有分组处理完成后存入列表,最后一次性做pd.concat合并结果,消除循环内反复拼接DataFrame产生的大量拷贝开销 - 大组二次分块:针对单组7000行的大型邮编组,可按标准化姓名的长度、首字符做二次分块,仅在块内做相似度比对,进一步缩小候选比对范围
内容的提问来源于stack exchange,提问作者Andrea
相关产品推荐
相关产品推荐

