Pandas中基于字符串相似度匹配列时NaN值未正确生成的问题排查
解决模糊匹配未生成预期NaN及错误保留低匹配结果的问题
问题根源
你的代码现在有两个核心问题导致结果不符合预期:
缺少匹配度阈值判断
你当前的逻辑只是在dico2['Surname']里找出和当前Name匹配度最高的项——不管这个匹配度有多低。比如Zadig和Patrice4的匹配度虽然极低,但在给定的四个Surname里它是最高的,所以代码就把它作为Zadig的Surname返回了,完全没考虑"这个匹配是否足够靠谱"。错误的NaN处理逻辑
你的代码里从来没有生成过真正的NaN值(你只是最后试图删除等于字符串"NaN"的行),但map_list里只会出现dico2里的Surname值,所以那行删除代码根本不起作用。
看这段关键的循环代码就清楚了:
map_list = [] for name in dico['Name']: best_ratio = None for idx, surname in enumerate(dico2['Surname']): if best_ratio == None: best_ratio = fuzz.ratio(name, surname) best_idx = 0 else: ratio = fuzz.ratio(name, surname) if ratio > best_ratio: best_ratio = ratio best_idx = idx map_list.append(dico2['Surname'][best_idx]) # 不管匹配度高低,硬塞一个值
这里没有任何逻辑判断"这个最佳匹配是否合格",所以哪怕匹配度只有10,也会把对应的Surname加进去。
修复方案
我们需要添加匹配度阈值,并且正确处理空值,下面是完整的修正代码:
import pandas as pd from fuzzywuzzy import fuzz import numpy as np # 原始数据 dico = {'Name': ['Arthur','Henri','Lisiane','Patrice','Zadig','Sacha'], "Age": ["20","18","62","73",'21','20'], "Studies": ['Economics','Maths','Psychology','Medical','Cinema','CS'] } dico2 = {'Surname': ['Arthur1','Henri2','Lisiane3','Patrice4']} # 转成DataFrame(建议直接用DataFrame操作,比字典更方便) df = pd.DataFrame(dico) surname_df = pd.DataFrame(dico2) map_list = [] # 设定匹配度阈值,这里用80,你可以根据实际情况调整(比如名字和姓氏前缀匹配度高的话可以设高一点) MATCH_THRESHOLD = 80 for name in df['Name']: best_ratio = -1 best_surname = np.nan # 默认空值 for surname in surname_df['Surname']: current_ratio = fuzz.ratio(name, surname) if current_ratio > best_ratio: best_ratio = current_ratio # 只有当匹配度超过阈值时,才更新为对应的姓氏 if best_ratio >= MATCH_THRESHOLD: best_surname = surname else: # 匹配度不够,保持空值 best_surname = np.nan map_list.append(best_surname) # 添加Surname列到原DataFrame df['Surname'] = map_list # 调整列顺序 df = df[["Name", "Surname", "Age", "Studies"]] # 删除Surname为空的行 df = df.dropna(subset=['Surname']) print(df)
关键改进点
- 阈值判断:新增了
MATCH_THRESHOLD,只有当模糊匹配度超过这个值时,才会把对应的Surname赋值给Name,否则保留np.nan(pandas能识别的空值)。 - 正确处理空值:用
dropna(subset=['Surname'])直接删除空值行,这是pandas处理空值的标准方式,比判断字符串"NaN"靠谱得多。 - 代码优化:把字典直接转成DataFrame操作,逻辑更清晰,也符合pandas的使用习惯。
运行结果
执行后会得到你预期的输出:
Name Surname Age Studies 0 Arthur Arthur1 20 Economics 1 Henri Henri2 18 Maths 2 Lisiane Lisiane3 62 Psychology
内容的提问来源于stack exchange,提问作者Arthur Langlois
相关产品推荐
相关产品推荐

