多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

综合能源负荷预测实战:电热负荷数据清洗、对齐与滑窗构造避坑指南

综合能源负荷预测实战:电热负荷数据清洗、对齐与滑窗构造避坑指南 简介这份资源面向电力系统、综合能源管理与负荷预测方向的研究人员、研究生及工程技术人员提供一套完整的电负荷与热负荷时间序列数据用于开展负荷预测建模、时间序列分析与气候因素影响研究。压缩包共41个文件约17.05MB以19个Python脚本、10个CSV数据文件、8个TXT文本为主另含2份PDF论文与演示文档、LICENSE及README说明兼顾数据处理与算法复现。数据涵盖电、热两类负荷序列并配套温度、太阳直接辐射、风速等气象变量以及标准负荷曲线便于分析气象条件对用电与取暖需求的影响。随包附带的分布式供暖系统调度策略项目包含完整代码、论文与演示材料可支撑从数据清洗、特征构造到模型训练与调度优化的全流程实践。目前已有1892人学习下载适合希望借助真实负荷数据完成预测实验、复现调度策略或撰写论文的读者参考使用。1. 电热负荷数据拿到手之后从“能跑通”到“敢写进报告”还差几步刚入行做综合能源或者负荷预测的朋友大概率经历过这个场景算法同事甩过来一个压缩包说“电负荷、热负荷数据都在里面了你先把预测模型跑起来看看”。你兴冲冲解压发现里面是几个 CSV时间列格式五花八门电负荷单位是 kW 还是 MW 全靠猜热负荷缺测值用 0 还是空值填也没人告诉你。模型跑出来 MAPE 看着还行但一到写报告、做汇报被问一句“你这数据清洗规则是什么、异常值怎么判的”当场卡壳。这份“完整电负荷、热负荷数据负荷预测”资源解决的就是这个断层。它不是一份只给你原始数字的裸数据而是围绕负荷预测这个任务把电、热两类负荷的时序数据组织成可以直接进特征工程、进模型训练、进误差分析的形态。适合谁做园区综合能源负荷预测的算法工程师、做需求侧响应分析的研究生、以及需要拿真实负荷曲线做仿真验证的电气方向从业者。你拿到手之后真正要花时间的不是“怎么读文件”而是“怎么把这份数据变成你报告里敢写、答辩时敢讲的依据”。下面按我实际拆包的顺序把选型理由、读取步骤、清洗参数和几个血泪坑一次讲透。2. 电热负荷数据的结构拆解时间粒度、量纲与缺失模式2.1 为什么负荷预测数据要先看时间粒度再谈模型很多人拿到数据第一反应是pd.read_csv然后df.head()看一眼数字就扔进 LSTM。但负荷预测里时间粒度直接决定你后面能做什么、不能做什么。常见做法是先确认采样间隔是 15 分钟、30 分钟还是 1 小时。这份资源里的电负荷和热负荷数据典型组织方式是按固定时间步长排列的时序记录电负荷反映的是有功功率随时间的变化热负荷反映的是热功率或供热量随时间的变化。两者时间轴必须对齐否则你做多能互补预测时会出现“电在 10:15 有值、热在 10:00 才有值”的错位。我一般会先写一段探查代码把时间列解析出来看最小间隔和最大间隔是否一致。如果发现间隔不均匀说明中间有缺测或者采集系统抖动这时候不能直接resample成均匀序列得先判断缺测是随机丢失还是整段丢失。随机丢失可以用插值补整段丢失比如某天全天没有热负荷就要考虑是不是停暖季或者设备检修这种段落在训练时要打标签或者剔除否则模型会学到“热负荷可以为 0”的错误模式。import pandas as pd # 读取电负荷与热负荷数据假设文件为 CSV含 timestamp 列 elec pd.read_csv(electric_load.csv, parse_dates[timestamp]) heat pd.read_csv(heat_load.csv, parse_dates[timestamp]) # 统一按时间排序并设为索引 elec elec.sort_values(timestamp).set_index(timestamp) heat heat.sort_values(timestamp).set_index(timestamp) # 检查时间间隔分布判断采样粒度是否均匀 for name, df in [(电负荷, elec), (热负荷, heat)]: diff df.index.to_series().diff().dropna() print(f{name} 最小间隔: {diff.min()}, 最大间隔: {diff.max()}, 众数间隔: {diff.mode().iloc[0]})这段代码的逻辑说明parse_dates确保时间列被解析成 datetime 类型避免字符串排序导致乱序sort_values加set_index是时序数据标准操作后续 resample 和 rolling 都依赖单调递增索引diff()计算相邻时间差mode()取众数间隔如果众数间隔和最大间隔差很多说明存在缺测段。参数上如果你的数据时间列名不是timestamp改成实际列名即可但建议统一命名后面合并电热数据时不容易出错。2.2 电负荷与热负荷的量纲对齐kW、MW 与 kWh 的换算边界量纲问题是负荷预测里最容易被忽视、又最容易翻车的地方。电负荷数据常见单位是 kW 或 MW热负荷常见单位是 kW、MW 或者 GJ/h。如果你直接把电负荷的 kW 和热负荷的 MW 放在同一个图里对比量级差 1000 倍可视化完全没法看。更隐蔽的是有些热负荷数据给的是累计供热量kWh 或 GJ而不是瞬时热功率这时候你需要先做差分再除以时间间隔才能得到和电负荷同量纲的功率序列。我一般会先看数据字典或者列名里的单位标识如果没有就用统计量反推一个中等规模园区的电负荷峰值通常在几千 kW 到几十 MW 之间热负荷峰值在冬季可能接近电负荷的 30% 到 60%。如果热负荷数值比电负荷小两三个数量级大概率是单位不一致。确认单位后统一换算成 kW 或者 MW并在后续所有计算中保持一致。这一步不做后面算 MAPE 的时候误差会被量纲放大模型评估结果完全不可信。# 假设电负荷单位为 MW热负荷单位为 kW统一换算为 kW elec[load_kw] elec[load_mw] * 1000 heat[load_kw] heat[load_kw] # 已经是 kW 则保持不变 # 如果热负荷给的是累计热量 kWh需要差分并除以时间间隔小时 # heat[load_kw] heat[cum_kwh].diff() / (heat.index.to_series().diff().dt.total_seconds() / 3600)逻辑说明第一段是直接乘换算系数第二段注释给出累计量转瞬时功率的公式。参数上diff()后第一个值为 NaN需要dropna()或者用fillna(0)处理但填 0 要谨慎因为第一个时间点的累计量差分没有物理意义。常见做法是丢弃第一个点或者用后一个点的值回填。这里没有绝对标准但必须在报告里写清楚你的处理方式。2.3 缺失模式识别随机缺失、整段缺失与传感器漂移负荷数据的缺失不是均匀分布的。电负荷缺失往往集中在通信中断的几分钟到几小时热负荷缺失则可能出现在季节切换或者供热管网检修期。更麻烦的是传感器漂移数值不是空值但长时间保持一个常数或者缓慢偏移这种“假数据”比空值更难发现。我一般会先统计每个月的缺失率再看连续缺失的最大长度。如果连续缺失超过 4 个采样点比如 1 小时粒度下缺 4 小时插值就不合适了得考虑用相似日填充或者直接标记为无效段。# 统计缺失率和连续缺失长度 def missing_profile(df, col): s df[col] miss_rate s.isna().mean() # 计算连续缺失的最大长度 groups (s.notna() ! s.notna().shift()).cumsum() max_consec s.isna().groupby(groups).sum().max() return miss_rate, max_consec for name, df in [(电负荷, elec), (热负荷, heat)]: rate, consec missing_profile(df, load_kw) print(f{name} 缺失率: {rate:.2%}, 最大连续缺失点数: {consec})逻辑说明isna()生成布尔序列notna().shift()配合cumsum()给每个连续段打组号再对缺失值分组求和取最大得到最大连续缺失长度。参数上如果最大连续缺失点数超过你采样频率对应的 2 到 3 个点建议不要用简单线性插值改用前向填充加相似日修正。这一步的输出直接决定你后面清洗策略的选择不能跳过。3. 从原始 CSV 到可训练特征清洗、对齐与滑窗构造3.1 异常值判定3σ、IQR 与负荷曲线的物理约束清洗负荷数据时异常值判定不能只靠统计方法。3σ 和 IQR 能帮你找出统计上的离群点但负荷曲线有物理约束电负荷不可能为负除非有分布式电源倒送但那是另一回事热负荷在非供暖季可能整体为 0 或接近 0。我一般会先用 IQR 找出统计离群点再用物理规则过滤电负荷小于 0 的置为 NaN热负荷在供暖季外持续为 0 的段标记为无效。注意不要直接把异常值删掉而是先标记后续在特征工程里决定是插值还是剔除。import numpy as np def flag_outliers(df, col, season_maskNone): s df[col].copy() q1, q3 s.quantile(0.25), s.quantile(0.75) iqr q3 - q1 lower, upper q1 - 1.5 * iqr, q3 1.5 * iqr outlier_mask (s lower) | (s upper) # 物理约束电负荷不能为负 if col elec_kw: outlier_mask | (s 0) # 热负荷在非供暖季为 0 的段标记 if col heat_kw and season_mask is not None: outlier_mask | (~season_mask) (s 0.01 * s.max()) df[f{col}_outlier] outlier_mask return df逻辑说明IQR 方法对偏态分布比 3σ 更稳健因为负荷数据通常不是正态分布。season_mask是一个布尔序列标记供暖季如果没有这个信息可以跳过。参数上1.5 倍 IQR 是常用阈值如果数据抖动大可以放宽到 3 倍但要在报告里说明。标记列_outlier保留下来后面做特征时可以作为指示变量输入模型让模型自己学习异常模式。3.2 电热时间轴对齐重采样、插值与合并陷阱电负荷和热负荷的采样时间可能不完全一致比如电是 15 分钟热是 1 小时。这时候需要先统一到较粗的粒度或者用插值升采样到较细粒度。我一般倾向于统一到较粗粒度因为插值会引入虚假的高频信息对预测模型是噪声。合并时用pd.merge的howinner还是outer取决于你的任务如果做电热联合预测用 inner 保证两个序列都有值如果做单变量预测各自处理即可。# 统一重采样到 1 小时粒度取均值 elec_h elec[load_kw].resample(1h).mean() heat_h heat[load_kw].resample(1h).mean() # 合并只保留两个序列都有值的时间点 combined pd.DataFrame({elec_kw: elec_h, heat_kw: heat_h}).dropna()逻辑说明resample(1h).mean()把 15 分钟数据聚合成小时均值如果原始数据有缺失mean()会自动跳过 NaN但全 NaN 的小时结果还是 NaN。dropna()做 inner join 效果保证后续滑窗构造时不会因为某一列缺失而整段丢弃。参数上如果你的任务是短期预测比如提前 15 分钟就不要降采样而是用插值把热负荷升到 15 分钟但要在报告里注明插值方法。3.3 滑窗构造用过去 N 步预测未来 M 步的代码模板负荷预测的监督学习格式核心是把时序转成(样本, 过去 N 步特征, 未来 M 步标签)。我一般会写一个通用函数支持单变量和多变量输入。注意滑窗构造要在数据清洗和对齐之后做否则窗口里混入异常值会污染训练集。def make_windows(data, n_steps, m_steps, target_col): X, y [], [] values data.values target_idx data.columns.get_loc(target_col) for i in range(len(data) - n_steps - m_steps 1): X.append(values[i : i n_steps]) y.append(values[i n_steps : i n_steps m_steps, target_idx]) return np.array(X), np.array(y) # 示例用过去 24 小时预测未来 1 小时的电负荷 X, y make_windows(combined, n_steps24, m_steps1, target_colelec_kw) print(X.shape, y.shape) # (样本数, 24, 2), (样本数, 1)逻辑说明n_steps是输入窗口长度m_steps是预测步长target_col指定预测目标。target_idx用于从多变量窗口里提取目标列作为标签。参数上24 小时输入适合捕捉日周期如果数据有周周期可以加到 168 小时但样本数会减少要权衡。这段代码没有做归一化实际训练前要对 X 和 y 分别做标准化且标准化参数只能从训练集计算避免数据泄漏。4. 避坑与排查电热负荷数据清洗中最容易翻车的五件事4.1 现象重采样后负荷峰值被削平 → 原因用了 mean 而不是 max → 解决按任务选聚合函数很多人习惯性resample(1h).mean()结果发现原本 15 分钟粒度下的尖峰负荷被平均掉了模型学不到峰值特征。如果你的任务是峰值预测或者需量管理应该用max()或者保留原始粒度。我一般会先画一下重采样前后的曲线对比确认峰值损失在可接受范围内。如果必须降采样又不想丢峰值可以用resample(1h).agg([mean, max])生成两列特征。4.2 现象电热合并后样本量骤减 → 原因outer join 引入大量 NaN 后 dropna → 解决先各自插值再合并电负荷和热负荷的缺失时间段往往不重合直接dropna()会把两个序列都有效的时间段之外全部丢掉样本量可能减少一半以上。正确做法是先对每个序列单独做时间插值限制最大插值长度再合并最后只对仍然缺失的点做 drop。插值时用interpolate(methodtime, limit4)limit控制最大连续插值点数超过就保留 NaN。4.3 现象模型在验证集上表现很好上线后误差翻倍 → 原因标准化参数用了全量数据 → 解决严格按时间切分训练/验证/测试这是时序预测的经典翻车点。如果你在构造滑窗之前就对整个数据集做了标准化验证集的信息已经泄漏到训练过程中。正确顺序是先按时间切分再在训练集上 fit 标准化器然后 transform 验证集和测试集。滑窗构造也要在切分之后做否则窗口会跨越切分边界。4.4 现象热负荷在夏季出现非零值 → 原因传感器漂移或数据错位 → 解决结合季节掩码和物理规则过滤热负荷在非供暖季理论上应该为 0 或者接近 0。如果发现夏季有持续非零值先检查是不是时间列错位比如年份解析错误再检查传感器是否故障。我一般会画一张全年热负荷热力图按小时和日期排列一眼就能看出异常段。确认是漂移后把这些段标记为 NaN不要直接删保留缺失标记供模型参考。4.5 现象滑窗样本标签与特征时间重叠 → 原因窗口步长设置错误 → 解决确保标签起始点等于特征结束点用make_windows时如果m_steps的起始索引写错会出现用未来信息预测未来的泄漏。检查方法打印第一个样本的最后一个特征时间戳和第一个标签时间戳确认标签时间戳严格大于特征时间戳。我一般会在函数里加一行断言assert data.index[i n_steps] data.index[i n_steps - 1]虽然看起来多余但能防止索引错位。5. 进阶用法用这份数据做电热耦合特征与预测结果验证5.1 构造电热耦合特征比值、滚动相关与滞后互相关电负荷和热负荷不是独立的尤其在综合能源园区里热泵、电锅炉等耦合设备会让两者产生关联。我一般会构造三类特征电热比值elec_kw / (heat_kw 1e-6)、滚动窗口内的相关系数、以及互相关函数找最大滞后。这些特征能帮模型捕捉耦合关系比单纯把两个序列拼在一起效果好。# 电热比值加小量避免除零 combined[elec_heat_ratio] combined[elec_kw] / (combined[heat_kw] 1e-6) # 24 小时滚动相关系数 combined[rolling_corr_24h] combined[elec_kw].rolling(24).corr(combined[heat_kw]) # 互相关找热负荷滞后电负荷的最佳小时数 def best_lag(x, y, max_lag12): lags range(-max_lag, max_lag 1) corrs [x.corr(y.shift(lag)) for lag in lags] best lags[np.argmax(np.abs(corrs))] return best, max(corrs, keyabs) lag, corr_val best_lag(combined[elec_kw], combined[heat_kw]) print(f最佳滞后: {lag} 小时, 相关系数: {corr_val:.3f})逻辑说明比值特征直接反映耦合强度滚动相关捕捉时变关联互相关找滞后关系。参数上max_lag根据你的采样粒度和物理过程时间常数定小时粒度下 12 小时通常够用。注意滚动相关会引入 NaN需要dropna()后再进模型。5.2 预测结果验证除了 MAPE 还要看峰值误差和方向准确率MAPE 在负荷接近 0 的时候会爆炸热负荷非供暖季就是典型场景。我一般会同时看三个指标MAPE只在负荷大于阈值时计算、峰值误差预测峰值与真实峰值的相对误差、方向准确率预测涨跌方向与实际一致的比例。这三个指标一起看才能判断模型是真的学到了趋势还是只会拟合均值。指标计算方式适用场景注意事项MAPEmean(abs((y_true - y_pred) / y_true))负荷稳定且非零负荷接近 0 时剔除或改用 SMAPE峰值误差abs(max(y_pred) - max(y_true)) / max(y_true)需量管理、峰值预测只看一个点需结合整体误差方向准确率mean(sign(diff(y_pred)) sign(diff(y_true)))趋势判断对噪声敏感需平滑后计算5.3 一个具体技巧用相似日修正节假日预测偏差节假日负荷模式和工作日差异很大如果训练集里节假日样本少模型在节假日预测会明显偏。我一般会从历史数据里找与目标日“日期类型相同、季节相近、天气相似”的相似日用相似日的负荷曲线对模型预测做加权修正。权重可以用电热比值相似度或者滚动相关系数来定。这个技巧不需要重新训练模型在预测后处理阶段就能用对园区负荷预测特别有效。def similar_day_correction(target_date, history, pred, top_k3): # 简化示例按星期几和月份筛选相似日 candidates history[ (history.index.weekday target_date.weekday()) (abs(history.index.month - target_date.month) 1) ] if len(candidates) 0: return pred # 取最近 top_k 个相似日的同一时刻均值作为修正参考 ref candidates.groupby(candidates.index.time).mean().iloc[:, 0] corrected 0.7 * pred 0.3 * ref.values[:len(pred)] return corrected逻辑说明这段代码是简化版实际用的时候要把天气、温度等特征加进去算相似度。top_k控制参考天数太多会平滑掉节假日的特殊性太少又不稳定。权重 0.7/0.3 是我在几个园区项目里试出来的经验值你可以根据验证集调整。从那以后我每次做节假日预测都会强制走一遍相似日修正哪怕模型本身已经加了日期特征后处理这一步也能再压几个点的误差。希望帮到你。本文还有配套的精品资源点击获取
返回列表