简介:这份资源面向电力系统、能源管理与负荷预测方向的研究人员和学生,提供完整的电负荷与热负荷时间序列数据,可用于时间序列分析、机器学习建模以及气候因素对负荷影响的研究。压缩包共41个文件,约17.05MB,以19个Python脚本、10个CSV数据文件、8个TXT文本为主,另含2份PDF论文与演示文档、许可证及说明文件。CSV文件分别记录电负荷与热负荷在不同场景下的测量值,TXT文件则提供标准负荷曲线、温度、太阳直接辐射与风速等气象数据,便于分析温度、光照和风速对用电与取暖需求的影响。配套的分布式供暖系统调度策略代码与论文,可帮助读者将负荷数据与优化调度算法结合,探讨提升能源利用效率、减少碳排放的路径。目前已有1892人学习下载,适合需要真实数据开展负荷预测实验、复现调度策略或撰写相关论文的读者参考使用。
1. 完整电负荷、热负荷数据:从两份时序到一张可训练的宽表
做综合能源或者园区微网的人,迟早会撞上同一个问题:电负荷数据好找,热负荷数据难凑,两份数据的时间粒度、时区、缺失模式还往往对不上。你手里可能有一份 15 分钟采样的电表数据,和一份每小时抄一次的供热流量表,想拿它们一起做负荷预测,第一步就卡住了。这个标题讲的正是这件事——把电负荷和热负荷两份独立时序,整理成一份时间对齐、缺失可控、能直接喂给预测模型的完整数据集。它解决的不是模型精度问题,而是数据能不能用的问题。适合做园区能源管理、综合能源调度、建筑能耗分析,以及任何需要电热耦合预测的从业者。下面按我实际处理过的路径,从数据理解一路讲到宽表落地和验证。
2. 电负荷与热负荷的数据特征:为什么不能直接拼在一起
2.1 两类负荷的物理差异决定了采样策略不同
电负荷的本质是功率,单位通常是 kW 或 MW,采样频率可以很高,秒级、分钟级都常见,因为电表本身就支持高频采集。热负荷的本质是热量,单位是 kW·h 或 GJ,它依赖供回水温度、流量三个量共同计算,很多现场的热量表本身就是积分仪表,直接输出累积热量,采样间隔往往被设成 1 小时甚至更长。这个物理差异带来的直接后果是:电负荷序列可以做到 15 分钟一个点,热负荷序列可能只有整点值。如果你强行把热负荷插值到 15 分钟,等于在数据里注入了大量虚假信息,模型会学到不存在的波动模式。
我一般会先做一件事:把两份数据的原始采样间隔、时间戳格式、时区标记全部列出来,不急着合并。常见的时间戳格式有2024-01-01 00:00:00、2024/1/1 0:00、Unix 秒级时间戳,甚至 Excel 里的浮点日期。时区问题在跨区域项目里尤其致命,电负荷可能用本地时间,热负荷平台可能用 UTC,差 8 小时合并出来的曲线完全错位。
2.2 缺失模式不同:电负荷断点短,热负荷断点长
电负荷数据缺失通常是通信中断,断几分钟到几小时,恢复后数值连续。热负荷数据缺失往往是供暖季切换、仪表检修或者人工抄表漏记,一断就是几天甚至整月。这两种缺失不能用一个填充策略。电负荷短断点可以用线性插值或者前向填充,热负荷长断点如果落在非供暖季,直接补零是合理的;如果落在供暖季中间,补零会制造一个巨大的假低谷,模型会误以为那几天不需要供热。
下面这段代码是我常用的数据概览脚本,先把两份数据的缺失情况和采样间隔摸清楚,再决定怎么对齐。
import pandas as pd import numpy as np def profile_series(df, time_col, value_col, name): """输出一份时序数据的基本画像:采样间隔、缺失率、连续缺失段""" df = df.copy() df[time_col] = pd.to_datetime(df[time_col]) df = df.sort_values(time_col).set_index(time_col) # 采样间隔分布 intervals = df.index.to_series().diff().dt.total_seconds().dropna() interval_mode = intervals.mode().iloc[0] if not intervals.empty else np.nan # 缺失率(按重采样后的空值比例估算) resampled = df[value_col].resample(f'{int(interval_mode)}s').mean() missing_rate = resampled.isna().mean() # 连续缺失段长度 is_na = resampled.isna() groups = (~is_na).cumsum() na_runs = is_na.groupby(groups).sum() max_na_run = na_runs.max() if not na_runs.empty else 0 print(f"[{name}] 采样间隔众数: {interval_mode}s, 缺失率: {missing_rate:.2%}, " f"最长连续缺失点数: {max_na_run}") return resampled # 假设 elec_df 和 heat_df 已经读入 elec_series = profile_series(elec_df, 'ts', 'power_kw', '电负荷') heat_series = profile_series(heat_df, 'ts', 'heat_kwh', '热负荷')这段脚本的关键在resample那一步,它把原始不规则时间戳强制拉到统一网格上,再统计缺失。interval_mode取采样间隔的众数而不是均值,是因为现场数据经常混有手动补录的点,均值会被拉偏。max_na_run告诉你最长连续缺失有多长,如果热负荷这个值超过 24,基本可以判断存在整日缺失,需要单独处理而不是简单插值。
2.3 对齐粒度选择:15 分钟、1 小时还是日粒度
对齐粒度不是拍脑袋定的,它由预测目标和数据质量共同决定。如果做日前调度,1 小时粒度足够;如果做实时优化,15 分钟是常见选择;如果只做月度能耗分析,日粒度就行。我的经验是:取两份数据中较粗的那个采样间隔作为基准,电负荷降采样到热负荷的粒度,而不是把热负荷升采样到电负荷的粒度。降采样用均值,升采样用插值,前者信息损失可控,后者会造假。
提示:降采样前先确认电负荷没有尖峰脉冲,如果有,均值会把它抹掉,必要时先用中位数滤波处理一遍。
3. 时间对齐与缺失填充:把两份时序拼成一张宽表
3.1 统一时区与时间戳格式的实操步骤
第一步永远是时区归一。我习惯全部转成 UTC 存储,展示时再转回本地时间。pandas的tz_localize和tz_convert能处理大部分情况,但要注意有些数据平台导出的时间戳不带时区标记,直接tz_localize('Asia/Shanghai')会报错,需要先tz_localize(None)去掉原有标记再重新赋予。
def normalize_timezone(df, time_col, tz='Asia/Shanghai'): """统一时间戳到指定时区,输出 UTC 存储""" df = df.copy() df[time_col] = pd.to_datetime(df[time_col]) # 如果已经带时区,先转 UTC;如果不带,先本地化再转 UTC if df[time_col].dt.tz is None: df[time_col] = df[time_col].dt.tz_localize(tz) df[time_col] = df[time_col].dt.tz_convert('UTC') return df elec_df = normalize_timezone(elec_df, 'ts') heat_df = normalize_timezone(heat_df, 'ts')逻辑说明:tz_localize是给无时区时间戳“贴标签”,tz_convert是真正转换时区。顺序不能反,否则会得到错误结果。参数tz根据项目所在地填,国内项目一般用Asia/Shanghai。
3.2 重采样到统一网格:降采样与升采样的选择依据
统一网格的生成用resample加agg完成。电负荷降采样用mean,热负荷如果是累积量,降采样要用sum而不是mean,因为累积热量在时间上可加。这一点经常被忽略,用错聚合函数会导致热负荷总量偏差几倍。
# 电负荷:15分钟 -> 1小时,取均值 elec_hourly = elec_series.resample('1h').mean() # 热负荷:如果是累积热量,1小时 -> 1小时不变;如果是瞬时功率,取均值 # 假设 heat_series 是累积热量 kWh,先差分再重采样 heat_diff = heat_series.diff().clip(lower=0) # 差分得到每小时增量 heat_hourly = heat_diff.resample('1h').sum()clip(lower=0)是为了处理仪表回零或换表导致的负差分,负值在物理上不合理,直接截断比保留更安全。resample('1h')的1h是 pandas 的频率字符串,也可以写60min,效果一样。
3.3 缺失填充策略:短断点插值、长断点标记
填充策略我分三档:缺失不超过 3 个连续点,线性插值;3 到 24 个点,前向填充加滑动均值平滑;超过 24 个点,不填充,加一列is_missing标记,让模型自己学。热负荷在非供暖季的缺失直接补零,供暖季内的长缺失用同期历史均值填充,但必须加标记列。
def fill_missing(series, max_gap=3, long_gap_threshold=24): """按缺失长度分档填充,返回填充后序列和缺失标记""" filled = series.copy() is_missing = series.isna() # 短断点线性插值 filled = filled.interpolate(method='linear', limit=max_gap) # 中等断点前向填充 filled = filled.ffill(limit=long_gap_threshold) # 长断点保持 NaN,后续用历史均值或模型处理 # 标记列:1 表示原始缺失,0 表示正常 missing_flag = is_missing.astype(int) return filled, missing_flag参数max_gap控制插值最大连续点数,long_gap_threshold控制前向填充上限。这两个值没有绝对标准,我一般根据数据采集频率和业务容忍度调,15 分钟数据取 4 和 96,小时数据取 3 和 24。
3.4 合并成宽表:列命名与索引规范
合并用pd.concat按时间索引对齐,列名要能看出物理含义和单位,比如elec_kw_mean、heat_kwh_sum、heat_is_missing。索引统一用 UTC 时间戳,方便后续跨时区复用。
wide_table = pd.concat([ elec_hourly.rename('elec_kw_mean'), heat_hourly.rename('heat_kwh_sum'), heat_missing_flag.rename('heat_is_missing') ], axis=1) # 加时间特征列,方便后续建模 wide_table['hour'] = wide_table.index.hour wide_table['dayofweek'] = wide_table.index.dayofweek wide_table['is_heating_season'] = wide_table.index.month.isin([11, 12, 1, 2, 3]).astype(int) wide_table.to_parquet('load_wide_table.parquet')存成 Parquet 而不是 CSV,是因为 Parquet 保留数据类型和时区信息,读取速度快,文件体积小。列名里的_mean和_sum是给自己看的,避免半年后忘了这列是怎么聚合出来的。
4. 避坑与排查:电热负荷数据合并的 5 个血泪教训
4.1 时区没对齐,曲线整体平移 8 小时
现象:电负荷早高峰在早上 8 点,热负荷早高峰在下午 4 点,两条曲线形状相似但错开。原因:一份数据用本地时间,另一份用 UTC,合并时没转换。解决:合并前统一tz_convert('UTC'),并在宽表里保留一列原始时区标记,方便回溯。
4.2 热负荷累积量直接重采样,总量翻倍
现象:热负荷日总量比实际供热报表高出近一倍。原因:累积热量是单调递增序列,直接resample('1D').mean()得到的是日均值而不是日增量。解决:先diff()再resample('1D').sum(),差分后的负值用clip(lower=0)截断。
4.3 非供暖季补零导致模型学到假低谷
现象:模型在夏季预测热负荷时输出接近零,但实际有生活热水负荷。原因:非供暖季热负荷数据缺失,填充时统一补零,模型把“夏季=零热负荷”当成了规律。解决:非供暖季缺失用同期历史均值填充,或者单独建一个生活热水负荷列,不要和供暖负荷混在一起。
4.4 电负荷尖峰被均值抹掉,峰值预测偏低
现象:模型预测的电负荷峰值始终比实际低 10% 到 15%。原因:15 分钟电负荷降采样到 1 小时时用mean,把短时尖峰平均掉了。解决:降采样时同时保留max列,或者用resample('1h').agg(['mean', 'max'])生成两列,让模型自己选。
4.5 缺失标记列被当成数值特征,模型学到错误关联
现象:模型把heat_is_missing列当成连续特征,学出“缺失越多热负荷越高”的荒谬关系。原因:标记列是 0/1 二值,但没做类别声明,树模型可能按数值大小分裂。解决:标记列用category类型,或者在特征工程阶段明确告诉模型这是二值标志,不要参与连续缩放。
5. 宽表质量验证与进阶用法:让数据自己说话
宽表建好后,别急着扔进模型。我习惯先做三件事:画电热负荷的散点图和相关系数矩阵,看两者是否存在合理的耦合关系;检查供暖季和非供暖季的热负荷分布是否有明显分层;用简单的线性回归跑一个基线,看 R² 是否为正。如果 R² 为负,说明数据里还有没处理干净的错位或异常。
import matplotlib.pyplot as plt import seaborn as sns # 电热负荷散点图,按供暖季着色 plt.figure(figsize=(8, 6)) sns.scatterplot(data=wide_table, x='elec_kw_mean', y='heat_kwh_sum', hue='is_heating_season', alpha=0.5) plt.xlabel('电负荷 (kW)') plt.ylabel('热负荷 (kWh)') plt.title('电热负荷耦合关系') plt.savefig('elec_heat_scatter.png', dpi=150)如果散点图显示供暖季热负荷和电负荷有明显的正相关,非供暖季热负荷集中在低值区,说明数据对齐基本正确。如果散点图是一团乱麻,回去检查时间对齐和缺失填充。
进阶用法上,我一般会在宽表基础上加滑动窗口统计特征,比如过去 24 小时电负荷均值和热负荷均值,以及它们的比值。这个比值在供暖季稳定,在过渡季波动大,是一个很好的工况指示器。另外,热负荷的累积量差分后如果出现长时间恒定值,说明仪表可能卡死,需要单独标记。
# 滑动窗口特征 wide_table['elec_roll24_mean'] = wide_table['elec_kw_mean'].rolling(24, min_periods=12).mean() wide_table['heat_roll24_mean'] = wide_table['heat_kwh_sum'].rolling(24, min_periods=12).mean() wide_table['elec_heat_ratio'] = wide_table['elec_roll24_mean'] / (wide_table['heat_roll24_mean'] + 1e-6) # 热负荷恒定值检测 heat_diff_abs = wide_table['heat_kwh_sum'].diff().abs() wide_table['heat_stuck'] = (heat_diff_abs < 1e-3).rolling(6).sum() >= 6min_periods=12是为了避免窗口前段全空导致特征全 NaN,1e-6是防止除零。heat_stuck列标记连续 6 小时热负荷变化小于 0.001 的情况,这种点大概率是仪表故障,训练时应该剔除或降权。
最后说一个我自己的习惯:每次合并完宽表,我都会随机抽三天,把电负荷和热负荷的原始曲线和宽表曲线画在一起,肉眼比对。这个动作花不了十分钟,但能抓住大部分对齐错误。数据清洗没有后悔药,只有笨办法。希望帮到你。
本文还有配套的精品资源,点击获取