如何按规则删除Pandas DataFrame中的非精确重复记录?
Pandas非精确重复数据清洗实现
问题场景
我们有如下Pandas DataFrame:
| Index | FirstName | Surname | Adress | Source |
|---|---|---|---|---|
| 1 | Paul | Baggins | same | good |
| 2 | Paaul | Baggins | same | bad |
| 3 | Mary | Baggins | same | good |
| 4 | Mary | Baggins | same | bad |
| 5 | Lucy | Smith | other | bad |
需要完成的清洗目标:
- 先筛选出住址(Adress)相同的记录(Adress作为家庭唯一标识)
- 删除因数据源不同导致FirstName输入错误的潜在重复项(即索引2、4的记录)
已尝试方法局限
用df.drop_duplicates(subset=['FirstName','Surname', 'Adress'], keep='first')只能删除索引4这种精确重复的记录,处理不了像索引2中Paul和Paaul这种非精确的重复。
已经实现了基于difflib的文本相似度计算函数:
from difflib import SequenceMatcher def similar(a, b): return SequenceMatcher(None, a, b).ratio()
比如similar('Paul', 'Paaul')返回0.888,但不知道怎么把这个逻辑整合到去重流程里。
期望实现流程
先生成包含Similar_to_Index(匹配到的目标索引)和Ratio(相似度)列的中间DataFrame,再按照**相似度Ratio>0.8且Source为'bad'**的规则删除索引2、4,最终得到两个结果:
- 清洗后的DataFrame
- 被删除记录的DataFrame
具体实现代码
步骤1:导入依赖并初始化数据
import pandas as pd from difflib import SequenceMatcher # 初始化示例DataFrame data = { 'Index': [1,2,3,4,5], 'FirstName': ['Paul', 'Paaul', 'Mary', 'Mary', 'Lucy'], 'Surname': ['Baggins', 'Baggins', 'Baggins', 'Baggins', 'Smith'], 'Adress': ['same', 'same', 'same', 'same', 'other'], 'Source': ['good', 'bad', 'good', 'bad', 'bad'] } df = pd.DataFrame(data).set_index('Index') # 相似度计算函数 def similar(a, b): return SequenceMatcher(None, a, b).ratio()
步骤2:生成中间匹配结果
按Adress和Surname分组,在每组内计算每条记录与同组其他记录的相似度,找到最匹配的记录索引和对应相似度:
# 初始化新增列 df['Similar_to_Index'] = None df['Ratio'] = 0.0 # 按家庭标识分组处理 for (adress, surname), group in df.groupby(['Adress', 'Surname']): # 遍历组内每条记录 for idx, row in group.iterrows(): # 排除自身,计算与组内其他记录的相似度 others = group.drop(idx) if not others.empty: # 计算当前记录与其他记录的FirstName相似度 others['current_ratio'] = others['FirstName'].apply(lambda x: similar(row['FirstName'], x)) # 找到相似度最高的记录 max_row = others.loc[others['current_ratio'].idxmax()] df.loc[idx, 'Similar_to_Index'] = max_row.name df.loc[idx, 'Ratio'] = max_row['current_ratio']
此时中间DataFrame结果如下:
| Index | FirstName | Surname | Adress | Source | Similar_to_Index | Ratio |
|---|---|---|---|---|---|---|
| 1 | Paul | Baggins | same | good | 2 | 0.888... |
| 2 | Paaul | Baggins | same | bad | 1 | 0.888... |
| 3 | Mary | Baggins | same | good | 4 | 1.0 |
| 4 | Mary | Baggins | same | bad | 3 | 1.0 |
| 5 | Lucy | Smith | other | bad | None | 0.0 |
步骤3:筛选并分离待删除记录
根据规则筛选出要删除的记录,同时保留清洗后的数据集:
# 筛选待删除条件:相似度>0.8 且 Source为'bad' to_drop = df[(df['Ratio'] > 0.8) & (df['Source'] == 'bad')] # 清洗后的数据集:排除待删除记录 cleaned_df = df.drop(to_drop.index) # 重置索引(可选,根据需求调整) cleaned_df = cleaned_df.reset_index(drop=False) to_drop = to_drop.reset_index(drop=False)
最终结果
- 清洗后的DataFrame:
| Index | FirstName | Surname | Adress | Source | Similar_to_Index | Ratio |
|---|---|---|---|---|---|---|
| 0 | 1 | Paul | Baggins | same | good | 2 |
| 1 | 3 | Mary | Baggins | same | good | 4 |
| 2 | 5 | Lucy | Smith | other | bad | None |
- 被删除的记录DataFrame:
| Index | FirstName | Surname | Adress | Source | Similar_to_Index | Ratio |
|---|---|---|---|---|---|---|
| 0 | 2 | Paaul | Baggins | same | bad | 1 |
| 1 | 4 | Mary | Baggins | same | bad | 3 |
补充说明
- 如果组内存在多条高相似度记录,可以根据实际需求调整匹配逻辑(比如优先保留
Source='good'的记录作为匹配基准) - 相似度阈值(0.8)可以根据业务场景灵活调整
内容的提问来源于stack exchange,提问作者Poldi
相关产品推荐
相关产品推荐

