如何用Pandas的itertuples高效匹配DataFrame并填充指定列
高效实现DataFrame多列匹配并映射索引的方法
针对你的需求——匹配df和df_unique的one_one_3first与zero_zero_3first列,将匹配行的df_unique索引填入df的unique_no列,以下是两种高效的矢量化实现方案,完全替代慢到离谱的iterrows,同时避免itertuples的常见错误:
方案1:使用merge(性能最优,优先选择)
merge是pandas原生的矢量化操作,底层基于C实现,处理大数据集时速度比迭代快几个数量级。步骤如下:
- 给
df_unique添加一列存储自身索引(后续要映射到df) - 以两列为键合并两个DataFrame,将匹配到的索引赋值给
df的unique_no
import pandas as pd # 给df_unique添加索引列 df_unique['unique_no'] = df_unique.index # 执行左连接合并,仅保留需要的列避免冗余 merged = df.merge( df_unique[['one_one_3first', 'zero_zero_3first', 'unique_no']], on=['one_one_3first', 'zero_zero_3first'], how='left' ) # 将合并后的索引值赋值给原df的unique_no列 df['unique_no'] = merged['unique_no']
说明:how='left'确保df的所有行都被保留,未匹配到的行unique_no会填充为NaN,符合常规需求。
方案2:使用字典映射(灵活度更高)
如果需要更自定义的匹配逻辑,可以将df_unique的两列组合为元组键,创建索引映射字典,再批量映射到df:
# 创建映射字典:(one_one_3first, zero_zero_3first) -> df_unique索引 mapping = df_unique.reset_index().set_index( ['one_one_3first', 'zero_zero_3first'] )['index'] # 批量映射到df df['unique_no'] = df.apply( lambda x: mapping.get((x['one_one_3first'], x['zero_zero_3first']), pd.NA), axis=1 )
说明:这里的apply虽然是逐行操作,但比iterrows高效,因为字典查找是O(1)时间复杂度,且apply的内部优化比手动循环更好。
为什么iterrows慢?
iterrows是Python级别的逐行迭代,每一行都要创建Series对象,频繁的Python-C切换导致性能极低,数据量越大差距越明显。而上面两种方案都是矢量化操作,将计算交给pandas的底层C引擎执行,效率提升显著。
关于itertuples的常见错误
你用itertuples时出现的AttributeError或列填充错误,大概率是因为:
- 列名包含特殊字符(如下划线)时,
namedtuple的属性名可能与Python关键字冲突,或者你错误地访问了属性 - 直接修改
namedtuple的属性(它是不可变的),导致赋值失败
如果一定要用itertuples,正确写法需要先创建映射字典,再通过索引赋值:
mapping = df_unique.set_index(['one_one_3first', 'zero_zero_3first']).index.to_series() for row in df.itertuples(): key = (row.one_one_3first, row.zero_zero_3first) df.at[row.Index, 'unique_no'] = mapping.get(key, pd.NA)
但依然不推荐,性能远不如前两种方案。
输入输出示例
输入df
| index | one_one_3first | zero_zero_3first | unique_no |
|---|---|---|---|
| 0 | 123 | 456 | NaN |
| 1 | 789 | 012 | NaN |
| 2 | 123 | 456 | NaN |
输入df_unique
| index | one_one_3first | zero_zero_3first |
|---|---|---|
| 0 | 123 | 456 |
| 1 | 789 | 012 |
输出df
| index | one_one_3first | zero_zero_3first | unique_no |
|---|---|---|---|
| 0 | 123 | 456 | 0 |
| 1 | 789 | 012 | 1 |
| 2 | 123 | 456 | 0 |
内容的提问来源于stack exchange,提问作者emor
相关产品推荐
相关产品推荐

