Pandas如何按区间匹配从另一DataFrame填充空值更新列值
可行实现方案
普通等值left join无法生效的核心原因是匹配规则包含非等值区间判断,且一个区间可能匹配多条largedf记录,必须搭配聚合逻辑取最小值,不能直接关联后用fillna处理。以下分别提供Pandas和SQL的可直接运行实现:
Pandas 实现
如果数据量在十万级以内,用分组缓存+逐行匹配的逻辑即可,逻辑完全贴合需求,不需要依赖额外第三方库:
import pandas as pd import numpy as np # --- 测试数据构造(可替换为你的实际数据)--- smalldf = pd.DataFrame({ 'RoadNo': ['A001', 'A001', 'A001', 'A002', 'A003'], 'Range_FROM': [0, 1, 3, 0, 2], 'Range_TO': [1, 3, 5, 2, 5], 'values': [1.2, 3, np.nan, 2.1, np.nan] }) largedf = pd.DataFrame({ 'RoadNo': ['A001','A001','A001','A001','A002','A002','A003','A003'], 'B': [0.5, 1.2, 2.2, 3.5, 0.8, 1.5, 2.7, 4.1], 'values': [1.2, 1.3, 1.6, 2.0, 1.9, 2.2, 1.6, 1.8] }) # --- 核心逻辑 --- # 先按RoadNo分组缓存largedf,减少重复筛选开销 large_group_map = {road: group for road, group in largedf.groupby('RoadNo')} def match_min_val(row): # 无对应RoadNo分组直接返回原值 if row['RoadNo'] not in large_group_map: return row['values'] road_df = large_group_map[row['RoadNo']] # 筛选B落在[Range_FROM, Range_TO]区间内的记录 match_mask = road_df['B'].between(row['Range_FROM'], row['Range_TO']) match_vals = road_df.loc[match_mask, 'values'] # 匹配到记录就返回最小值,否则返回原值 return match_vals.min() if not match_vals.empty else row['values'] # 直接覆盖values列:原有值大于匹配最小值就更新,NaN直接填充 smalldf['values'] = smalldf.apply(match_min_val, axis=1)
运行后结果完全符合规则示例:smalldf第二行原3更新为1.3,第三行NaN填充为1.6,其余行均取对应区间最小values。
如果数据量超过百万级,逐行apply性能不足,可以用pandasql库直接执行下方SQL逻辑,性能会有明显提升。
SQL 实现
用非等值关联+分组聚合先算出每个区间对应的最小值,再关联更新原表即可:
WITH interval_min_val AS ( SELECT s.RoadNo, s.Range_FROM, s.Range_TO, MIN(l.values) AS match_min FROM smalldf s LEFT JOIN largedf l ON s.RoadNo = l.RoadNo AND l.B >= s.Range_FROM AND l.B <= s.Range_TO GROUP BY s.RoadNo, s.Range_FROM, s.Range_TO ) UPDATE smalldf s SET s.values = COALESCE(m.match_min, s.values) FROM interval_min_val m WHERE s.RoadNo = m.RoadNo AND s.Range_FROM = m.Range_FROM AND s.Range_TO = m.Range_TO;
内容的提问来源于stack exchange,提问作者HIM
相关产品推荐
相关产品推荐

