如何基于当日前后2值及前2天同期值填充电力时序数据缺失值?
解决Pandas按电表ID和时段填充缺失值的方案
步骤1:数据预处理
先将date列转为datetime类型,再把宽表转换为长表,让每个时段的数据成为独立行,方便后续按电表和时段分组处理。
import pandas as pd import numpy as np # 假设原始数据存储在df中 df['date'] = pd.to_datetime(df['date']) # 宽表转长表:保留电表ID和日期,将时段列转为「时段标识」和「读数」两列 df_long = df.melt(id_vars=['meter IDs', 'date'], var_name='hour_slot', value_name='reading') # 生成完整时间戳,便于后续定位前后时段与历史同期值 df_long['timestamp'] = df_long['date'] + pd.to_timedelta(df_long['hour_slot'])
步骤2:分组计算填充值
按电表ID和时段分组,对每个缺失值,收集以下有效值取均值填充:
- 当日该时段前后各2个相邻时段的有效值(比如01:00缺失时,取00:00、00:30、01:30、02:00的有效值)
- 前1天、前2天同一时段的有效值(比如2020-09-02的01:00缺失时,取2020-09-01、2020-08-31的01:00有效值)
def fill_missing(group): # 按时间戳排序,保证时序连续性 group = group.sort_values('timestamp').reset_index(drop=True) # 遍历所有缺失值的位置 for idx, row in group[group['reading'].isna()].iterrows(): # 收集当日前后2个时段的有效值 current_day_window = group[ (group['date'] == row['date']) & (group['timestamp'].between(row['timestamp'] - pd.Timedelta(hours=1), row['timestamp'] + pd.Timedelta(hours=1))) & (group['timestamp'] != row['timestamp']) ]['reading'].dropna() # 收集前1、2天的同期有效值 prev_days_vals = [] for days_back in [1, 2]: target_date = row['date'] - pd.Timedelta(days=days_back) val = group[ (group['date'] == target_date) & (group['hour_slot'] == row['hour_slot']) ]['reading'].dropna() if not val.empty: prev_days_vals.extend(val.tolist()) # 合并有效值并计算均值填充 all_valid_vals = np.concatenate([current_day_window.values, prev_days_vals]) if len(all_valid_vals) > 0: group.loc[idx, 'reading'] = np.mean(all_valid_vals) return group # 分组处理缺失值 df_filled_long = df_long.groupby(['meter IDs', 'hour_slot']).apply(fill_missing).reset_index(drop=True)
步骤3:恢复宽表格式
将处理后的长表转回原始的宽表结构:
df_filled = df_filled_long.pivot(index=['meter IDs', 'date'], columns='hour_slot', values='reading').reset_index() # 可选:恢复原始时段列的顺序,和输入表保持一致 original_time_cols = [col for col in df.columns if col not in ['meter IDs', 'date']] df_filled = df_filled[['meter IDs', 'date'] + original_time_cols]
补充说明
- 若某缺失值没有任何有效参考值(如新电表前2天无数据且当日前后时段全缺失),会保留为NaN,可根据需求额外处理这类极端情况。
- 若需要调整前后时段的数量,可修改
pd.Timedelta(hours=1)(当前对应2个30分钟时段)。
内容的提问来源于stack exchange,提问作者Rajesh Ahir
相关产品推荐
相关产品推荐

