嵌套循环与嵌套if语句中的KeyError异常排查
问题分析与解决方案
让我来帮你拆解这个问题,你遇到的KeyError: 9和大量的"No match found"提示,其实是几个常见的pandas操作误区和循环逻辑漏洞导致的,我逐个给你说明:
1. KeyError的核心原因:错误的DataFrame赋值方式
你代码里用dgh[i]['crm_ref'] = accountnumber来赋值是链式索引的错误用法。dgh[i]会尝试从DataFrame中取名为i(这里是行索引9)的列,而不是定位第i行;当你的列名里没有9时,自然就抛出了KeyError。
正确的赋值方式应该用:
.loc[i, 'crm_ref']:通过行索引+列名定位赋值(最推荐).at[i, 'crm_ref']:针对单个标量值的快速赋值
2. 嵌套循环的变量冲突
你在父循环和所有子循环里都用了row变量:
for i, row in dgh.iterrows(): # ... for u, row in temp_key.iterrows(): # ...
这会直接覆盖父循环的row变量,虽然不是触发KeyError的直接原因,但会导致代码可读性极差,容易引发其他逻辑混乱,建议给子循环的行变量换个名字,比如temp_row。
3. 子循环过早退出,导致匹配失败
在每个子循环的else分支里,你直接写了break:
else: break
这意味着只要第一行不匹配,就直接退出子循环,根本不会检查temp_key或temp_city里的其他行——这就是为什么你会看到大量"No match found"的原因:明明可能有后续行能匹配,却被提前终止了。应该去掉这个else里的break,让循环遍历完所有可能的匹配项。
4. NaN判断的错误用法
你用math.isnan(dgh.iloc[i]['crm_ref'])来判断是否匹配成功,但如果crm_ref列的初始类型不是float(比如是object类型的空字符串),math.isnan会抛出TypeError。建议用pandas自带的pd.isna()来判断缺失值,兼容性更好。
修复后的完整代码
import pandas as pd from fuzzywuzzy import fuzz # 先确保dgh里有crm_ref列,没有就初始化 if 'crm_ref' not in dgh.columns: dgh['crm_ref'] = pd.NA for i, dgh_row in dgh.iterrows(): dgh_key = dgh_row['addresskey'] dgh_name = dgh_row['DGH_custname'] match_found = False # 第一步:按addresskey匹配 temp_key = crm.loc[crm['addresskey'] == dgh_key].reset_index(drop=True) for u, temp_row in temp_key.iterrows(): crm_name = temp_row['name'] accountnumber = temp_row['accountnumber'] # 合并三种模糊匹配条件,满足任一即匹配 if (fuzz.ratio(dgh_name.lower(), crm_name.lower()) >= 60 or fuzz.partial_ratio(dgh_name.lower(), crm_name.lower()) >= 90 or fuzz.token_set_ratio(dgh_name.lower(), crm_name.lower()) >= 90): dgh.loc[i, 'crm_ref'] = accountnumber match_found = True break # 找到匹配就退出子循环 if match_found: continue # 找到匹配就直接进入下一个父循环 # 第二步:按城市匹配 dgh_city = dgh_row['DGH_custcity'] temp_city = crm.loc[crm['address1_city'] == dgh_city].reset_index(drop=True) for y, temp_row in temp_city.iterrows(): crm_name = temp_row['name'] accountnumber = temp_row['accountnumber'] if (fuzz.ratio(dgh_name.lower(), crm_name.lower()) >= 60 or fuzz.partial_ratio(dgh_name.lower(), crm_name.lower()) >= 90 or fuzz.token_set_ratio(dgh_name.lower(), crm_name.lower()) >= 90): dgh.loc[i, 'crm_ref'] = accountnumber match_found = True break if not match_found: print(f'No match found for row {i}: {dgh_name}')
额外优化建议
- 尽量避免用
iterrows(),它的运行效率很低,对于大数据集可以用apply()或者向量化操作替代 - 可以把模糊匹配的逻辑封装成一个独立函数,减少代码重复
- 如果crm数据集很大,建议先对
addresskey和address1_city建立索引,能大幅提升查询速度
内容的提问来源于stack exchange,提问作者Fabio D'Elfant
相关产品推荐
相关产品推荐

