news 2026/10/10 10:49:23

电热负荷数据合并实战:从时序对齐到宽表构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
电热负荷数据合并实战:从时序对齐到宽表构建

简介:这份资源面向电力系统、能源管理与负荷预测方向的研究人员和学生,提供完整的电负荷与热负荷时间序列数据,可用于时间序列分析、机器学习建模以及气候因素对负荷影响的研究。压缩包共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() >= 6

min_periods=12是为了避免窗口前段全空导致特征全 NaN,1e-6是防止除零。heat_stuck列标记连续 6 小时热负荷变化小于 0.001 的情况,这种点大概率是仪表故障,训练时应该剔除或降权。

最后说一个我自己的习惯:每次合并完宽表,我都会随机抽三天,把电负荷和热负荷的原始曲线和宽表曲线画在一起,肉眼比对。这个动作花不了十分钟,但能抓住大部分对齐错误。数据清洗没有后悔药,只有笨办法。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 10:49:03

Windows打印机添加失败的四大原因与精准解决路径

1. 为什么“添加打印机”这件事&#xff0c;十年来始终是Windows用户最常卡住的环节你有没有过这样的经历&#xff1a;新买一台激光打印机&#xff0c;盒子刚拆开&#xff0c;说明书翻到第三页就停住了——“请访问官网下载驱动”&#xff0c;点开网页&#xff0c;满屏的“Driv…

作者头像 李华
网站建设 2026/10/10 10:48:19

Matlab目标规划实战:从偏差变量到分层序列法

这套数学建模Matlab算法系列的教程&#xff0c;我在草稿箱里存了二十章的稿子&#xff0c;一直没想好怎么把目标规划这一章讲得不那么“教材腔”。原因很简单&#xff0c;线性规划在建模题里已经被用得烂熟&#xff0c;可一旦遇到“既要利润高&#xff0c;又要加班少&#xff0…

作者头像 李华
网站建设 2026/10/10 10:48:18

多智能体编排实战:从单Agent困境到agency-agents框架落地

这两年做AI应用&#xff0c;最大的感触就是&#xff1a;单Agent是玩具&#xff0c;多Agent才是工程。可一旦把多个Agent真正放到一起跑&#xff0c;你很快会发现&#xff0c;真正难的不是模型能力&#xff0c;而是怎么把它们组织起来、让它们协作不互相踩脚、出了问题还能快速定…

作者头像 李华
网站建设 2026/10/10 10:47:11

Java基础入门教程:从JVM原理到面向对象与异常处理实战

1. 内容整体设计与思路拆解1.1 为什么这篇教程要这样写敲下第一个System.out.println("Hello World")的时候&#xff0c;你有没有想过一个问题&#xff1a;为什么 Java 语法看起来这么繁琐&#xff1f;一个 for 循环、一个类定义都比 Python 多写好几行&#xff0c;为…

作者头像 李华
网站建设 2026/10/10 10:46:47

CodeX源码解读:排查本地代理报错与接入DeepSeek配置指南

CodeX发布之后&#xff0c;热度一直没降过。我最早是在一个周五下午被同事拉去救火&#xff0c;说他终端里的CodeX突然报了一串刺眼的英文&#xff1a;cc switch local proxy failed while handling codex endpoint /responses。我盯着这行报错看了半天&#xff0c;第一反应是“…

作者头像 李华
网站建设 2026/10/10 10:46:38

基于OpenCV与dlib的人脸录入识别系统全流程解析

简介&#xff1a;一套基于 Python 与 OpenCV 的人脸录入与识别开源项目&#xff0c;借助 dlib 机器学习库实现人脸检测、特征提取与比对&#xff0c;并设计了 tkinter 图形界面&#xff0c;方便录入人脸及中英文姓名信息。适合正在学习计算机视觉、人脸识别或 dlib 应用的开发者…

作者头像 李华