☰
电力负荷预测毕设方案:统计+机器学习+Python全链路复现
2026/10/24 5:43:53 网站建设 项目流程

简介:这份资源围绕电力系统负荷(有功功率)预测展开,面向电力规划调度、电网运营及机器学习应用方向的学习者与从业者,帮助理解短期、中期、长期负荷预测的建模思路,并应对气象条件、社会事件等不确定因素给传统模型带来的局限。压缩包共9个文件,约455KB,以csv与xls数据表承载历史负荷及未来预测的中间结果,html页面呈现十天15分钟粒度、未来三个月及中期预测的分析视图,另附docx格式的分析报告,形成从数据到结论的完整链路。目前已有3332人学习下载,具备一定的参考基础。读者可据此复现负荷预测流程,对照中间数据理解特征构造与结果输出,借助报告梳理建模逻辑与结论表达,适合作为课程设计、课题研究或电力数据分析入门的实践素材。

1. 电力负荷预测分析:从一份能跑通的代码到一份能交的报告

做电力负荷预测这个题目,最怕的不是模型跑不出结果,而是交材料的时候发现三样东西对不上:代码里用的特征和报告里写的不是一套,报告里的图和中间数据版本不一致,答辩时被问一句“你这个 MAPE 是怎么算的”就卡住。这套方案要解决的就是这个断层——把数据清洗、特征构造、模型训练、误差评估、报告成文串成一条可复现的链路,代码、报告、中间数据三者互相印证。它适合正在做机器学习方向毕设或课程设计的同学,也适合需要快速搭一版负荷预测原型的从业者。关键词里的机器学习、统计分析、预测、Python,在这套方案里不是并列的四个标签,而是四个先后咬合的环节:统计先把负荷的日周期和周周期摸清,机器学习再在这个基础上做非线性拟合,Python 负责把整条链路固化成脚本。

2. 需求与数据底座:负荷预测到底要预测什么、拿什么预测

2.1 先把预测目标定义清楚,别一上来就调模型

负荷预测按时间尺度分好几类,超短期是未来几分钟到几小时,短期是未来一天到一周,中期是未来数月,长期是年度规划。毕设和课设里最常见、也最容易做出对比实验的是短期日负荷预测:给定历史若干天的 96 点或 24 点负荷序列,预测次日同一粒度的负荷曲线。这个定义决定了后面所有设计——输入窗口多长、特征怎么构造、误差按点算还是按日算。

我一般会先跟需求方(哪怕是导师或自己)确认三件事:预测粒度是 15 分钟还是 1 小时,预测跨度是提前一天还是提前一周,评估口径是全天 96 点整体误差还是峰值时段单独看。这三件事不确认,后面模型选型就是玄学。粒度 15 分钟意味着一天 96 个点,序列更长、噪声更多,LSTM 这类序列模型更有发挥空间;粒度 1 小时则样本量小,树模型加统计特征往往更稳。

2.2 数据字段与中间数据的组织方式

原始负荷数据通常是一张时间戳加负荷值的宽表,可能还带温度、湿度、节假日标记。这套方案里,中间数据不是随手存的 csv,而是按阶段分层落盘,保证任何一步都能回退重跑。典型的目录组织如下:

project/ ├── data/ │ ├── raw/ # 原始导出,只读不改 │ ├── interim/ # 清洗后、缺失值处理完的中间层 │ └── processed/ # 特征工程后、可直接喂模型的数据集 ├── src/ │ ├── clean.py # 清洗与重采样 │ ├── features.py # 特征构造 │ ├── train.py # 训练与评估 │ └── report_fig.py # 报告用图表生成 └── reports/ └── figures/ # 报告引用的图,全部由脚本生成

这样分层的好处是:报告里每一张图都能追溯到某个脚本的某次运行,中间数据出问题只重跑对应层,不用从头再来。raw 层只读是血泪经验——一旦在原始文件上直接改,后面所有版本都对不上账。

2.3 统计分析先行的必要性

很多同学跳过统计分析直接上 LSTM,结果模型学到的其实是“昨天同一时刻的值”,报告里却写成“捕捉了复杂非线性关系”,答辩一问就露馅。统计分析这一步至少要做三件事:画负荷的日周期曲线看双峰特征(早高峰、晚高峰),算工作日与周末的均值差异,做自相关分析确认滞后阶数。这三件事的结论直接决定特征工程的输入窗口长度和是否引入星期几特征。通常的做法是先用 pandas 做分组聚合和自相关,把结论写进报告的数据分析章节,再进入建模。

3. 总体方案设计:统计特征打底、机器学习建模、误差闭环

3.1 三层结构:数据层、特征层、模型层

整套方案按三层划分。数据层负责清洗、重采样、缺失值填补和异常值处理,输出干净的等间隔序列。特征层在统计分析的结论上构造三类特征:时间特征(小时、星期几、是否节假日)、滞后特征(前 1 天、前 7 天同一时刻的负荷)、滑窗统计特征(过去 24 点均值、最大值、标准差)。模型层负责训练、验证和误差评估,输出预测曲线和误差指标。

三层之间用中间数据文件解耦,而不是在内存里一路传下去。这样做的好处是特征层可以独立迭代——换一组滞后阶数不用重跑清洗,模型层换算法不用重跑特征。对于要交报告的项目,这种解耦还带来一个隐性收益:报告里的“数据预处理”和“特征工程”两章可以分别对应两个脚本,写起来有据可依。

3.2 模型选型:为什么是“统计基线 + 树模型 + 序列模型”三档对比

单一模型交不了差,因为报告需要对比实验。我一般会设计三档:第一档是统计基线,用历史同期均值或简单移动平均,作用是给后面所有模型一个下限;第二档是梯度提升树(如 LightGBM 或 XGBoost),吃的是手工构造的特征,训练快、可解释性强,特征重要性图直接能进报告;第三档是序列模型(如 LSTM 或 GRU),吃原始序列窗口,用来体现“深度学习方法”的对比价值。

三档对比的意义不在于证明谁一定最好,而在于报告里能说清楚“在什么数据条件下哪类方法更合适”。实际项目里,树模型在样本量中等、特征构造到位时经常不输序列模型,这个结论写进报告比硬吹深度学习更有说服力。参数上,树模型重点调树的深度、学习率和叶子数,序列模型重点调窗口长度、隐藏单元数和训练轮次,这些参数表要进报告附录。

3.3 评估口径与误差指标的统一

误差指标不统一是翻车的重灾区。同一份预测结果,用 MAE、RMSE、MAPE 算出来的排序可能不一样。这套方案固定一套口径:全天逐点计算 MAE 和 RMSE,MAPE 只在负荷值不为零的点上算并注明处理方式,另外单独统计峰值时段的误差。所有模型用同一套测试集、同一套指标函数,指标函数写成独立模块被各模型调用,避免各写各的。

import numpy as np def evaluate(y_true, y_pred, eps=1e-6): """统一误差口径:MAE / RMSE / MAPE,MAPE 对近零点做保护""" y_true = np.asarray(y_true, dtype=float) y_pred = np.asarray(y_pred, dtype=float) mae = np.mean(np.abs(y_true - y_pred)) rmse = np.sqrt(np.mean((y_true - y_pred) ** 2)) mask = np.abs(y_true) > eps # 过滤近零点,避免 MAPE 爆炸 mape = np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100 return {"MAE": mae, "RMSE": rmse, "MAPE": mape, "valid_ratio": mask.mean()}

这段函数的关键在mask:负荷序列在深夜低谷时可能接近零,直接算 MAPE 会得到几百甚至上千的百分比,报告里没法看。用valid_ratio记录参与 MAPE 计算的点占比,答辩时被问到就能解释清楚。eps是保护阈值,按数据量纲调整,负荷以 MW 为单位时取 1e-6 足够。

4. 关键模块实现:清洗、特征、训练三段脚本怎么落地

4.1 数据清洗与重采样:缺失值、异常值、时间对齐

原始数据最常见的三个问题是时间戳不连续、缺失值、异常尖峰。清洗脚本按固定顺序处理:先统一时间戳格式并按粒度重采样,再处理缺失值,最后处理异常值。缺失值填补策略按缺口长度分档——短缺口线性插值,长缺口用同日同时刻的历史均值,整段缺失则标记并剔除。异常值用滑动窗口的 3 倍标准差或 IQR 判定,判定后不直接删,而是替换为插值结果并打标记,方便报告里统计异常点数量。

import pandas as pd def clean_load(df, freq="15min"): df = df.copy() df["ts"] = pd.to_datetime(df["ts"]) df = df.set_index("ts").sort_index() # 重采样到固定粒度,缺口先置空 s = df["load"].resample(freq).mean() # 短缺口线性插值,长缺口用同日同时刻均值 s = s.interpolate(method="linear", limit=4) same_slot = s.groupby([s.index.hour, s.index.minute]).transform("mean") s = s.fillna(same_slot) # IQR 异常判定,替换为插值并保留标记 q1, q3 = s.quantile(0.25), s.quantile(0.75) iqr = q3 - q1 outlier = (s < q1 - 1.5 * iqr) | (s > q3 + 1.5 * iqr) s_clean = s.mask(outlier).interpolate(method="linear") return pd.DataFrame({"load": s_clean, "is_outlier": outlier})

limit=4对应 15 分钟粒度下一小时的缺口,超过就交给同日同时刻均值,这个阈值要按粒度换算。is_outlier列保留下来,报告里可以画一张异常点分布图,说明数据质量。注意groupby用的是小时和分钟两级,粒度变了要同步改。

4.2 特征构造:时间特征、滞后特征、滑窗统计

特征构造是这套方案里最影响效果的一步。时间特征把小时、星期几做周期编码(sin/cos),避免模型把 23 点和 0 点当成相距很远;滞后特征取前 1 天、前 7 天同一时刻,捕捉日周期和周周期;滑窗统计取过去 24 点的均值和标准差,刻画近期波动水平。构造时严格按时间顺序 shift,绝不能用未来信息。

import numpy as np def build_features(s, horizon=96): df = pd.DataFrame({"load": s}) idx = df.index # 周期编码,horizon 为一天的点数 df["hour_sin"] = np.sin(2 * np.pi * idx.hour / 24) df["hour_cos"] = np.cos(2 * np.pi * idx.hour / 24) df["dow"] = idx.dayofweek # 滞后特征:前 1 天、前 7 天同一时刻 df["lag_1d"] = df["load"].shift(horizon) df["lag_7d"] = df["load"].shift(horizon * 7) # 滑窗统计:过去一天 df["roll_mean_1d"] = df["load"].shift(1).rolling(horizon).mean() df["roll_std_1d"] = df["load"].shift(1).rolling(horizon).std() return df.dropna()

shift(horizon)是防数据泄漏的关键,horizon必须等于一天的点数(15 分钟粒度是 96,1 小时粒度是 24)。rolling前先shift(1)是为了把当前点排除在统计窗口外。dropna()会砍掉开头一段样本,报告里要说明有效样本量,别让读者以为样本凭空少了。

4.3 训练与验证:时间序列切分与多模型对比

时间序列不能随机切分,必须按时间先后切训练集、验证集、测试集,否则就是拿未来预测过去。这套方案按 7:1.5:1.5 的比例顺序切分,验证集用于调参和早停,测试集只在最后评估一次。多模型对比时,每个模型输出同一格式的预测结果,统一调用第 3 章的evaluate函数,结果汇总成一张对比表进报告。

from sklearn.model_selection import TimeSeriesSplit def time_split(df, val_ratio=0.15, test_ratio=0.15): n = len(df) n_test = int(n * test_ratio) n_val = int(n * val_ratio) train = df.iloc[: n - n_val - n_test] val = df.iloc[n - n_val - n_test : n - n_test] test = df.iloc[n - n_test :] return train, val, test

TimeSeriesSplit也可以用于交叉验证调参,但报告里最终评估必须用上面这种一次性顺序切分,保证测试集只被看一次。切分比例不是死的,样本量小时可以调成 8:1:1,但要在报告里写明并说明理由。

5. 避坑与常见问题:这几处最容易让结果对不上账

5.1 数据泄漏:滞后特征用了未来值

现象是离线评估误差低得离谱,上线或换一段数据就崩。原因通常是构造滞后特征时忘了 shift,或者滑窗统计把当前点算进去了。解决办法是把特征构造写成纯函数,输入只有历史序列,输出特征矩阵,并在单元测试里断言“第 t 行的所有特征只依赖 t 之前的数据”。我一般会抽几行手工核对 lag 值是否等于对应历史时刻。

5.2 时间戳时区与夏令时导致的错位

现象是重采样后点数对不上,或者某天多出或少了一小时。原因是原始时间戳带时区信息,或者跨夏令时切换。解决办法是清洗第一步就统一转成无时区的本地时间并显式声明,重采样后检查每天点数是否等于预期(15 分钟粒度应为 96)。发现点数异常先查原始数据的时间范围,别急着改代码。

5.3 MAPE 在低谷时段爆炸

现象是 MAPE 动辄几百,但 MAE 看起来正常。原因是深夜负荷接近零,分母极小。解决办法是按第 3 章的evaluate做近零点过滤并报告有效占比,或者改用 sMAPE、WAPE 等对低值更稳健的指标。报告里必须注明 MAPE 的计算口径,否则答辩时说不清。

5.4 报告图表与代码版本不一致

现象是报告里的图和最新代码跑出来的对不上,导师让复现时手忙脚乱。原因是图是手工截的或旧脚本生成的。解决办法是所有报告图由report_fig.py统一生成并覆盖写入reports/figures/,图文件名带指标名,报告引用相对路径。每次改完代码重跑一遍出图脚本,形成习惯。

5.5 训练集与测试集分布差异过大

现象是验证集表现好、测试集明显变差。原因是测试集恰好落在节假日或极端天气段,而训练集没有类似样本。解决办法是在切分前先看负荷的月度分布,必要时在报告里单独讨论节假日样本的处理策略,或者把节假日标记作为特征引入,让模型至少知道“今天特殊”。

6. 验收与答辩:用一张复现清单把材料锁死

验收这套方案,我一般不看模型指标有多漂亮,先看能不能在干净环境里从 raw 数据一路跑到报告图。具体做法是写一份复现清单,按顺序列出每一步的命令、输入文件、输出文件和预期行数,答辩前自己走一遍,任何一步对不上就回去修。清单大致长这样:

步骤命令输入输出校验点
清洗python src/clean.pydata/raw/*.csvdata/interim/clean.csv每日点数=96
特征python src/features.pydata/interim/clean.csvdata/processed/feat.csv无 NaN、无未来泄漏
训练python src/train.pydata/processed/feat.csv指标 json + 预测 csv三模型指标齐全
出图python src/report_fig.py指标 json + 预测 csvreports/figures/*.png图与指标一致

答辩时被问得最多的三类问题,提前准备好答案:一是“为什么选这个模型”,用第 3 章的三档对比表回答,说清楚数据条件下各类方法的取舍;二是“误差怎么算的”,直接翻到evaluate函数讲口径和近零点处理;三是“结果能复现吗”,现场打开复现清单,指出每一步的输入输出。指标不用追求极致,但口径必须自洽、图表必须可追溯。

有个习惯我每次交材料前都强制走一遍:把项目复制到一个新目录,删掉所有中间数据和图,只留 raw 数据和脚本,从头跑一遍。跑不通就说明有隐藏依赖,跑通了才敢交。这个动作救过我两次——一次是某个中间文件是手工改的没进脚本,一次是图是旧版本。从那以后我每次交方案前都强制走一遍这条冷启动复现,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询