
本文介绍如何对 dataframe 中某字符串列(以逗号分隔)逐行解析为集合,两两计算元素交集大小,找出每行与其他所有行的最大匹配数及对应行 id 列表。
本文介绍如何对 dataframe 中某字符串列(以逗号分隔)逐行解析为集合,两两计算元素交集大小,找出每行与其他所有行的最大匹配数及对应行 id 列表。
在文本相似性分析或数据清洗场景中,常需比较同一列中不同行的字符串内容,识别“最相似”的记录。例如,当每行存储的是标签集合(如 'AA,AC,BC,DE'),我们希望量化其与其余各行的共同标签数量,并汇总出最大匹配数(max_matching#)及对应的匹配行 ID 列表(Rowid)。下面将提供一套基于 pandas、scipy.spatial.distance.pdist 和集合运算的高效实现方案。
✅ 核心思路
- 字符串 → 集合:将 Col1 每行按 , 分割并转为 set,消除重复、提升交集计算效率;
- 两两交集计数:定义自定义距离函数(实为相似度函数),返回两集合交集长度;
- 成对矩阵构建:使用 pdist + squareform 生成对称的交集计数矩阵;
- 聚合统计:过滤掉自匹配(rowid == Rowid)和零匹配项,按原行分组,取最大交集数,并拼接所有达到该最大值的行 ID。
? 完整代码实现
import pandas as pd
from scipy.spatial.distance import pdist, squareform
# 构造示例数据
data = [[1, 'AA,AC,BC,DE'], [2, 'AA,AD,BC,D'], [3, 'A,C,BC,E'], [4, 'AA,AC,BC,DEEE'], [5, 'KK']]
df = pd.DataFrame(data, columns=['rowid', 'Col1'])
# 步骤1:将 Col1 转为 set 列表
s = df['Col1'].apply(lambda x: set(x.split(',')))
# 步骤2:定义交集长度函数(注意 pdist 默认计算“距离”,此处返回正向相似度)
def intersection_size(a, b):
return len(a[0] & b[0])
# 步骤3:计算成对交集矩阵(squareform 输出方阵)
similarity_matrix = pd.DataFrame(
squareform(pdist(s.to_frame(), metric=intersection_size)),
index=df['rowid'],
columns=df['rowid'].rename('Rowid')
)
# 步骤4:展开矩阵,过滤自匹配与零匹配,分组聚合
intermediate = (similarity_matrix
.stack() # 转为 (rowid, Rowid) → count
.reset_index(name='count')
.query('(rowid != Rowid) & (count > 0)') # 排除自身和无匹配
)
# ✅ 关键:仅保留每个 rowid 下 count 等于其最大值的记录(即“所有最优匹配行”)
result = (intermediate
.loc[intermediate.groupby('rowid')['count'].transform('max').eq(intermediate['count'])]
.groupby('rowid', as_index=False)
.agg({
'count': 'max',
'Rowid': lambda x: ','.join(map(str, sorted(x))) # 可选:排序使结果稳定
})
.rename(columns={'count': 'max_matching#'}))
# 步骤5:与原始 df 合并,补全无匹配行(max_matching#=0, Rowid='NA')
df_out = df.merge(result, on='rowid', how='left')
df_out['max_matching#'] = df_out['max_matching#'].fillna(0).astype(int)
df_out['Rowid'] = df_out['Rowid'].fillna('NA')
print(df_out)
? 输出结果
rowid Col1 max_matching# Rowid 0 1 AA,AC,BC,DE 3 4 1 2 AA,AD,BC,D 2 1,4 2 3 A,C,BC,E 1 1,2,4 3 4 AA,AC,BC,DEEE 3 1 4 5 KK 0 NA
? 说明:第1行 'AA,AC,BC,DE' 与第4行 'AA,AC,BC,DEEE' 有 AA,AC,BC 共3个精确匹配项(注意 DE ≠ DEEE),故 max_matching# = 3,且仅第4行达到该值,因此 Rowid = '4'。
⚠️ 注意事项
- 大小写与空格敏感:当前逻辑区分大小写且不自动 strip 空格。若需鲁棒性,预处理建议添加 .str.strip().str.upper();
- 性能提示:pdist 对 N 行数据时间复杂度为 O(N²),适用于数千行以内;超大规模建议改用近似算法(如 MinHash + LSH);
- 空值处理:本例假设 Col1 无缺失值;如有,需提前用 fillna('') 或跳过;
- 匹配逻辑:严格基于子字符串相等(非模糊匹配),如需编辑距离或语义相似度,应替换为 fuzzywuzzy 或 sentence-transformers。
通过该方法,你可快速构建列内行间结构化相似性指标,支撑去重、聚类、推荐等下游任务。










