简介:这份资源围绕电力系统负荷(有功功率)预测展开,面向电力规划调度、电网运营及机器学习应用方向的学习者与从业者,帮助理解短期、中期、长期负荷预测的建模思路,并应对气象条件、社会事件等不确定因素给传统模型带来的局限。压缩包共9个文件,约455KB,以csv与xls数据表承载历史负荷及未来预测的中间结果,html页面呈现十天15分钟粒度、未来三个月及中期预测的分析视图,另附docx格式的分析报告,形成数据、代码结果与文字说明相互对照的完整链路。目前已有3332人学习下载,具备一定的参考基础。读者可从中获得从数据整理、模型分析到结果呈现的完整实践样例,借助中间数据与报告理解预测流程的关键环节,并对照不同时间尺度的输出结果梳理建模与验证思路,适合作为课程设计、课题研究或相关项目的起步参考。
1. 从一份“跑完却不敢交”的负荷预测作业说起
电力负荷预测这个题目,几乎每年都有人做,但真正能交出去的不多。我见过太多这样的情况:模型训练完了,RMSE 也打印出来了,可导师一问“你的特征怎么来的”“预测曲线为什么在周末翘起来”“中间数据能不能复现”,就答不上来。问题不在算法,而在于整个方案是散的——数据、特征、模型、评估各写各的,中间结果没有落盘,报告里的图和代码跑出来的图对不上。
这套“电力负荷预测分析”资源要解决的正是这件事:它把代码、报告和中间数据打包成一个闭环,用机器学习做预测、用统计分析做解释,Python 一条链路走到底。适合正在做数据挖掘课设、时序预测毕设,或者要给业务方出一份负荷分析方案的从业者。下面按需求、设计、实现、避坑、验收的顺序,把这份方案拆开讲清楚,你可以直接照着改。
2. 需求与数据底座:负荷预测到底要预测什么、拿什么预测
2.1 先把预测目标定义清楚,别一上来就调模型
负荷预测最容易翻车的地方,是目标定义含糊。是预测未来 1 小时、1 天还是 1 周?是预测全网总负荷,还是某条馈线、某个台区?粒度不同,特征工程和模型选型完全是两套东西。这份方案默认做的是短期日负荷预测:以 15 分钟或 1 小时为采样间隔,预测未来 24 小时的负荷曲线。这个粒度是课程设计和多数业务场景的交集,既有足够的时序特征可挖,又不至于像超短期预测那样对数据质量要求苛刻。
目标确定后,要明确输出形态。合格的做法是输出一条长度为 96(15 分钟粒度)或 24(小时粒度)的预测序列,而不是一个单点值。因为负荷预测的价值在于曲线形状——峰谷时刻对不对、峰值高不高,业务方看的是这个。所以评估指标不能只用 MAE,还要看峰值误差和曲线相关性,这一点在第 5 章的验收里会展开。
2.2 数据来源与字段结构:三类数据缺一不可
负荷预测的数据底座通常由三部分组成,这份方案的中间数据也是按这个结构组织的:
| 数据类别 | 典型字段 | 作用 | 缺失后果 |
|---|---|---|---|
| 历史负荷 | 时间戳、有功负荷、无功负荷 | 模型主输入,提供时序规律 | 无法建模 |
| 时间特征 | 小时、星期、是否节假日、月份 | 刻画周期性与作息规律 | 周末/节假日预测失真 |
| 气象数据 | 温度、湿度、体感温度 | 解释空调负荷等气象敏感分量 | 极端天气下误差放大 |
历史负荷是主表,时间特征可以从时间戳直接派生,气象数据是外部关联表,按时间戳左连接进来。这里有个血泪经验:气象数据的采样粒度往往和负荷不一致(气象常是小时级,负荷可能是 15 分钟级),直接 merge 会产生大量空值。通常的做法是先对气象数据做前向填充或线性插值到负荷粒度,再连接,而不是反过来降采样负荷——降采样会丢掉负荷的尖峰信息。
2.3 数据清洗的四个固定动作
原始负荷数据几乎不可能干净,这份方案的预处理环节固定做四件事,顺序不能乱:
- 时间对齐:统一时区、统一采样间隔,缺失时间戳补齐成连续序列。
- 异常值识别:用 3σ 准则或 IQR 方法标记离群点,结合负荷的物理上下限(比如不可能为负、不可能超过变压器容量)双重过滤。
- 缺失值处理:短缺口(连续 1~2 个点)用线性插值,长缺口用同日同时段的历史均值填充,并打上标记位。
- 重采样与聚合:如果原始是 15 分钟级而建模用小时级,用均值聚合,但峰值分析要保留原始粒度。
import pandas as pd import numpy as np def clean_load_series(df, time_col="timestamp", load_col="load"): """负荷序列清洗:对齐 -> 异常标记 -> 插值 -> 重采样""" df = df.copy() df[time_col] = pd.to_datetime(df[time_col]) df = df.sort_values(time_col).set_index(time_col) # 1. 补齐连续时间索引(15分钟粒度) full_idx = pd.date_range(df.index.min(), df.index.max(), freq="15min") df = df.reindex(full_idx) # 2. IQR 识别异常,置为 NaN 待插值 q1, q3 = df[load_col].quantile([0.25, 0.75]) iqr = q3 - q1 lower, upper = q1 - 1.5 * iqr, q3 + 1.5 * iqr df.loc[(df[load_col] < lower) | (df[load_col] > upper), load_col] = np.nan # 3. 短缺口线性插值,长缺口用同日同时段均值 df[load_col] = df[load_col].interpolate(method="linear", limit=2) same_slot_mean = df.groupby([df.index.hour, df.index.minute])[load_col].transform("mean") df[load_col] = df[load_col].fillna(same_slot_mean) # 4. 聚合到小时级,均值代表该小时负荷水平 hourly = df[load_col].resample("1h").mean().to_frame() return hourly这段代码的关键在limit=2这个参数:它限制线性插值最多补两个连续点,超过就交给同日同时段均值。为什么这么设?因为负荷曲线在缺口较长时线性插值会拉出一条不真实的直线,而同日同时段均值保留了日周期形状。resample("1h").mean()之后,如果后续要做峰值分析,记得保留清洗后的 15 分钟版本,别只存小时级。
3. 总体架构设计:从原始数据到预测报告的四层链路
3.1 四层结构:数据层、特征层、模型层、评估层
这份方案的代码组织不是一个大脚本,而是按职责分成四层,每层有明确的输入输出,中间结果全部落盘。这样做的直接好处是:报告里的每一张图都能追溯到某个中间文件,答辩时被问“这个图哪来的”能立刻定位。
- 数据层:原始数据读取、清洗、重采样,输出
clean_load.csv和clean_weather.csv。 - 特征层:时间特征派生、滞后特征构造、气象特征合并,输出
features.parquet。 - 模型层:训练/验证集切分、模型训练、超参搜索,输出模型文件和预测结果
pred.csv。 - 评估层:指标计算、可视化、报告图表生成,输出指标表和图片。
层与层之间只通过文件交互,不共享内存状态。这个约束看起来笨,但它是可复现性的保障——任何一层出问题,重跑那一层就行,不用从头跑全流程。我一般会在项目根目录建data/raw、data/interim、data/processed、outputs/figures、outputs/metrics五个目录,中间数据按阶段存放,报告里的图统一从outputs/figures引用。
3.2 特征工程:时间特征、滞后特征、气象特征三件套
负荷预测的特征工程有相对固定的套路,这份方案的特征层主要构造三类特征:
时间特征是基础,包括小时、星期几、月份、是否周末、是否节假日。注意小时和星期要做周期性编码,不能直接当连续数值喂给模型——23 点和 0 点在数值上差 23,但在负荷规律上只差 1 小时。通常的做法是用正弦余弦编码:
def add_cyclical_features(df, col, period): df[f"{col}_sin"] = np.sin(2 * np.pi * df[col] / period) df[f"{col}_cos"] = np.cos(2 * np.pi * df[col] / period) return df # 小时周期 24,星期周期 7 df = add_cyclical_features(df, "hour", 24) df = add_cyclical_features(df, "weekday", 7)滞后特征是时序预测的核心,用过去时刻的负荷预测当前时刻。常用的是 lag_1(前一小时)、lag_24(前一天同时刻)、lag_168(上周同时刻),以及滑动窗口均值(过去 3 小时、24 小时的均值)。这里有个坑:构造滞后特征时如果直接shift而不考虑时间连续性,遇到缺失时间戳会错位。所以特征层必须在数据层补齐连续索引之后再做 shift。
气象特征主要是温度和湿度,必要时加体感温度和温湿指数。温度对负荷的影响是非线性的——太冷太热都推高负荷,中间段平缓,所以可以加温度的平方项,或者做分箱。这份方案里我一般会加temp、temp_squared、humidity三个字段,够用且不冗余。
3.3 模型选型:为什么用梯度提升树打底、统计模型做对照
模型层这份方案用的是“梯度提升树为主、统计模型为对照”的组合。主模型选 LightGBM 或 XGBoost 这类梯度提升树,原因是它们对表格型特征友好、能自动处理特征交互、训练快,在负荷预测这类结构化数据上通常比深度学习更稳。对照模型选 SARIMA,用来验证树模型是否真的学到了时序规律——如果 SARIMA 的曲线形状明显更合理,说明特征工程有问题。
选型不是拍脑袋,而是有明确的取舍逻辑:树模型擅长利用滞后特征和气象特征做非线性拟合,但对长期趋势的外推能力弱;SARIMA 显式建模周期性和自相关,但对多变量特征利用不足。两者对照,能快速定位问题出在特征还是模型。这份方案的报告里专门有一节做模型对比,用的就是这两个模型的预测曲线叠加图。
提示:如果数据量很小(比如只有几个月),树模型容易过拟合,这时 SARIMA 或简单的季节分解反而更稳。选型要看数据量,不要迷信“树模型一定好”。
4. 关键模块实现:训练、预测与统计分析的落地细节
4.1 训练集/验证集切分:时序数据不能随机切
时序预测最常见的错误是用train_test_split随机切分。随机切分会让模型在训练时“看到未来”,验证指标虚高,上线就崩。正确做法是按时间顺序切:前 80% 做训练,后 20% 做验证,或者用滚动窗口做交叉验证。
def time_series_split(df, train_ratio=0.8): """按时间顺序切分,禁止 shuffle""" n = len(df) split = int(n * train_ratio) train = df.iloc[:split].copy() valid = df.iloc[split:].copy() return train, valid train, valid = time_series_split(features) # 特征列排除目标本身和时间戳 feature_cols = [c for c in train.columns if c not in ("load", "timestamp")] model.fit(train[feature_cols], train["load"]) pred = model.predict(valid[feature_cols])这段代码里iloc的顺序切分是硬约束,shuffle参数根本不该出现。另外注意feature_cols的排除逻辑:目标列load和时间戳列不能进特征,否则就是标签泄漏。滞后特征虽然也是负荷的衍生,但它们是历史值,属于合法特征,这个边界要分清。
4.2 滚动预测与多步输出:一步预测和 24 步预测是两回事
很多方案只做了一步预测(用 t 时刻特征预测 t+1),但业务要的是未来 24 小时。多步预测有两种做法:递归预测和直接多输出。递归预测是用预测值回填成滞后特征再预测下一步,实现简单但误差会累积;直接多输出是训练 24 个模型或一个多输出模型,每步独立预测,误差不累积但训练成本高。
这份方案默认用递归预测,因为实现简单、和单步模型复用度高,但在评估时会单独报告“预测步长 vs 误差”的曲线,让读者看到误差随步长增长的趋势。如果误差在第 6 步之后急剧上升,说明递归策略不适合,要换直接多输出。这个诊断步骤比模型本身更重要,报告里必须体现。
def recursive_forecast(model, history, feature_cols, steps=24): """递归多步预测:每步把预测值回填到滞后特征""" preds = [] window = history.copy() for _ in range(steps): x = window[feature_cols].iloc[[-1]] y_hat = model.predict(x)[0] preds.append(y_hat) # 构造下一行:时间推进1小时,滞后特征用预测值更新 next_row = x.copy() next_row["lag_1"] = y_hat window = pd.concat([window, next_row], ignore_index=True) return preds这里的window维护是关键,每预测一步就把结果拼回去,让下一步的lag_1用上预测值。实际实现中还要同步更新时间特征(小时、星期会随步长变化),否则时间特征会停在起点,预测曲线会失去日周期形状。这个细节是递归预测最容易漏的地方。
4.3 统计分析模块:用分解和相关性解释“为什么”
机器学习给预测值,统计分析给解释,这是这份方案区别于纯调包作业的地方。统计分析模块主要做三件事:
时序分解:用 STL 把负荷拆成趋势项、季节项、残差项,报告里放分解图,直观展示日周期和周周期的强度。如果残差项在某段时间突然变大,往往对应节假日或极端天气,这是模型误差的来源,也是报告里可以展开的分析点。
相关性分析:算负荷与温度、湿度、小时的相关系数,用热力图呈现。注意 Pearson 相关系数只反映线性关系,温度和负荷是非线性的,所以还要配合散点图或分箱均值图看。报告里我一般会放一张“温度-负荷”的分箱均值曲线,比单纯报相关系数有说服力。
峰谷特征统计:统计每日峰谷时刻分布、峰谷差、负荷率,这些指标是业务方最关心的。比如峰谷差逐月扩大,说明用电结构在变化,这个结论比 RMSE 更能打动评审。
from statsmodels.tsa.seasonal import STL def decompose_load(series, period=24): """STL分解,period=24 对应小时级日周期""" stl = STL(series, period=period, robust=True) res = stl.fit() return pd.DataFrame({ "trend": res.trend, "seasonal": res.seasonal, "resid": res.resid })robust=True这个参数值得说一句:它让 STL 对异常值更稳健,负荷数据里偶发的尖峰不会被误判成趋势。period=24是小时级数据的日周期,如果数据是 15 分钟级,这里要改成 96。周期参数设错,分解图会完全失真,这是统计分析模块最常见的参数错误。
5. 避坑与排查:这份方案里最容易翻车的五个点
5.1 指标虚高:先查标签泄漏,再查数据切分
现象:验证集 RMSE 低得离谱,比如只有训练集的一半,或者 R² 接近 1。原因:九成是标签泄漏。常见来源有三个:特征里混入了目标列或其衍生列;随机切分导致未来信息进入训练;滞后特征构造时用了shift(-1)而不是shift(1)。解决:先打印特征列清单,逐个确认没有目标泄漏;把切分改成时间顺序切分;检查所有shift的方向,滞后特征必须是向过去看。改完后指标通常会“变差”,但那是真实水平。
5.2 预测曲线平移:时间索引没对齐
现象:预测曲线形状对,但整体比真实曲线晚一个小时或早一个小时。原因:时间特征和负荷的时间戳错位。常见于气象数据 merge 后索引重置,或者重采样时label、closed参数用了默认值导致区间归属偏移。解决:merge 后立刻set_index并检查首尾时间戳;重采样显式指定label="left", closed="left";画图前把预测值和真实值按时间戳对齐再比,不要按行号比。
5.3 周末和节假日预测崩盘
现象:工作日误差正常,一到周末或节假日误差翻倍。原因:训练数据里周末样本占比低,模型没学到周末模式;或者节假日标记没做,模型把节假日当普通工作日。解决:加is_weekend、is_holiday特征;对节假日单独做误差分析,必要时对节假日样本加权;如果节假日数据太少,考虑用相似日方法做修正,而不是硬让模型学。
5.4 中间数据对不上:报告图和代码输出不一致
现象:报告里的图和重新跑代码得到的图不一样,或者指标表数字对不上。原因:中间数据被手动改过,或者代码有随机性但没固定种子,或者报告引用的是旧版本输出。解决:所有中间数据只由代码生成,不手动编辑;固定随机种子(random_state=42);报告里的图统一从outputs/figures引用,每次重跑覆盖;在报告里标注数据版本和运行时间。
5.5 递归预测误差累积到发散
现象:预测步长超过 6 步后,预测值越来越离谱,甚至出现负负荷。原因:递归预测把预测误差回填成特征,误差逐步放大;模型在训练时没见过“预测值作为输入”的分布。解决:加物理约束,预测值裁剪到合理区间;缩短递归步长,改用直接多输出;或者用滚动预测时每步都用真实历史值修正(如果业务允许)。报告里要如实呈现误差随步长的增长曲线,不要只报第一步的指标。
6. 验收与答辩:怎么证明这套方案是可信的
验收这份方案,我一般不看模型多复杂,而是看三件事能不能自圆其说:数据可追溯、指标可复现、结论有解释。答辩时评审最常问的无非是“数据哪来的”“为什么用这个模型”“误差这么大怎么解释”,提前把这三条准备好,基本就稳了。
具体做法上,我会准备一张验收对照表,把每个交付物和验证方式列清楚:
| 交付物 | 验证方式 | 合格标准 |
|---|---|---|
| 清洗后数据 | 检查时间连续性、缺失率 | 无重复时间戳,缺失率低于 5% |
| 特征表 | 抽查滞后特征与原始值对应关系 | lag_24 等于前一天同时刻值 |
| 模型预测 | 重跑代码对比 pred.csv | 指标差异小于 1% |
| 评估图表 | 图与中间数据交叉核对 | 图上数值能在 csv 里找到 |
| 统计分析 | 分解图周期是否合理 | 日周期峰值出现在负荷高峰时段 |
答辩陈述有个技巧:不要从模型讲起,从一条预测曲线讲起。先展示某一天的预测值和真实值叠加图,指出哪里准、哪里偏,然后倒推原因——偏的地方是节假日还是极端温度,对应到特征工程和模型的哪个环节。这样讲,评审会跟着你的思路走,而不是一上来就质疑算法。我吃过亏,早年答辩一上来讲 LightGBM 参数,评审直接问“你这特征里有没有未来信息”,当场卡住。从那以后我每次做时序项目,都强制先跑一遍标签泄漏检查再动模型,这个习惯救过我好几次。
还有一点,报告里的误差分析要主动写,不要等评审问。把误差最大的几天挑出来,逐日分析原因,哪怕结论是“这天是突发降温,气象数据滞后导致预测偏低”,也比藏着强。主动暴露问题并给出解释,比假装完美可信得多。希望这套结构能帮到你,照着改一版,交出去的东西至少是站得住的。
本文还有配套的精品资源,点击获取