光谷激光1h工厂负荷数据:从清洗到储能容量配置的完整实战 简介这份资源聚焦光谷激光工厂的真实用电负荷以1小时为采样粒度面向从事能源管理、负荷预测与工业数据分析的工程师及学生。数据覆盖电压、电流、需量、电量、功率、温度及冻结表码等维度可用于负荷趋势建模、能效对比、节能策略制定与设备异常预警等场景。压缩包共23个文件约2.02MB包含9个xlsx数据表、6个csv数据文件、7个m脚本和1个docx说明文档表格承载原始与预处理数据脚本涵盖SVM回归、ELM电力预测及数据清洗流程文档则记录预测效果。已有127人学习下载。读者可据此复现从数据清洗、特征工程到负荷预测建模的完整链路并借助现成脚本快速验证SVM与ELM等算法效果为工厂能耗分析与调度优化提供可直接参考的实践素材。1. 光谷激光负荷数据1h 粒度工厂用电曲线到底能拿来干什么如果你正在做工业负荷预测、需量管理或者储能容量配置大概率绕不开一个问题公开数据集里全是居民用电和气象数据真正带工厂标签、带产线节奏的 1h 负荷曲线少得可怜。这份「光谷激光_实际工厂负荷数据_时间尺度为1h」就是冲着这个缺口来的——它记录的是某激光加工类工厂的真实用电负荷采样间隔固定为 1 小时属于典型的工业时序数据。适合三类人做短期负荷预测算法验证的、做峰谷电价套利测算的、以及给微电网或储能系统做容量仿真的。它不能直接告诉你变压器该选多大但能让你把模型跑在真实工业波形上而不是拿居民曲线硬套。下面我按「数据长什么样 → 怎么读进来 → 怎么清洗 → 怎么建模 → 坑在哪」的顺序拆一遍。2. 先看懂 1h 工业负荷曲线的物理含义别拿居民用电的直觉套工厂2.1 1h 采样到底丢了什么、留住了什么时间尺度为 1h意味着一天只有 24 个点。这个粒度对居民负荷够用因为居民用电的尖峰通常持续几十分钟到一两小时1h 平均后还能看出早晚高峰。但工厂不一样激光切割机、焊接机器人、空压机这类设备启停可能在几分钟内完成1h 平均会把瞬时冲击抹平。所以你拿到这份数据后第一件事是明确它能回答什么问题、不能回答什么问题。能回答的日负荷率、峰谷差、班次规律、周内生产节奏、月度用电趋势、需量申报策略。不能回答的设备级冲击电流、无功波动、谐波分析、秒级功率爬坡。常见做法是把它当作「电量结算级」数据来用而不是「电能质量级」数据。我一般会先算三个统计量来判断这条曲线值不值得深挖import pandas as pd # 假设数据为两列timestamp小时级和 load_kw有功功率单位kW df pd.read_csv(factory_load_1h.csv, parse_dates[timestamp]) df df.sort_values(timestamp).set_index(timestamp) # 日负荷率日均负荷 / 日最大负荷反映生产平稳程度 daily df[load_kw].resample(D) load_rate (daily.mean() / daily.max()).round(3) # 峰谷差率(最大 - 最小) / 最大反映波动幅度 peak_valley ((daily.max() - daily.min()) / daily.max()).round(3) # 周内规律按星期几聚合看周末是否停产 weekday_profile df[load_kw].groupby(df.index.dayofweek).mean().round(1) print(load_rate.head(10)) print(peak_valley.head(10)) print(weekday_profile)逻辑说明resample(D)把小时数据聚合成天mean()/max()得到日负荷率。参数上load_kw列名要按你实际文件的列名改如果原始数据是电量kWh而非功率kW1h 粒度下数值等价不用换算。dayofweek返回 0 到 6对应周一到周日看周末均值是否明显掉下来就能判断这家工厂是连续生产还是单班制。2.2 工业负荷的「班次指纹」怎么识别居民负荷的指纹是早晚双峰工厂负荷的指纹是「跟班次走」。单班制工厂通常 8 点到 17 点高位、夜间接近待机功率两班制会拖到 22 点三班制则全天高位但仍有换班时的浅谷。识别班次指纹不需要复杂模型画一张「小时 × 星期」的热力图就够了。import seaborn as sns import matplotlib.pyplot as plt # 构造 小时 × 星期 的透视表 df[hour] df.index.hour df[dow] df.index.dayofweek pivot df.pivot_table(valuesload_kw, indexdow, columnshour, aggfuncmean) sns.heatmap(pivot, cmapYlOrRd, annotFalse) plt.xlabel(Hour of Day) plt.ylabel(Day of Week (0Mon)) plt.title(Hourly Load Heatmap) plt.show()逻辑说明pivot_table对每个「星期几 小时」组合求均值抹掉单日异常。参数上aggfunc可以换成median如果数据里有明显尖刺中位数比均值更稳。热力图上如果出现「工作日整片深色、周末整片浅色」说明是单班或双班制如果周末仍有中等深度可能是连续生产或存在保安、照明等基础负荷。提示别急着把周末数据当异常删掉。很多工厂周末仍有空压机、冷冻机在跑这部分基础负荷对储能容量配置很关键。3. 把数据读进管线的完整操作从 CSV 到可建模的 DataFrame3.1 时间戳对齐与缺失值处理工业现场采集的数据时间戳很少是干净的。常见问题有三种时区不统一、夏令时跳变、采集器掉线导致整段缺失。1h 粒度下缺失通常表现为某几个小时直接没有行而不是空值。所以第一步不是fillna而是reindex补全时间轴。# 生成完整的小时时间轴从数据最小时间到最大时间 full_range pd.date_range( startdf.index.min(), enddf.index.max(), freqh, tzdf.index.tz # 保持原时区避免时区错位 ) # 重新索引缺失的小时会被标为 NaN df_full df.reindex(full_range) df_full.index.name timestamp # 统计缺失比例 missing_ratio df_full[load_kw].isna().mean() print(f缺失比例: {missing_ratio:.2%}) # 短缺口3h用线性插值长缺口标记出来单独判断 df_full[load_kw] df_full[load_kw].interpolate( methodlinear, limit3, limit_directionboth ) df_full[is_long_gap] df_full[load_kw].isna()逻辑说明reindex是补全时间轴的关键它不会凭空造数据而是把缺失位置显式标成 NaN让你能统计缺口大小。interpolate的limit3表示最多连续插 3 个小时超过 3 小时的缺口不插值因为工业负荷停 4 小时以上往往是停产或检修线性插值会造出假的生产曲线。limit_directionboth让首尾缺口也能被邻近值填充。参数上freqh对应 1h 粒度如果你的数据是半小时改成30min。3.2 异常值判定别用 3σ用 IQR 加分位数工业负荷的分布不是正态的用 3σ 会把正常的生产高峰当成异常。我一般用 IQR四分位距配合业务分位数来筛。Q1 df_full[load_kw].quantile(0.25) Q3 df_full[load_kw].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR # 标记异常但不直接删除 df_full[is_outlier] ( (df_full[load_kw] lower_bound) | (df_full[load_kw] upper_bound) ) # 结合业务判断超过 P99 且持续小于 2 小时的才视为可疑尖刺 p99 df_full[load_kw].quantile(0.99) df_full[is_spike] ( (df_full[load_kw] p99) (df_full[load_kw].rolling(2, min_periods1).count() 2) ) print(df_full[df_full[is_outlier]].head(20))逻辑说明IQR 法对偏态分布更稳健1.5 * IQR是经典阈值工业场景可以放宽到2.0甚至3.0取决于你想保留多少真实高峰。rolling(2).count()用来判断尖刺是否孤立如果连续多个小时都超 P99那可能是真实的生产高峰而非采集错误。参数上quantile(0.99)可以按数据量调整数据少于 1000 条时用0.95更合适。注意异常值处理完一定要留一列标记不要直接覆盖原始值。后面建模时可以把标记列作为特征让模型自己学「哪些点不可信」。4. 基于 1h 负荷的建模与验证从基线到特征工程4.1 三个基线模型先跑通再谈复杂算法很多人一上来就上 LSTM、Transformer结果连「昨天同时刻负荷」这个基线都打不过。1h 工业负荷的强周期性决定了简单基线往往出奇地稳。我一般先跑三个基线模型做法适用场景持久化模型用昨天同一小时的值作为预测生产节奏稳定的连续工厂周同期模型用上周同一天同一小时的值有明显周规律的工厂移动平均过去 24 小时均值波动平缓、无强班次特征# 持久化基线t 时刻预测值 t-24 时刻实际值 df_full[pred_persist] df_full[load_kw].shift(24) # 周同期基线t 时刻预测值 t-168 时刻实际值7*24 df_full[pred_weekly] df_full[load_kw].shift(168) # 移动平均基线 df_full[pred_ma24] df_full[load_kw].shift(1).rolling(24).mean() # 计算 MAE from sklearn.metrics import mean_absolute_error for col in [pred_persist, pred_weekly, pred_ma24]: mask df_full[col].notna() df_full[load_kw].notna() mae mean_absolute_error(df_full.loc[mask, load_kw], df_full.loc[mask, col]) print(f{col} MAE: {mae:.2f} kW)逻辑说明shift(24)和shift(168)分别对应「昨天同时刻」和「上周同时刻」这是时序预测里最常用的两个滞后特征。shift(1).rolling(24).mean()先移一位再取 24 小时均值避免用到当前时刻的真实值造成数据泄漏。参数上MAE 的单位和负荷单位一致方便直接判断误差是否可接受。如果周同期基线 MAE 明显低于持久化说明这家工厂周规律强后续特征工程要重点做「星期几」相关特征。4.2 特征工程把「班次」和「电价时段」编码进去1h 粒度下最有用的特征不是历史负荷的复杂变换而是时间标签和业务标签。我一般会构造这几类# 时间特征 df_full[hour] df_full.index.hour df_full[dow] df_full.index.dayofweek df_full[is_weekend] (df_full[dow] 5).astype(int) # 班次特征假设 8-16 为白班16-24 为中班0-8 为夜班 def shift_label(h): if 8 h 16: return 0 # 白班 elif 16 h 24: return 1 # 中班 else: return 2 # 夜班 df_full[shift] df_full[hour].apply(shift_label) # 电价时段特征假设峰段 10-12、14-19谷段 23-7其余平段 def price_period(h): if 10 h 12 or 14 h 19: return peak elif 23 h or h 7: return valley else: return flat df_full[price_period] df_full[hour].apply(price_period) # 滞后特征过去 1、2、3、24 小时 for lag in [1, 2, 3, 24]: df_full[flag_{lag}] df_full[load_kw].shift(lag) # 滚动统计过去 24 小时均值和标准差 df_full[roll_mean_24] df_full[load_kw].shift(1).rolling(24).mean() df_full[roll_std_24] df_full[load_kw].shift(1).rolling(24).std()逻辑说明shift_label和price_period是业务特征它们把「工厂怎么排班」和「电费怎么算」这两件跟负荷强相关的事编码成离散变量。参数上班次划分要按你手上工厂的实际排班改别照搬。lag_1到lag_3捕捉短时惯性lag_24捕捉日周期roll_mean_24和roll_std_24分别刻画近期水平和波动。所有滚动特征都先shift(1)确保不泄漏当前时刻信息。提示如果数据里包含节假日一定要单独加一列is_holiday。工业负荷对节假日的响应比居民负荷更剧烈漏掉这个特征会让模型在节后第一天误差飙升。5. 避坑与排查1h 工厂负荷数据最容易翻车的五个地方5.1 时区没对齐整条曲线平移几小时现象画出来的负荷曲线高峰出现在凌晨 3 点明显不符合生产常识。原因采集器用 UTC 时间戳而你以为它是本地时间读进来后没做时区转换。解决读数据时先pd.to_datetime(..., utcTrue)再用.tz_convert(Asia/Shanghai)转到本地时区最后统一用本地时间做特征。5.2 把「停产检修」当异常值删了现象清洗后数据量少了 15%模型在检修后第一天预测全错。原因连续多天的低负荷被 IQR 判为异常直接删除。解决异常标记只用于建模时的样本权重调整不要物理删除。检修段可以单独标记为is_maintenance让模型学到「检修后负荷会爬坡」这个模式。5.3 用未来数据做滚动统计现象离线评估 MAE 很低上线后误差翻倍。原因rolling(24).mean()没有shift(1)当前时刻的负荷被算进了特征。解决所有滚动和滞后特征统一先shift(1)并在代码里加断言检查特征列与目标列的时序关系。5.4 忽略需量申报的 15 分钟窗口现象按 1h 平均负荷算的需量申报值实际结算时被罚。原因需量通常按 15 分钟滑动窗口的最大值结算1h 平均会低估尖峰。解决如果手头只有 1h 数据用「1h 最大值 × 1.1~1.2 系数」做保守估计或者向现场要 15 分钟原始数据。5.5 训练集和测试集随机划分现象模型在测试集上表现很好但预测下个月完全不能用。原因时序数据随机划分会让未来信息泄漏到训练集。解决严格按时间切分比如前 80% 时间做训练、后 20% 做测试中间留一段 gap 避免边界泄漏。6. 进阶技巧用 1h 数据反推储能容量与需量策略数据拿到手最终要落到决策上。我一般会做两件事一是用峰谷电价算储能套利空间二是用负荷持续曲线定需量申报值。# 假设峰段电价 1.2 元/kWh谷段 0.4 元/kWh储能效率 0.9 peak_price 1.2 valley_price 0.4 efficiency 0.9 # 按天计算峰段用电量和谷段用电量 daily df_full.groupby(df_full.index.date) result [] for day, group in daily: peak_kwh group.loc[group[price_period] peak, load_kw].sum() valley_kwh group.loc[group[price_period] valley, load_kw].sum() # 可转移电量取峰谷较小值受储能功率和容量限制 shiftable min(peak_kwh, valley_kwh * efficiency) saving shiftable * (peak_price - valley_price / efficiency) result.append({date: day, peak_kwh: peak_kwh, valley_kwh: valley_kwh, saving_yuan: saving}) saving_df pd.DataFrame(result) print(f日均套利空间: {saving_df[saving_yuan].mean():.1f} 元) print(f年化估算: {saving_df[saving_yuan].mean() * 330:.0f} 元)逻辑说明peak_kwh和valley_kwh分别是峰段和谷段的电量shiftable取两者较小值并考虑效率损耗代表实际能搬移的电量。参数上330是按年生产天数估的你要按工厂实际生产天数改。efficiency0.9是储能充放电综合效率磷酸铁锂一般取 0.85 到 0.92。负荷持续曲线用来定需量申报值把全年 1h 负荷从大到小排序取第 95 百分位对应的值作为申报需量既能覆盖大部分高峰又不会因为个别尖刺把需量抬得过高。sorted_load df_full[load_kw].sort_values(ascendingFalse).reset_index(dropTrue) declare_demand sorted_load.quantile(0.95) print(f建议需量申报值: {declare_demand:.1f} kW)逻辑说明quantile(0.95)取排序后第 95 百分位意味着允许 5% 的时间超出申报值。参数上这个分位数可以按工厂对超需罚款的敏感度调整罚款重就提到 0.98罚款轻就降到 0.90。从那以后我每次拿到新的工厂负荷数据都强制先跑一遍「时区检查 → 缺失统计 → 班次热力图 → 三个基线」这四步确认数据没被自己读歪再动模型。希望帮到你。本文还有配套的精品资源点击获取