3. 能源数据预处理实战

能源异常用量与漏损识别的准确率,很大程度上不取决于算法有多复杂,而取决于进入分析环节的数据是否干净、可比、可对齐。工厂里常见的原始能耗数据往往来自电表、水表、蒸汽流量计、压缩空气流量计、分项计量网关和手工抄表记录,采样周期从1分钟到1天不等,单位有kWh、MWh、m³、t、GJ,时间戳还可能存在时区、夏令时和网关本地缓存延迟问题。如果直接把这些数据送进漏损分析模型,得到的"异常"很可能只是数据缺陷造成的假象。本节围绕缺失值填补、异常值剔除、时间对齐、单位统一四个关键动作,给出可落地的清洗流程与工具实操方法。

一、先明确清洗目标与数据基线

清洗不是把所有数据"修得好看",而是让数据满足后续分析的三条基线:时间可比(同一时间粒度、同一时区)、量纲可比(统一到同一能量或体积单位)、统计可信(缺失与异常比例在可接受范围内,且处理方式有记录)。建议在清洗前先做一次数据画像:统计每个测点的记录条数、时间跨度、采样间隔分布、缺失率、零值率、负值率、最大值与最小值。例如某车间电表共43 200条15分钟数据,缺失312条(缺失率0.72%),出现连续6小时零值1段,这类信息决定了后续用哪种填补策略。

二、缺失值填补:按缺失形态选方法

缺失分三类:完全随机缺失(如网关偶发丢包)、随机缺失(如某班次未抄表)、非随机缺失(如仪表故障期间整段无数据)。处理原则是先判断缺失形态,再选方法,并保留缺失标记列。

  • 短时点缺失(1~3个采样点):优先线性插值或前向填充。对15分钟粒度的电表,连续缺失2个点用线性插值即可,误差通常小于该测点日波动的5%。
  • 整段缺失(数小时至数天):不宜简单插值,应使用同类型日负荷曲线或相似日匹配填补。例如某空压机流量计缺失8小时,可用前一周同星期、同生产班次的平均值按小时比例缩放填补,并在数据表中标注fill_method=similar_day。
  • 长时间缺失或仪表故障:不做数值填补,标记为无效区间,在漏损分析时排除该时段,避免把"无数据"误判为"零用量"。
关键细节:填补前先区分"真零"与"假零"。能源用量为0可能是停产,也可能是通信中断。可结合设备运行状态信号或相邻测点(如总表有量、分表为0)交叉判断,假零应按缺失处理。

三、异常值剔除:物理规则与统计规则并用

异常值识别建议双层过滤。第一层是物理规则:负值、超过仪表量程、超过历史最大值的1.5倍、单位时间内变化率超过物理可能(如一台110 kW空压机1小时耗电不可能超过110 kWh太多)。第二层是统计规则:对每个测点按小时或按班次计算中位数与四分位距,落在[Q1-3×IQR, Q3+3×IQR]之外的点标记为疑似异常。两层都命中的点才剔除或修正,避免把真实的用能高峰误删。

剔除后不要直接留空,应做修正或标记:单点跳变可用前后点均值替换;连续异常段按缺失处理;所有被处理点写入flag列(0正常、1插补、2剔除、3无效区间),保证全过程可追溯。

四、时间对齐与单位统一

时间对齐的核心是把不同采样周期的数据重采样到统一粒度(漏损分析常用15分钟或1小时)。降采样时,功率类(kW)取区间均值,电量类(kWh)取区间求和,流量类(m³/h)取均值后乘以小时数换算为体积,累计读数类(电表底码)取区间首尾差值。升采样(如日数据拆到小时)只在确有需要时使用,并按典型日曲线分配,且必须标注为估算值。

单位统一要建立一张换算表并固化到清洗脚本中,避免人工换算出错。常用换算:1 MWh = 1000 kWh;1 GJ = 277.78 kWh;蒸汽1 t 按焓值折算,例如0.8 MPa饱和蒸汽约2760 MJ/t,即约0.767 MWh/t(示例参数,实际应按蒸汽参数表取值);压缩空气1 m³ 按比功率折算为电耗。所有换算系数写入配置,输出统一为kWh或m³并保留原始单位列。

能源数据清洗主链路与分支处理 原始数据源 电表 / 水表 / 蒸汽 流量计 / 手工抄表 数据画像 缺失率 / 零值率 采样间隔 / 量程 缺失值填补 插值 / 相似日 无效区间标记 异常值剔除 物理规则 + IQR 修正 / flag 标记 时间对齐 重采样 15min/1h 均值 / 求和 / 差值 单位统一 kWh / m³ / GJ 换算系数固化 清洗后分析数据集 统一时间粒度 + 统一单位 + flag 追溯列 供漏损识别与异常用量分析使用 分支判断:零值是真零还是假零? 总表有量、分表为 0 → 通信中断,按缺失处理 设备停机信号为 1 → 真零,保留 无法判断 → 标记 flag=3,分析时排除 说明:flag 列取值 0=正常、1=插补、2=剔除修正、3=无效区间;所有处理动作写入清洗日志,保证可追溯。

五、工具实操:用 Python 完成一次完整清洗

实际工程中推荐用 Python(pandas + numpy)编写可复用的清洗脚本,把上述规则参数化。下面给出一个针对15分钟电表数据的示例片段,参数为教学假设值,实际使用时应按现场仪表特性调整。

import pandas as pd, numpy as np

# df: 列 = [meter_id, ts, value, unit]
df["ts"] = pd.to_datetime(df["ts"])
df = df.sort_values(["meter_id", "ts"])

# 1) 单位统一:MWh -> kWh
df.loc[df["unit"] == "MWh", "value"] *= 1000
df["unit"] = "kWh"

# 2) 时间对齐:15分钟重采样,电量类求和
g = (df.set_index("ts").groupby("meter_id")["value"]
       .resample("15min").sum().reset_index())

# 3) 假零与缺失标记
g["flag"] = 0
g.loc[g["value"].isna(), "flag"] = 3

# 4) 短时缺失线性插值(仅限连续<=2点)
g["value"] = g.groupby("meter_id")["value"].transform(
    lambda s: s.interpolate(limit=2, limit_area="inside"))
g.loc[g["value"].notna() & (g["flag"] == 3), "flag"] = 1

# 5) 物理规则 + IQR 异常剔除
def clean(s):
    q1, q3 = s.quantile(0.25), s.quantile(0.75)
    iqr = q3 - q1
    mask = (s < 0) | (s > q3 + 3*iqr) | (s < q1 - 3*iqr)
    s = s.mask(mask)
    return s.interpolate(limit=1)
g["value"] = g.groupby("meter_id")["value"].transform(clean)
g.loc[g["value"].isna(), "flag"] = 2

运行后应检查三项结果:每个测点的缺失率是否低于设定阈值(如2%)、被标记为flag=2或3的记录占比是否合理(通常不超过5%)、重采样后每个时间窗是否恰好一条记录。若某测点异常比例过高,说明仪表可能故障或量程配置错误,应回到数据画像环节排查,而不是继续加大剔除力度。

六、清洗流程的固化与复盘

建议把清洗流程固化为"配置 + 脚本 + 日志"三件套:配置文件中存放各测点的采样周期、单位、量程、换算系数和阈值;脚本按配置执行;日志记录每次运行的输入条数、缺失数、插补数、剔除数、输出条数。这样当漏损分析出现可疑结论时,可以快速回溯是数据问题还是真实漏损。一个实用的判断经验是:如果某测点清洗后用量曲线在夜间仍持续偏高且flag正常,才更可能是真实漏损;如果偏高时段恰好对应大量插补记录,则应优先怀疑数据质量。

常见误区:用全局均值填补整段缺失、用3σ规则在强季节性负荷上直接剔除、把累计读数当瞬时值重采样求和。这三类错误都会系统性放大或掩盖漏损信号,务必在清洗阶段规避。

完成本节清洗后,数据应达到"时间对齐、单位统一、缺失与异常有标记、处理过程可追溯"的状态,才具备进入后续异常用量识别与漏损定位分析的条件。