很多人学电力交易,最开始会陷入一个困境:
概念都听过,但不知道怎么用。
比如:
负荷是什么?
电量是什么?
电价怎么看?
最高最低价差怎么计算?
负荷率有什么意义?
电价波动程度到底在反映什么?
如果只是背定义,很快就忘。但如果你能用 Python 把它们算出来,这些概念就会变得具体。
比如:
负荷不是抽象词,而是一列 load_mw 数据。
电量不是口号,而是根据 负荷 × 时间 计算出来的结果。
平均电价不是感觉,而是某一段时间内价格数据的统计结果。
最高最低价差不是一句“价格差很大”,而是可以用最高价减最低价得到的指标。
电价波动程度不是“价格忽高忽低”的口头描述,而是可以用标准差、变异系数等方法量化。
这就是本章的核心思路:
不只是看懂电力交易概念,而是学会把概念变成 Python 指标。
前面几章,我们已经完成了四件事:
第1章:知道为什么要学 Python 电力交易。
第2章:理解电力交易的基本规则和参与者。
第3章:学会用 Python 读取电价数据并画趋势图。
第4章:学会对电力交易数据做基础清洗。
从第5章开始,我们要把“概念”变成“指标”。
如果你是第一次系统学习电力交易数据分析,这一章建议先收藏。
本章不是让你背概念,而是用 Python 把几个常见概念真正算出来。
文末我准备了第5章配套资料包,包括完整代码、模拟月度数据、处理后数据、日度统计、月度核心指标、图表示例、自动分析报告和核心指标速查表。
很多电力交易新人会背这些词:
“负荷、电量、平均负荷、最大负荷、负荷率、最高最低价差、电价波动程度、电量加权平均电价。
但真正到业务里,问题会变成:
这个用户一天用了多少电?
这个地区月度用电水平是否平稳?
电价波动大不大?
负荷高的时候,电价是不是也容易高?
这个月的最大负荷和平均负荷差距大吗?
这个月价格最高点和最低点差距有多大?
这些问题不能只靠概念解释。
它们要靠数据算出来。
所以本章目标是:
用 Python 把电力交易里的几个核心概念,变成可以计算、可以对比、可以写进报告的指标。
一、负荷和电量到底有什么区别?
这是电力数据分析里最容易混淆的一组概念。
负荷,更像“某一刻用电有多猛”。
电量,更像“一段时间一共用了多少电”。
举个生活化例子。
你打开一台 2kW 的电暖器。这台电暖器运行时的功率大约是 2kW。如果它连续运行 1 小时,用电量就是:
2kW × 1小时 = 2kWh
其中:
kW 是功率单位,常用于描述负荷。
kWh 是电量单位,日常也叫“度”。
放到企业用电场景里也是一样。
某工厂 10:00 的负荷是 800kW,表示它在这个时刻用电功率较高。如果这个负荷代表 10:00—11:00 这一小时的平均负荷,那么这一小时近似用电量就是:
800kW × 1小时 = 800kWh
所以你可以先记住:
负荷看“瞬间或时段功率水平”,电量看“一段时间累计用了多少”。
这里要特别注意:
如果你拿到的是“某个整点的瞬时负荷”,它并不一定等于这一小时的平均负荷。
只有当 load_mw 表示该小时平均负荷时,才可以近似使用:
小时电量 = 小时平均负荷 × 1小时
这个前提很重要。
二、负荷与电量:怎么用 Python 计算日、月、年用电量?
在电力数据里,负荷通常是按时间点或时间段记录的。
例如:
每小时一个点。
每 15 分钟一个点。
每 5 分钟一个点。
如果是小时级平均负荷数据,可以近似理解为:
每小时电量 = 该小时平均负荷 × 1小时
如果负荷单位是 MW,那么电量单位就是 MWh。
比如:
500MW × 1小时 = 500MWh
如果是 15 分钟负荷数据,则一条记录对应 0.25 小时:
15分钟电量 = 负荷 × 0.25小时
这一步非常关键。
因为很多电费计算、交易电量分析、负荷分析、储能测算,最终都离不开“电量”。
在本章实操中,我们会使用小时级模拟数据,所以计算方式是:
小时电量 = 小时平均负荷 × 1
然后再汇总:
日用电量。
月用电量。
月度平均负荷。
月度最大负荷。
月度负荷率。
本章模拟的是 2026 年 1 月完整小时级数据,共 31 天、744 条记录。
真实业务中,计算月度指标前必须先检查记录数是否完整。
如果一个完整自然月理论上应有 744 条小时级记录,但实际只有 700 条,那么总电量、平均负荷、平均电价、电价波动程度等指标都需要谨慎解释。
三、电价解读:标杆电价、现货电价、峰谷分时电价
电力交易新人看到“电价”两个字,很容易直接开始算。
但在电力行业里,“电价”不是一个单一概念。
不同电价背后的适用对象、形成机制和使用场景都不一样。
1. 标杆电价:历史概念,不宜和当前市场价格混用
过去,燃煤发电曾长期使用“标杆上网电价”机制。
国家发展改革委在 2021 年关于燃煤发电上网电价市场化改革的公开说明中提到,2019 年已将实施多年的燃煤发电标杆上网电价机制改为“基准价+上下浮动”的市场化电价机制。
也就是说,
“标杆电价”在当前市场化电价体系中,更多是历史概念或政策比较口径,不能直接等同于现货电价、中长期交易价格、售电公司零售电价或用户侧分时电价。
2. 现货电价:更接近短期供需变化
根据《电力市场运行基本规则》,电能量交易按交易周期分为电力中长期交易和电力现货交易。
其中,电力现货交易是通过现货交易平台,在日前及更短时间内集中开展的次日、日内至实时调度之前电力交易活动的总称。
翻译一下就是:
现货交易离实际运行时点更近,更容易反映短期供需变化。
例如:
高温天气导致负荷上升。
新能源出力较高导致供给增加。
机组检修影响可用容量。
用电低谷时段需求下降。
输电断面约束影响局部价格。
这些因素都可能影响现货市场价格。
*但注意:不同省份现货市场规则、价格披露口径、价格上下限、结算方式不完全相同。所以本书在教学阶段只用模拟数据练习方法,不直接替代真实市场分析。
3. 峰谷分时电价:引导用户削峰填谷
峰谷分时电价更常见于用户侧电价场景。
国家发展改革委关于进一步完善分时电价机制的通知提出,要发挥分时电价信号作用,引导用户削峰填谷、改善电力供需状况、促进新能源消纳。
相关政策解读也提到,各地应结合电力供需状况、新能源装机占比等因素,科学划分峰谷时段,合理确定峰谷电价价差。
也就是说:
用电紧张、系统负荷高的时段,价格可以更高。
用电低谷、系统压力小的时段,价格可以更低。
这样可以引导部分用户把用电从高峰时段挪到低谷时段。
但这里要特别区分两个概念:
峰谷价差,通常需要先定义峰段和谷段,再计算峰段电价与谷段电价之间的差异。
最高最低价差,只是某一段时间内最高电价减最低电价。
本章暂不展开真实分时电价时段划分。
因此,本章不严格计算“峰谷价差”,而是先用:
最高最低价差 = 最高电价 - 最低电价
演示价差指标的计算方法。
如果后续要计算严格意义上的峰谷价差,需要先明确:
峰段、谷段分别是哪些时段。
采用单一价格还是时段均价。
是否使用用户侧分时电价。
是否使用现货市场价格。
统计周期是日、月还是年。
这些口径不明确,不能随便写“峰谷价差”。
四、电力交易基本逻辑:供需关系如何影响电价?
电力交易里有一个非常基础的逻辑:
当电力供应相对宽松时,价格压力可能较小;当负荷高、供应紧张时,价格更容易上行。
这句话并不等于:
负荷一高,电价一定上涨。
真实电价还会受到很多因素影响:
机组报价、新能源出力、外送外购、输电约束、市场规则、检修安排、燃料成本、天气变化、交易限价、市场干预规则、中长期合约结算、辅助服务费用分摊等。
但从入门角度看,供需关系仍然是理解电价变化的第一把钥匙。
在 Python 里,我们可以用一个非常简化的教学变量表示供需松紧:
供需余量 = 可用供给能力 - 实际负荷
如果供需余量较大,说明系统相对宽松。
如果供需余量较小,说明系统相对紧张。
本章实操会用模拟数据画出:
负荷变化、供需余量变化、电价变化。
让你直观看到:
“在本教学模拟数据中,当负荷升高、供需余量变小时,模拟电价可能上升。”
*注意:这只是教学模拟,不代表真实市场出清机制。真实市场价格由市场规则、申报报价、供需情况、安全校核、输电约束、市场限价、结算机制等多种因素共同决定。
五、核心指标1:负荷率
负荷率可以先理解为:
“一段时间内,平均负荷相对于最大负荷的比例。”
本书采用一个适合入门教学的口径:
负荷率 = 平均负荷 ÷ 最大负荷
例如:
某月平均负荷是 600MW。
最大负荷是 1000MW。
那么月度负荷率就是:
600 ÷ 1000 = 60%
负荷率越高,通常说明平均负荷和最大负荷差距较小,负荷曲线相对平稳。
负荷率越低,通常说明最大负荷和平均负荷差距较大,可能存在尖峰负荷明显的问题。
但这里要注意:
负荷率不是越高越好,也不是越低越差。
它要结合用户类型、生产班次、行业特征、季节、设备容量、电价机制一起看。
例如:
数据中心可能负荷比较平稳。
商业综合体可能白天高、夜间低。
工厂可能和生产班次强相关。
充电站可能存在明显高峰时段。
光伏出力和用户负荷匹配情况,也会影响用户侧分析。
所以负荷率是一个入口指标,不是最终结论。
不能简单理解为:
“负荷率低,就一定适合做储能。”
而是:
“负荷率可以帮助我们初步观察负荷平稳程度,但是否存在优化空间,还需要结合负荷曲线、电价机制、设备容量、生产约束和项目经济性进一步分析。”
六、核心指标2:电价波动程度指标
电价波动程度可以先理解为:
“一段时间内,电价上下变化有多明显。”
对初学者来说,我们先不用复杂金融模型。
本章采用两个简单指标:
第一,电价标准差。
第二,电价变异系数。
标准差可以理解为:
价格围绕平均值上下波动的幅度。
变异系数可以理解为:
电价变异系数 = 电价标准差 ÷ 平均电价
为什么要除以平均电价?
因为不同地区、不同价格类型的平均水平可能不一样。
只看标准差,有时候不好比较。
例如:
A 地区平均电价 300 元/MWh,标准差 30。
B 地区平均电价 600 元/MWh,标准差 30。
虽然标准差一样,但相对波动程度不一样。
所以变异系数能帮助我们做一个相对比较。
但也要注意:
本章说的“电价波动程度指标”,不是金融意义上的收益率波动率。
它只是对价格序列离散程度的入门量化描述。
它不能保证未来价格走势。
更不能直接推出交易收益。
不能简单理解为:
“电价波动越大,越容易赚钱。”
而是:
“电价波动程度可以帮助我们观察价格变化特征,但是否具备交易价值或项目价值,还需要结合市场规则、交易准入、结算机制、项目参数、成本约束和风险控制进一步分析。”
七、简单平均电价和电量加权平均电价
很多新手计算电价时,会直接使用:
平均电价 = 所有时段电价的平均值
这个口径没有错,但它只是“简单平均电价”。
简单平均电价适合观察价格序列本身的平均水平。
但如果你要分析用户实际用电成本,就不能只看简单平均电价。
因为用户在每个时段的用电量不同。
如果用户在高价时段用电多,实际用电成本就会更高。
如果用户在低价时段用电多,实际用电成本就会更低。
这时更适合使用:
电量加权平均电价 = Σ(电价 × 电量) ÷ Σ电量
也就是说:
先计算每个时段的电费,再把所有时段电费加总,最后除以总电量。
本章代码会同时计算:
简单平均电价、电量加权平均电价。
这样就更容易理解:
同样是“平均电价”,不同口径代表不同含义。
这也是电力数据分析中非常重要的习惯:
不要只看指标名字,要先看指标口径。
八、示例数据说明
本章实操使用教学用模拟数据,模拟某地区 2026 年 1 月完整小时级数据。
时间范围:
2026-01-01 00:00 至 2026-01-31 23:00
共:
31 天 × 24 小时 = 744 条记录
字段包括:
本章会计算:
日用电量、月用电量、月度平均负荷、月度最大负荷、月度负荷率、简单平均电价、电量加权平均电价、最高电价、最低电价、最高最低价差、电价标准差、电价变异系数、负荷与电价相关系数。
并生成一份简单的文本版分析报告。
*再次强调:以下数据为教学用模拟数据,不代表任何地区真实负荷、真实现货电价、真实分时电价或真实交易规则。
为了减少路径报错,建议把本章代码放在一个简单英文路径中,例如:
D:\python_power_trade\chapter05
本章代码会自动在这个文件夹中创建和读取:
monthly_power_data.csv
处理完成后,也会把所有结果文件导出到同一个文件夹中。
下面的代码不要求你一次性全部看懂。
第5章的目标只有一个:
用 Python 跑通“负荷 → 电量 → 电费 → 负荷率 → 价差 → 电价波动程度 → 自动报告”的基础流程。
如果你不想手动整理文件,可以在文末私信关键词领取本章资料包,里面已经准备好代码、模拟数据、图表示例、指标速查表和报告模板。
九、Python 完整代码
本章需要安装:
python -m pip install pandas matplotlib
如果 Windows 上 python 命令不生效,可以尝试:
py -m pip install pandas matplotlib
如果下载较慢,可以使用:
python -m pip install pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
或者:
py -m pip install pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
请新建文件:
chapter05_power_metrics.py
复制下面完整代码运行:
# -*- coding: utf-8 -*-# 第5章实操:用 Python 量化理解负荷、电量、电价、负荷率和电价波动程度# 说明:# 1. 本案例使用教学用模拟数据,不代表任何地区真实负荷、电价或交易规则# 2. 本案例仅用于 Python 数据分析学习,不构成交易建议、投资建议或收益承诺from pathlib import Pathimport pandas as pdimport matplotlib.pyplot as pltprint("=== Python电力交易第5章:核心概念量化分析 ===")# 1. 找到当前 Python 文件所在文件夹current_folder = Path(__file__).resolve().parent# 2. 设置输入、输出文件路径data_file = current_folder / "monthly_power_data.csv"processed_file = current_folder / "monthly_power_data_processed.csv"daily_summary_file = current_folder / "daily_energy_summary.csv"monthly_summary_file = current_folder / "monthly_metrics_summary.csv"report_file = current_folder / "monthly_power_analysis_report.txt"load_chart_file = current_folder / "monthly_load_trend.png"supply_margin_chart_file = current_folder / "monthly_supply_margin_trend.png"price_chart_file = current_folder / "monthly_price_trend.png"load_price_chart_file = current_folder / "load_price_relationship.png"supply_margin_price_chart_file = current_folder / "supply_margin_price_relationship.png"# 3. 如果文件不存在,自动创建一份教学用模拟数据ifnot data_file.exists():# 生成 2026 年 1 月完整小时级时间序列# 2026 年 1 月共 31 天,每天 24 小时,共 744 条记录 time_range = pd.date_range( start="2026-01-01 00:00", end="2026-01-31 23:00", freq="h" ) data = []for dt in time_range: hour = dt.hour day = dt.day# 设计一个简单的教学用负荷规律:# 夜间负荷较低,白天负荷较高,晚高峰略高if0 <= hour <= 6: load_mw = 420elif7 <= hour <= 11: load_mw = 650elif12 <= hour <= 17: load_mw = 760elif18 <= hour <= 21: load_mw = 820else: load_mw = 560# 加一点周期性变化,让数据更像有规律的时间序列 load_mw = load_mw + (day % 7) * 15# 教学用可用供给能力 available_supply_mw = 1200# 供需余量 supply_margin_mw = available_supply_mw - load_mw# 教学用价格逻辑:# 负荷越高、供需余量越小,模拟电价越高# scarcity_pressure 表示供需偏紧带来的价格压力 scarcity_pressure = max(0, 500 - supply_margin_mw) price_yuan_mwh = (260 + (load_mw - 400) * 0.30 + scarcity_pressure * 0.25 )# 晚高峰增加一点价格压力if18 <= hour <= 21: price_yuan_mwh = price_yuan_mwh + 80# 夜间低谷价格略低if0 <= hour <= 6: price_yuan_mwh = price_yuan_mwh - 40 data.append([ dt, load_mw, available_supply_mw, supply_margin_mw, round(price_yuan_mwh, 2) ]) df_sample = pd.DataFrame( data, columns=["datetime","load_mw","available_supply_mw","supply_margin_mw","price_yuan_mwh" ] ) df_sample.to_csv(data_file, index=False, encoding="utf-8-sig") print(f"\n未发现 {data_file.name},已自动创建教学用模拟数据。")else: print(f"\n已发现 {data_file.name},将直接读取该文件。")# 4. 读取数据df = pd.read_csv(data_file, encoding="utf-8-sig")# 5. 检查必要字段是否存在required_columns = ["datetime","load_mw","available_supply_mw","supply_margin_mw","price_yuan_mwh",]for column in required_columns:if column notin df.columns:raise ValueError(f"CSV 文件中缺少必要字段:{column}")# 6. 转换时间格式df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")# 7. 转换数字字段numeric_columns = ["load_mw","available_supply_mw","supply_margin_mw","price_yuan_mwh",]for column in numeric_columns: df[column] = pd.to_numeric(df[column], errors="coerce")# 8. 检查是否存在无法识别的时间或数字if df["datetime"].isna().any():raise ValueError("datetime 字段中存在无法识别的时间,请检查 CSV 文件。")for column in numeric_columns:if df[column].isna().any():raise ValueError(f"{column} 字段中存在无法转换成数字的内容,请检查 CSV 文件。")# 9. 提取日期、月份、小时df["date"] = df["datetime"].dt.datedf["month"] = df["datetime"].dt.to_period("M")df["hour"] = df["datetime"].dt.hour# 10. 计算小时电量# 本案例是小时级数据,并假设 load_mw 表示该小时平均负荷# 因此每条记录对应 1 小时,电量 = 平均负荷 × 1小时df["energy_mwh"] = df["load_mw"] * 1# 11. 计算时段电费# 电价单位是 元/MWh,电量单位是 MWh,所以相乘得到 元df["energy_cost_yuan"] = df["energy_mwh"] * df["price_yuan_mwh"]# 12. 电价单位换算df["price_yuan_kwh"] = df["price_yuan_mwh"] / 1000print("\n【1】数据预览:")print(df.head())print("\n数据记录数:", len(df))# 13. 按日统计电量和价格daily_energy = df.groupby("date").agg( daily_record_count=("datetime", "count"), daily_energy_mwh=("energy_mwh", "sum"), daily_avg_load_mw=("load_mw", "mean"), daily_max_load_mw=("load_mw", "max"), daily_simple_avg_price_yuan_mwh=("price_yuan_mwh", "mean"), daily_total_cost_yuan=("energy_cost_yuan", "sum")).reset_index()daily_energy["daily_weighted_avg_price_yuan_mwh"] = ( daily_energy["daily_total_cost_yuan"] / daily_energy["daily_energy_mwh"])# 14. 增加日度记录数完整性检查daily_expected_record_count = 24daily_energy["daily_expected_record_count"] = daily_expected_record_countdaily_energy["daily_is_record_count_complete"] = ( daily_energy["daily_record_count"] == daily_energy["daily_expected_record_count"])print("\n【2】日度统计结果预览:")print(daily_energy.head())# 15. 按月统计核心指标monthly_summary = df.groupby("month").agg( record_count=("datetime", "count"), total_energy_mwh=("energy_mwh", "sum"), total_energy_cost_yuan=("energy_cost_yuan", "sum"), avg_load_mw=("load_mw", "mean"), max_load_mw=("load_mw", "max"), min_load_mw=("load_mw", "min"), simple_avg_price_yuan_mwh=("price_yuan_mwh", "mean"), max_price_yuan_mwh=("price_yuan_mwh", "max"), min_price_yuan_mwh=("price_yuan_mwh", "min"), price_std=("price_yuan_mwh", "std"), avg_supply_margin_mw=("supply_margin_mw", "mean"), min_supply_margin_mw=("supply_margin_mw", "min")).reset_index()# 16. 增加月度记录数完整性检查monthly_expected_record_count = 31 * 24monthly_summary["expected_record_count"] = monthly_expected_record_countmonthly_summary["is_record_count_complete"] = ( monthly_summary["record_count"] == monthly_summary["expected_record_count"])# 17. 计算电量加权平均电价monthly_summary["weighted_avg_price_yuan_mwh"] = ( monthly_summary["total_energy_cost_yuan"] / monthly_summary["total_energy_mwh"])# 18. 计算负荷率monthly_summary["load_factor"] = ( monthly_summary["avg_load_mw"] / monthly_summary["max_load_mw"])# 19. 计算最高最低价差monthly_summary["max_min_price_spread"] = ( monthly_summary["max_price_yuan_mwh"] - monthly_summary["min_price_yuan_mwh"])# 20. 计算电价变异系数monthly_summary["price_cv"] = ( monthly_summary["price_std"] / monthly_summary["simple_avg_price_yuan_mwh"])print("\n【3】月度核心指标:")print(monthly_summary)# 21. 简单分析负荷与电价关系correlation_load_price = df["load_mw"].corr(df["price_yuan_mwh"])# 22. 简单分析供需余量与电价关系correlation_margin_price = df["supply_margin_mw"].corr(df["price_yuan_mwh"])print("\n【4】负荷与电价相关系数:")print(round(correlation_load_price, 4))print("\n【5】供需余量与电价相关系数:")print(round(correlation_margin_price, 4))# 23. 定义一个简单画图函数defsave_line_chart(x, y, title, xlabel, ylabel, output_file): plt.figure(figsize=(10, 5)) plt.plot(x, y) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.tight_layout() plt.savefig(output_file, dpi=150) plt.close()defsave_scatter_chart(x, y, title, xlabel, ylabel, output_file): plt.figure(figsize=(8, 5)) plt.scatter(x, y) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.tight_layout() plt.savefig(output_file, dpi=150) plt.close()# 24. 生成图表1:负荷趋势图save_line_chart( df["datetime"], df["load_mw"],"Monthly Load Trend","Datetime","Load (MW)", load_chart_file)# 25. 生成图表2:供需余量趋势图save_line_chart( df["datetime"], df["supply_margin_mw"],"Monthly Supply Margin Trend","Datetime","Supply Margin (MW)", supply_margin_chart_file)# 26. 生成图表3:电价趋势图save_line_chart( df["datetime"], df["price_yuan_mwh"],"Monthly Price Trend","Datetime","Price (Yuan/MWh)", price_chart_file)# 27. 生成图表4:负荷与电价关系图save_scatter_chart( df["load_mw"], df["price_yuan_mwh"],"Load and Price Relationship","Load (MW)","Price (Yuan/MWh)", load_price_chart_file)# 28. 生成图表5:供需余量与电价关系图save_scatter_chart( df["supply_margin_mw"], df["price_yuan_mwh"],"Supply Margin and Price Relationship","Supply Margin (MW)","Price (Yuan/MWh)", supply_margin_price_chart_file)# 29. 导出数据结果df.to_csv(processed_file, index=False, encoding="utf-8-sig")daily_energy.to_csv(daily_summary_file, index=False, encoding="utf-8-sig")monthly_summary.to_csv(monthly_summary_file, index=False, encoding="utf-8-sig")# 30. 生成一份简单分析报告summary = monthly_summary.iloc[0]report_text = f"""第5章教学用月度电力数据分析报告一、基础用电情况本月样本记录数:{summary['record_count']:.0f} 条本月理论应有记录数:{summary['expected_record_count']:.0f} 条本月记录数是否完整:{summary['is_record_count_complete']}本月总电量:{summary['total_energy_mwh']:.2f} MWh本月平均负荷:{summary['avg_load_mw']:.2f} MW本月最大负荷:{summary['max_load_mw']:.2f} MW本月最小负荷:{summary['min_load_mw']:.2f} MW本月负荷率:{summary['load_factor']:.2%}说明:本章使用的是 2026 年 1 月完整小时级教学样本。若真实业务中记录数不完整,则总电量、平均负荷、负荷率等指标都需要谨慎解释。二、电价情况本月简单平均电价:{summary['simple_avg_price_yuan_mwh']:.2f} 元/MWh本月电量加权平均电价:{summary['weighted_avg_price_yuan_mwh']:.2f} 元/MWh本月最高电价:{summary['max_price_yuan_mwh']:.2f} 元/MWh本月最低电价:{summary['min_price_yuan_mwh']:.2f} 元/MWh本月最高最低价差:{summary['max_min_price_spread']:.2f} 元/MWh本月电价标准差:{summary['price_std']:.2f}本月电价变异系数:{summary['price_cv']:.2%}说明:简单平均电价用于观察价格序列本身的平均水平。电量加权平均电价更接近用电成本分析口径。最高最低价差不等同于严格意义上的峰谷价差。如果要计算峰谷价差,需要先定义峰段和谷段。三、供需与价格关系本月平均供需余量:{summary['avg_supply_margin_mw']:.2f} MW本月最小供需余量:{summary['min_supply_margin_mw']:.2f} MW本教学样本中,负荷与电价的相关系数为:{correlation_load_price:.4f}本教学样本中,供需余量与电价的相关系数为:{correlation_margin_price:.4f}说明:由于本章模拟电价公式中人为加入了负荷和供需余量因素,因此相关系数只用于演示“如何用 Python 计算相关性”。它不能证明真实市场中负荷、供需余量与电价必然存在同样关系,更不能作为交易判断依据。四、风险提示本报告基于教学用模拟数据生成,不代表任何真实地区、真实用户、真实现货价格或真实交易规则。本报告仅用于 Python 数据分析学习,不构成交易建议、投资建议、售电策略建议、储能收益测算依据或收益承诺。"""with open(report_file, "w", encoding="utf-8") as f: f.write(report_text)print("\n处理完成!已生成以下文件:")print(f"1. {data_file.name}")print(f"2. {processed_file.name}")print(f"3. {daily_summary_file.name}")print(f"4. {monthly_summary_file.name}")print(f"5. {load_chart_file.name}")print(f"6. {supply_margin_chart_file.name}")print(f"7. {price_chart_file.name}")print(f"8. {load_price_chart_file.name}")print(f"9. {supply_margin_price_chart_file.name}")print(f"10. {report_file.name}")print("\n恭喜你,已经完成负荷率、电价波动程度和简易分析报告生成!")print("再次提醒:本案例为教学模拟,不构成交易建议、投资建议或收益承诺。")
十、代码解释
1. 文件编码声明
# -*- coding: utf-8 -*-
这一行表示代码文件使用 UTF-8 编码。
现在大多数新版编辑器默认支持 UTF-8,所以这行通常不是必须的。
但对零基础读者来说,保留这一行可以减少少数电脑上中文乱码的概率。
2. 导入工具库
from pathlib import Pathimport pandas as pdimport matplotlib.pyplot as plt
这里导入了三个工具。
pathlib 是 Python 自带的路径处理工具,用来帮助程序找到文件位置。
pandas 用来读取和处理表格数据。
matplotlib.pyplot 用来画图。
其中:
import pandas as pd
意思是把 pandas 简写成 pd。
import matplotlib.pyplot as plt
意思是把画图工具简写成 plt。
这些都是 Python 数据分析中非常常见的写法。
3. 找到当前代码文件所在文件夹
current_folder = Path(__file__).resolve().parent
这行代码的意思是:
找到当前这个 Python 文件所在的文件夹。
为什么要这样写?
因为很多新手会遇到一个问题:
明明 CSV 文件和 Python 文件放在同一个文件夹里,但运行时还是提示找不到文件,或者输出结果不知道保存到哪里去了。
原因通常是:
Python 当前运行位置,和代码文件实际所在位置不一致。
使用这一行代码后,程序会从“当前 Python 文件所在文件夹”去生成、读取和导出文件,路径更稳定。
4. 设置输入和输出文件路径
data_file = current_folder / "monthly_power_data.csv"processed_file = current_folder / "monthly_power_data_processed.csv"daily_summary_file = current_folder / "daily_energy_summary.csv"monthly_summary_file = current_folder / "monthly_metrics_summary.csv"report_file = current_folder / "monthly_power_analysis_report.txt"
这几行代码表示:
monthly_power_data.csv 是原始教学模拟数据。
monthly_power_data_processed.csv 是处理后的明细数据。
daily_energy_summary.csv 是日度统计结果。
monthly_metrics_summary.csv 是月度核心指标结果。
monthly_power_analysis_report.txt 是自动生成的文本版分析报告。
它们都会放在当前 Python 文件所在文件夹中。
代码还会把五张图表也保存到同一个文件夹里。
这样对新手更友好,不容易找不到文件。
5. 自动生成一个完整月的小时级数据
time_range = pd.date_range( start="2026-01-01 00:00", end="2026-01-31 23:00", freq="h")
这段代码生成从 2026 年 1 月 1 日 00:00 到 2026 年 1 月 31 日 23:00 的小时级时间序列。
也就是说,每一行代表一个小时。
2026 年 1 月有 31 天。
每天 24 小时。
所以总共是:
31 × 24 = 744 行数据
这就是完整自然月小时级数据。
真实业务中,也要先检查数据完整性。
如果一个月理论上应该有 744 条记录,但你实际只有 700 条,就不能直接把统计结果当成完整月度结果。
6. 构造教学用负荷规律
if0 <= hour <= 6: load_mw = 420elif7 <= hour <= 11: load_mw = 650elif12 <= hour <= 17: load_mw = 760elif18 <= hour <= 21: load_mw = 820else: load_mw = 560
这段代码模拟一天内不同时间的负荷变化:
夜间负荷较低,白天负荷上升,晚高峰负荷最高。
*这只是教学模拟。真实负荷曲线要看具体用户类型。工厂、商场、数据中心、居民区、充电站的负荷特征都不一样。
7. 计算供需余量
supply_margin_mw = available_supply_mw - load_mw
这行代码计算供需余量。
可以简单理解为:
供需余量 = 可用供给能力 - 实际负荷
如果供需余量大,说明系统相对宽松。
如果供需余量小,说明系统相对紧张。
*但真实市场中,“可用供给能力”并不是一个随便设定的数字,它可能涉及机组可用容量、新能源预测、外来电、检修安排、输电约束等因素。本章只是用它演示供需松紧的基本概念。
8. 构造教学用电价
scarcity_pressure = max(0, 500 - supply_margin_mw)price_yuan_mwh = (260 + (load_mw - 400) * 0.30 + scarcity_pressure * 0.25)
这段代码是教学用价格公式。
它表达的是:
负荷越高,模拟电价越高。
供需余量越小,模拟电价越高。
其中:
scarcity_pressure = max(0, 500 - supply_margin_mw)
可以理解为:
当供需余量低于 500MW 时,出现一定的供需偏紧压力。
供需余量越小,这个压力越大。
*再次强调:这个价格公式是人为构造出来的教学公式。它不是任何真实市场出清模型。不能用它预测真实现货价格。
9. 检查必要字段是否存在
required_columns = ["datetime","load_mw","available_supply_mw","supply_margin_mw","price_yuan_mwh",]for column in required_columns:if column notin df.columns:raise ValueError(f"CSV 文件中缺少必要字段:{column}")
这段代码用于检查 CSV 文件里是否有本章需要的字段。
如果读者手动修改过 CSV 表头,比如把 load_mw 改成了 load,程序会主动提示缺少哪个字段。
这样比直接出现 KeyError 更适合新手理解。
小白阶段建议先不要改字段名,先完整跑通示例代码。
10. 转换时间和数字字段
df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
这行代码把 datetime 转换成 Python 可以识别的时间格式。
下面这段代码把负荷、供给能力、供需余量、电价转换成数字:
numeric_columns = ["load_mw","available_supply_mw","supply_margin_mw","price_yuan_mwh",]for column in numeric_columns: df[column] = pd.to_numeric(df[column], errors="coerce")
为什么要转换?
因为 CSV 文件里的数字,有时看起来是数字,但实际可能被当作文本。
如果读者手动把数据改成:
500MW约500-
后面计算就会出错。
所以本章代码会先进行数字转换,并检查是否存在无法转换的内容。
11. 计算小时电量
df["energy_mwh"] = df["load_mw"] * 1
因为本章数据是小时级数据,并假设 load_mw 表示该小时平均负荷。
所以每条记录对应 1 小时。
如果某小时平均负荷是 500MW,那么该小时电量近似为:
500MW × 1h = 500MWh
如果以后处理 15 分钟数据,就不能乘以 1,而要乘以 0.25。
即:
df["energy_mwh"] = df["load_mw"] * 0.25
前提仍然是:
load_mw 代表该 15 分钟时段的平均负荷。
12. 计算时段电费
df["energy_cost_yuan"] = df["energy_mwh"] * df["price_yuan_mwh"]
这里的单位关系是:
电量单位:MWh。
电价单位:元/MWh。
所以:
MWh × 元/MWh = 元
这一步是为了后面计算电量加权平均电价。
13. 计算电量加权平均电价
monthly_summary["weighted_avg_price_yuan_mwh"] = ( monthly_summary["total_energy_cost_yuan"] / monthly_summary["total_energy_mwh"])
这行代码计算电量加权平均电价。
公式是:
电量加权平均电价 = 总电费 ÷ 总电量
它和简单平均电价不同。
简单平均电价只看价格序列本身。
电量加权平均电价会考虑用户在不同时段用了多少电。
所以在用户用电成本分析中,电量加权平均电价通常更有业务意义。
14. 按日统计电量和价格
daily_energy = df.groupby("date").agg( daily_record_count=("datetime", "count"), daily_energy_mwh=("energy_mwh", "sum"), daily_avg_load_mw=("load_mw", "mean"), daily_max_load_mw=("load_mw", "max"), daily_simple_avg_price_yuan_mwh=("price_yuan_mwh", "mean"), daily_total_cost_yuan=("energy_cost_yuan", "sum")).reset_index()
这段代码按日期分组。
每天统计:
日记录数、日用电量、日均负荷、日最大负荷、日简单平均电价、日总电费。
后面又新增了:
daily_energy["daily_weighted_avg_price_yuan_mwh"] = ( daily_energy["daily_total_cost_yuan"] / daily_energy["daily_energy_mwh"])
也就是日度电量加权平均电价。
这是电力数据分析中非常常见的操作。
以后如果你拿到一个用户一个月的负荷和电价数据,也可以用类似方法做日度汇总。
15. 日度记录数完整性检查
daily_expected_record_count = 24daily_energy["daily_expected_record_count"] = daily_expected_record_countdaily_energy["daily_is_record_count_complete"] = ( daily_energy["daily_record_count"] == daily_energy["daily_expected_record_count"])
这几行代码用于检查每天的记录数是否完整。
本章是小时级数据,所以一天理论上应该有 24 条记录。
如果某一天只有 20 条,说明这一天数据不完整。
真实业务中,数据颗粒度可能不同。
如果是 30 分钟数据,一天理论上应该有 48 条。
如果是 15 分钟数据,一天理论上应该有 96 条。
所以实际项目中,需要根据数据颗粒度调整这个值。
16. 计算月度负荷率
monthly_summary["load_factor"] = ( monthly_summary["avg_load_mw"] / monthly_summary["max_load_mw"])
这段代码计算:
负荷率 = 平均负荷 ÷ 最大负荷
如果负荷率比较高,说明平均负荷和最大负荷差距不大。
如果负荷率比较低,说明最大负荷明显高于平均负荷,可能存在尖峰负荷较突出的问题。
但这只是初步判断,不能直接得出优化结论。
真实业务中还要结合:
用户行业、生产班次、电价机制、容量电费或需量电费、设备约束、是否允许调整生产计划、是否具备储能、需求响应或负荷管理条件等。
17. 月度记录数完整性检查
monthly_expected_record_count = 31 * 24monthly_summary["expected_record_count"] = monthly_expected_record_countmonthly_summary["is_record_count_complete"] = ( monthly_summary["record_count"] == monthly_summary["expected_record_count"])
这几行代码用于检查月度记录数是否完整。
本章模拟的是 2026 年 1 月小时级数据。
2026 年 1 月有 31 天。
每天 24 小时。
所以理论上应该有:
31 × 24 = 744 条记录
如果真实业务中只有 700 条,就不能直接写成“完整月度分析”。
更稳妥的说法是:
“基于现有样本计算得到的月度样本统计结果”。
18. 计算最高最低价差
monthly_summary["max_min_price_spread"] = ( monthly_summary["max_price_yuan_mwh"] - monthly_summary["min_price_yuan_mwh"])
这段代码计算:
最高最低价差 = 最高电价 - 最低电价
注意:
这不是严格意义上的峰谷价差。严格意义上的峰谷价差通常要先定义峰段和谷段。
例如:
峰谷价差 = 峰段平均电价 - 谷段平均电价
而本章只是用最高最低价差演示价差指标的计算方法。
最高最低价差可以帮助我们观察价格范围,但不能直接作为储能收益判断依据。
19. 计算电价波动程度
monthly_summary["price_cv"] = ( monthly_summary["price_std"] / monthly_summary["simple_avg_price_yuan_mwh"])
这里的 price_std 是电价标准差。
price_cv 是电价变异系数。
它们都可以用来描述电价波动程度。
小白阶段可以先这样理解:
标准差越大,价格绝对波动越明显。变异系数越大,价格相对波动越明显。
但要注意:
它们只是描述历史价格序列的统计指标,不是收益率,不是套利空间,也不能保证未来价格走势。
20. 计算负荷和电价相关系数
correlation_load_price = df["load_mw"].corr(df["price_yuan_mwh"])
这行代码计算负荷和电价之间的相关性。
结果范围通常在 -1 到 1 之间。
接近 1,表示两者更倾向于同向变化。
接近 -1,表示两者更倾向于反向变化。
接近 0,表示线性关系不明显。
但注意:
相关不等于因果。
即使负荷和电价相关,也不能简单说“负荷导致电价上涨”。
更何况,本章模拟电价公式中本来就人为加入了负荷因素。
所以这个相关系数只用于演示计算方法。
不能作为真实市场规律。
21. 计算供需余量和电价相关系数
correlation_margin_price = df["supply_margin_mw"].corr(df["price_yuan_mwh"])
这行代码计算供需余量和电价之间的相关性。
在本章教学模拟中,供需余量越小,电价越容易升高。
所以二者可能呈现负相关。
但真实市场中,供需余量只是影响价格的因素之一。
真实电价还受到市场报价、出清规则、输电约束、新能源出力、检修、限价、结算方式等因素影响。
因此,这个相关系数也只能作为教学演示。
22. 保存图表
本章代码定义了两个简单函数:
defsave_line_chart(x, y, title, xlabel, ylabel, output_file): plt.figure(figsize=(10, 5)) plt.plot(x, y) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.tight_layout() plt.savefig(output_file, dpi=150) plt.close()
以及:
defsave_scatter_chart(x, y, title, xlabel, ylabel, output_file): plt.figure(figsize=(8, 5)) plt.scatter(x, y) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.tight_layout() plt.savefig(output_file, dpi=150) plt.close()
它们的作用是:
生成图表。
保存为 PNG 图片。
关闭图表对象。
本章没有使用 plt.show() 强制弹出窗口。
这样做是为了减少新手遇到图表窗口卡住、连续弹出多个窗口、误以为程序没有运行完的问题。
运行完成后,直接在代码所在文件夹里打开 PNG 图片即可。
十一、运行结果说明
运行完成后,你会得到一批文件。
1. 原始模拟数据
monthly_power_data.csv
这是自动生成的教学用模拟数据。
包含:时间、负荷、可用供给能力、供需余量、电价。
2. 处理后数据
monthly_power_data_processed.csv
这个文件增加了:
日期、月份、小时、电量、时段电费、元/kWh 电价。
适合后续继续分析。
3. 日度统计结果
daily_energy_summary.csv
这个文件包含每天的:
日记录数、日理论应有记录数、日记录数是否完整、日用电量、日均负荷、日最大负荷、日简单平均电价、日电量加权平均电价、日总电费。
4. 月度核心指标
monthly_metrics_summary.csv
这个文件包含本章最重要的结果:
样本记录数、理论应有记录数、记录数是否完整、月总电量、月总电费、平均负荷、最大负荷、最小负荷、负荷率、简单平均电价、电量加权平均电价、最高电价、最低电价、最高最低价差、电价标准差、电价变异系数、平均供需余量、最小供需余量。
5. 五张图表
代码会生成五张图片:
monthly_load_trend.pngmonthly_supply_margin_trend.pngmonthly_price_trend.pngload_price_relationship.pngsupply_margin_price_relationship.png
分别表示:
月度负荷趋势图。
月度供需余量趋势图。
月度电价趋势图。
负荷与电价关系图。
供需余量与电价关系图。
这些图可以直接用于学习笔记,也可以作为后续报告模板的基础。
但在对外报告中,必须说明数据来源、统计口径和样本范围。
6. 文本版分析报告
monthly_power_analysis_report.txt
这是本章生成的简易报告。
它会自动写出:
基础用电情况、电价情况、供需与价格关系、风险提示。
这一步很重要。
因为数据分析不是只输出数字,更重要的是把数字翻译成业务语言。
但也要注意:
自动生成的报告只是模板,真实业务报告需要人工复核数据来源、指标口径和业务解释。
7. 文件保存位置
本章所有文件都会生成在当前 Python 文件所在文件夹中。
也就是和这个文件放在一起:
chapter05_power_metrics.py
你应该能在同一个文件夹中看到:
monthly_power_data.csvmonthly_power_data_processed.csvdaily_energy_summary.csvmonthly_metrics_summary.csvmonthly_load_trend.pngmonthly_supply_margin_trend.pngmonthly_price_trend.pngload_price_relationship.pngsupply_margin_price_relationship.pngmonthly_power_analysis_report.txt
十二、本章小练习
练习 1:修改晚高峰负荷
把代码中的:
load_mw = 820
改成:
load_mw = 950
重新运行代码。
观察:
最大负荷是否上升?
负荷率是否变化?
最高电价是否变化?
供需余量是否变小?
练习 2:修改供给能力
把代码中的:
available_supply_mw = 1200
改成:
available_supply_mw = 1000
重新运行代码。
观察:
供需余量是否变小?
最高电价是否变化?
供需余量与电价关系图是否变化?
思考:
如果供需余量长期偏小,市场价格可能会受到什么影响?
*注意:这仍然只是教学模拟,不代表真实市场出清结果。
练习 3:计算元/kWh 电价
观察代码中的:
df["price_yuan_kwh"] = df["price_yuan_mwh"] / 1000
然后打印前 5 行:
print(df[["datetime", "price_yuan_mwh", "price_yuan_kwh"]].head())
理解:
元/MWh 和 元/kWh 相差 1000 倍
练习 4:改成 15 分钟数据时怎么处理?
思考题:
如果你的负荷数据不是每小时一条,而是每 15 分钟一条,那么这行代码:
df["energy_mwh"] = df["load_mw"] * 1
应该怎么改?
答案提示:
每 15 分钟是 0.25 小时,所以应该改成:
df["energy_mwh"] = df["load_mw"] * 0.25
前提是:
load_mw 表示该 15 分钟时段的平均负荷。
练习 5:比较简单平均电价和电量加权平均电价
观察报告中的两个指标:
简单平均电价电量加权平均电价
思考:
为什么这两个值可能不完全一样?
提示:
如果高价时段用电量较大,电量加权平均电价可能高于简单平均电价。
如果低价时段用电量较大,电量加权平均电价可能低于简单平均电价。
练习 6:检查记录数
观察 monthly_summary 中的:
record_countexpected_record_countis_record_count_complete
思考一个问题:
2026 年 1 月小时级数据理论上应该有多少条?
答案是:
31 × 24 = 744 条
如果真实业务中只有 700 条,还能不能直接写“完整月度分析”?
答案是:
不能。
你只能说:
这是基于现有样本计算得到的样本统计结果。
十三、本章小结
本章我们没有只是背概念,而是把电力交易中的几个核心概念变成了可以计算的指标。
你至少需要记住以下几点:
第一,负荷看功率水平,电量看累计用电量。
第二,MW 是负荷或功率单位,MWh 是电量单位,不能混用。
第三,小时电量 = 小时平均负荷 × 1 小时,前提是负荷代表该小时平均负荷。
第四,元/MWh 和元/kWh 相差 1000 倍,必须统一单位。
第五,负荷率 = 平均负荷 ÷ 最大负荷,但它只是入口指标,不是最终结论。
第六,最高最低价差不等于严格意义上的峰谷价差。
第七,简单平均电价和电量加权平均电价代表不同口径。
第八,电价标准差和变异系数可以描述价格波动程度,但不是收益率。
第九,相关系数不等于因果关系。
第十,教学模拟数据不能用于真实交易、售电报价、储能收益测算或投资判断。
这章看起来是在讲指标计算。
但真正想让你建立的是一种能力:
把电力交易概念变成可计算指标,再把指标翻译成有边界、有口径、有风险提示的业务语言。
这才是从“会写代码”走向“懂业务分析”的关键一步。
十四、资料领取与评论互动
如果你已经跑通本章代码,欢迎在评论区回复:
核心指标已跑通
如果你遇到报错,也可以直接留言:
pandas安装失败
matplotlib安装失败
ValueError
图表不显示
中文乱码
后续我会专门整理一期:
《Python 电力交易新手常见报错与解决方法》
如果你想直接领取本章配套资料,可以私信公众号关键词:
电力Python05
领取《Python电力交易》第5章核心指标资料包,包括:
chapter05_power_metrics.pymonthly_power_data.csv 模拟月度数据monthly_power_data_processed.csv 处理后数据daily_energy_summary.csv 日度统计结果monthly_metrics_summary.csv 月度核心指标结果
建议使用顺序:
先看运行说明 → 跑通完整代码 → 查看日度和月度统计结果 → 对照指标速查表理解每个指标 → 用报告模板练习写一段分析结论。
也欢迎你在评论区回答一个问题:
你现在最想先学会哪个指标?
A. 日用电量
B. 月度负荷率
C. 最高最低价差
D. 电价波动程度
E. 负荷与电价相关性
F. 电量加权平均电价
G. 自动生成分析报告
*本系列文章仅用于 Python 数据分析学习 和 电力交易基础知识科普,不构成交易建议、投资建议、售电策略建议、储能收益测算依据或具体经营决策依据。我国各省电力市场建设阶段、交易规则、价格机制、结算方式、数据披露口径和准入条件存在差异。实际业务应以国家主管部门、当地电力交易中心、电力调度机构、电网企业及正式规则文件为准。本系列所有数据均为教学用模拟数据,不代表任何真实地区、真实用户、真实负荷曲线、真实现货电价、真实分时电价或真实交易规则。本系列所有指标均为入门教学口径。真实业务中,负荷率、电价均值、价差、电价波动程度等指标,必须先明确统计周期、数据颗粒度、价格类型、是否加权、数据是否完整、是否剔除问题样本,不能直接套用教学公式输出业务结论。