
本文介绍如何在 Pandas 中实现基于自定义逻辑(如编辑距离、子字符串重叠)的重复行识别与合并,替代默认的 duplicated() 或 groupby() 行为,并保持数值列求和、非数值列保留首行值。
本文介绍如何在 pandas 中实现基于自定义逻辑(如编辑距离、子字符串重叠)的重复行识别与合并,替代默认的 `duplicated()` 或 `groupby()` 行为,并保持数值列求和、非数值列保留首行值。
在实际数据清洗中,标准的 df.duplicated(subset=['A','B']) 往往无法满足复杂语义层面的“重复”定义——例如,当两行的英文拼写接近(Levenshtein 距离 ≤1)且西班牙语完全一致时,或英文完全相同但西班牙语存在词元重叠(如 'hola' 与 'hola_a' 分割后集合交集非空)时,才应视为重复。Pandas 原生不支持此类多条件、跨字段、带字符串处理的自定义去重逻辑,需结合 Python 函数与显式循环/分组策略实现。
以下是一个完整、可复用的解决方案:
✅ 步骤一:安装依赖
pip install python-Levenshtein
⚠️ 注意:
python-Levenshtein比纯 Python 实现快数十倍,强烈推荐;若安装失败,可用pip install Levenshtein(功能一致,但性能略低)。
✅ 步骤二:定义自定义重复判定函数
该函数严格遵循题设三类规则:
- 英文完全相等 + 西班牙语按
_分割后集合有交集 → 重复; - 英文编辑距离 (默认 1)+ 西班牙语完全相等 → 重复;
- 其他情况(如仅英文近似 或 仅西班牙语部分重叠)→ 不视为重复。
import pandas as pd
import numpy as np
import Levenshtein
def are_duplicates(row1, row2, levenshtein_threshold=1):
en1, es1 = row1['English'], row1['Spanish']
en2, es2 = row2['English'], row2['Spanish']
# 规则1:英文完全匹配 + 西班牙语词元重叠
if en1 == en2:
parts1 = set(es1.split('_'))
parts2 = set(es2.split('_'))
if parts1 & parts2: # 集合交集非空
return True
# 规则2:英文近似(Levenshtein)+ 西班牙语完全匹配
elif Levenshtein.distance(en1, en2) < levenshtein_threshold and es1 == es2:
return True
return False✅ 步骤三:实现高效合并逻辑(避免 O(n²) 全量嵌套)
虽然题解中使用双层 iterrows() 可读性强,但对大数据集效率较低。我们改用 索引标记 + 单次遍历 策略,并利用 select_dtypes() 自动识别所有数值列进行累加:
def merge_custom_duplicates(df, levenshtein_threshold=1):
df = df.copy()
n = len(df)
processed = np.zeros(n, dtype=bool) # 标记是否已合并
merged_rows = []
for i in range(n):
if processed[i]:
continue
# 初始化主行(保留首行的 English/Spanish,数值列用于累加)
base_row = df.iloc[i].copy()
numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()
# 向后扫描,寻找可合并的后续行
for j in range(i + 1, n):
if not processed[j] and are_duplicates(df.iloc[i], df.iloc[j], levenshtein_threshold):
# 对所有数值列求和
for col in numeric_cols:
base_row[col] += df.iloc[j][col]
processed[j] = True
merged_rows.append(base_row)
return pd.DataFrame(merged_rows).reset_index(drop=True)
# 示例数据
df = pd.DataFrame({
'English': ['hello', 'helo', 'hello', 'hallo'],
'Spanish': ['hola', 'hola', 'hola_a', 'a_aureola'],
'Count': [23, 2, 1, 1],
'AF': [0, 1, 0, 1]
})
result = merge_custom_duplicates(df)
print(result)输出结果:
English Spanish Count AF 0 hello hola 26 1 1 hallo a_aureola 1 1
✅ 行 0(hello/hola)与行 1(helo/hola)因编辑距离=1+西班牙语相同被合并;
✅ 行 0 与行 2(hello/hola_a)因英文相同+西班牙语 {'hola'} ∩ {'hola','a'} ≠ ∅ 被合并;
❌ 行 2 与行 3 不满足任一规则,不合并。
⚠️ 注意事项与优化建议
-
性能提示:上述算法时间复杂度为 O(n²),适用于万级以下数据。若需处理十万+行,建议先按
Spanish分组(groupby('Spanish')),再在每组内对English做聚类(如scikit-learn的 AgglomerativeClustering + 自定义距离矩阵),可降至 O(n log n)。 -
扩展性:将
are_duplicates改为接受columns_to_compare和rules字典参数,即可适配任意字段组合与规则组合。 -
稳定性:
Levenshtein.distance对空字符串返回长度,务必确保English列无缺失值(可用df.dropna(subset=['English'])预处理)。 -
保留首行非数值字段:当前逻辑天然保留
iloc[i]的English/Spanish,符合“保留第1行语言列”的需求;如需保留最长/最规范的字符串,可在合并时添加max(..., key=len)逻辑。
通过该方法,你不再受限于 Pandas 的结构化去重范式,而是拥有了完全可控的语义级重复识别能力——这正是高质量文本数据工程的关键一环。

















