两个DataFrame按SAMPFROM匹配替换C-ORG列-99值的实现方法
问题说明
现有两个DataFrame,需按规则匹配合并C-ORG列:当两个表的SAMPFROM列取值相等时,用data2中对应的C-ORG值,替换data1中对应位置的-99值。
两个测试数据集结构如下:
data1
| SAMPFROM | SAMPTO | C-ORG |
|---|---|---|
| 0 | 5 | -99 |
| 5 | 10 | -99 |
| 10 | 15 | -99 |
| 15 | 20 | -99 |
| 20 | 25 | -99 |
| 25 | 30 | -99 |
| 30 | 35 | -99 |
| 35 | 40 | -99 |
| 40 | 45 | -99 |
data2
| SAMPFROM | SAMPTO | C-ORG |
|---|---|---|
| 0 | 20 | 2.5 |
| 20 | 40 | 7 |
| 40 | 60 | 7 |
| 60 | 80 | 9 |
原有测试代码未达到预期,代码如下:
LIST1=list(data1['SAMPFROM']) LIST2=list(data2['SAMPFROM']) values=[] for i in LIST1: for j in LIST2: if i==j: values.append(i) for k in values: for q in data2['SAMPFROM']: if k==q: data1['new']=data2['CO3_pct_BESTEL']
原因分析
原代码存在三个核心问题:
- 列名引用错误:data2中不存在
CO3_pct_BESTEL列,目标替换列名为C-ORG - 赋值逻辑错误:循环中每次匹配到值都直接给整列赋值,会被后续循环结果覆盖,且两个DataFrame长度不一致,直接整列赋值会出现索引错位
- 实现方式低效:多层嵌套循环在数据量稍大时性能极差,pandas原生提供了专门的映射、合并方法,不需要手写循环实现
解决方案
方案1:严格按「SAMPFROM值相等」规则替换
如果需求确实是仅当两个表SAMPFROM值完全相等时才替换,用映射字典实现即可,代码简洁且运行效率高:
# 构建SAMPFROM到C-ORG的映射关系 corg_map = data2.set_index('SAMPFROM')['C-ORG'].to_dict() # 逐行判断替换:原C-ORG为-99且SAMPFROM存在匹配值时,用映射值替换 data1['C-ORG'] = data1.apply( lambda x: corg_map[x['SAMPFROM']] if x['C-ORG'] == -99 and x['SAMPFROM'] in corg_map else x['C-ORG'], axis=1 )
运行后data1中SAMPFROM为0、20、40的三行C-ORG会被替换为2.5、7、7,其余行因无匹配SAMPFROM保留-99。
方案2:按区间匹配替换(更符合样本分段数据的常规逻辑)
从两个表的SAMPTO字段可以看出,data2是分段区间数据,如果实际需求是data1的SAMPFROM落在data2对应[SAMPFROM, SAMPTO)区间时,就用该区间的C-ORG替换-99,可以用pandas的merge_asof实现近似区间匹配,不需要手写循环判断区间:
import pandas as pd # merge_asof要求关联键必须先排序 data1_sorted = data1.sort_values('SAMPFROM').reset_index(drop=True) data2_sorted = data2.sort_values('SAMPFROM').reset_index(drop=True) # 左连接匹配最近的、不大于当前SAMPFROM的data2记录 merge_res = pd.merge_asof( data1_sorted, data2_sorted[['SAMPFROM', 'C-ORG']], on='SAMPFROM', direction='backward', suffixes=('', '_match') ) # 替换-99值,删除临时列 merge_res['C-ORG'] = merge_res.apply( lambda x: x['C-ORG_match'] if x['C-ORG'] == -99 else x['C-ORG'], axis=1 ) data1_result = merge_res.drop(columns=['C-ORG_match'])
运行后所有行的C-ORG都会被正确填充:
- SAMPFROM属于[0,20)区间(0、5、10、15)的行,C-ORG为2.5
- SAMPFROM属于[20,40)区间(20、25、30、35)的行,C-ORG为7
- SAMPFROM属于[40,60)区间(40、45)的行,C-ORG为7
内容的提问来源于stack exchange,提问作者Taiwo
相关产品推荐
相关产品推荐

