如何基于参考站点趋势填充Python DataFrame中的科考站点缺失值
基于参考站点趋势填充科考站点缺失值的解决方案
针对你提到的「参考站与科考站数值对应关系不固定」「科考站值相同时参考站仍有波动」两个核心问题,以下是几种比pandas.interpolate更适配的方案:
1. 区间比例映射法(优先推荐)
核心思路
以科考站的已知测量点为分界,将全年划分为多个独立区间,每个区间内基于参考站的变化比例,映射出科考站的缺失值。这种方法直接解决你提到的两个痛点:
- 每个区间的映射比例独立计算,适配「同一参考值对应不同科考值」的全年变化
- 若区间内科考站起始/结束值相同,无论参考站怎么波动,填充值都会保持一致
代码实现(Pandas)
import pandas as pd # 假设数据集包含日期(date)、参考站数值(ref_val)、科考站数值(site_val,含缺失) df = df.sort_values('date').reset_index(drop=True) # 提取科考站已知数据的索引 known_indices = df[df['site_val'].notna()].index # 遍历每个已知区间,填充缺失值 for i in range(len(known_indices) - 1): start_idx, end_idx = known_indices[i], known_indices[i+1] # 获取区间两端的参考站、科考站数值 ref_start, ref_end = df.loc[start_idx, 'ref_val'], df.loc[end_idx, 'ref_val'] site_start, site_end = df.loc[start_idx, 'site_val'], df.loc[end_idx, 'site_val'] # 定位区间内的缺失值 mask = (df.index > start_idx) & (df.index < end_idx) if ref_start != ref_end: # 按参考站的变化比例映射科考站数值 df.loc[mask, 'site_val'] = site_start + (df.loc[mask, 'ref_val'] - ref_start) * (site_end - site_start) / (ref_end - ref_start) else: # 参考站区间内无变化,直接填充已知起始值 df.loc[mask, 'site_val'] = site_start
2. 局部拟合函数法
核心思路
针对科考站的已知数据,结合对应参考站的数值,拟合一个非线性模型(如局部加权回归、样条曲线),用模型预测缺失值。适合全年参考站与科考站对应关系非线性特征明显的场景。
代码实现(Statsmodels LOWESS)
from statsmodels.nonparametric.smoothers_lowess import lowess import pandas as pd # 提取科考站的已知配对数据 known_data = df[df['site_val'].notna()][['ref_val', 'site_val']] x = known_data['ref_val'].values y = known_data['site_val'].values # 拟合局部加权回归模型,frac参数控制平滑程度(可根据数据调整) lowess_result = lowess(y, x, frac=0.3) # 将拟合结果转为字典,方便快速查询 fit_map = dict(zip(lowess_result[:, 0], lowess_result[:, 1])) # 填充缺失值:用当日参考站数值匹配拟合结果 df['site_val'] = df.apply( lambda row: fit_map.get(row['ref_val'], row['site_val']) if pd.isna(row['site_val']) else row['site_val'], axis=1 )
3. 逐点邻域加权法
核心思路
对每个缺失值,结合时间邻近性和参考值相似性,计算周边已知科考站数据的加权平均值作为填充值。适合数据点分布稀疏、局部波动较大的场景。
代码实现
import pandas as pd import numpy as np def weighted_fill(row, known_data): if not pd.isna(row['site_val']): return row['site_val'] # 计算每个已知点与当前日期的时间差(天) known_data['time_diff'] = abs((row['date'] - known_data['date']).dt.days) # 计算每个已知点与当前参考值的差异 known_data['ref_diff'] = abs(row['ref_val'] - known_data['ref_val']) # 生成权重:时间越近、参考值越相似,权重越高 known_data['weight'] = 1 / (known_data['time_diff'] + 1) * 1 / (known_data['ref_diff'] + 1) # 加权平均得到填充值 return np.average(known_data['site_val'], weights=known_data['weight']) # 提取科考站已知数据 df_known = df[df['site_val'].notna()].copy() # 批量填充缺失值 df['site_val'] = df.apply(lambda row: weighted_fill(row, df_known), axis=1)
方案选择建议
- 若数据区间划分清晰、局部对应关系稳定:优先用区间比例映射法,逻辑简单且计算高效
- 若全年参考站与科考站的对应关系非线性特征明显:选择局部拟合函数法
- 若数据点稀疏、局部波动大:尝试逐点邻域加权法
内容的提问来源于stack exchange,提问作者Angus Parvis
相关产品推荐
相关产品推荐

