4. 数据预处理与特征工程基础
食品加工设备的能耗数据通常来自电表、PLC、变频器和温度/压力传感器,采样间隔从1秒到15分钟不等。原始数据几乎不可能直接送入预测模型:通讯中断会留下空洞,传感器漂移和启停冲击会产生尖峰,不同设备的时间戳还可能错位。本节围绕一条真实的数据处理链展开——从缺失值处理、异常检测、平滑滤波,到最终提取时域与频域特征,形成可供模型使用的特征矩阵。
缺失值:先判断机制,再选择策略
缺失并非同一种现象。以一台工业烤箱为例,若因网关断电导致连续2小时无记录,属于整段缺失;若某温度通道偶发丢包,属于随机点缺失;若传感器在高温段饱和后不再输出,则属于系统性缺失。三者处理方式完全不同。
实操中建议先做缺失模式统计:按设备、按通道计算缺失率与连续缺失长度。对于连续缺失长度小于3个采样点的随机缺失,线性插值或前向填充即可;对于整段缺失,不应强行插值,而应将该时间段标记为无效区间,并在特征中增加“缺失时长”指示变量,让模型知道这段数据不可信。对于能耗这类具有日周期性的序列,可用“同设备、同星期、同时刻”的历史中位数填补,比简单均值更合理。
import pandas as pd
import numpy as np
# df: 索引为时间戳,列为各能耗/工况通道
# 1) 标记连续缺失段长度
def missing_run_length(s):
grp = (s.notna() != s.notna().shift()).cumsum()
return s.isna().groupby(grp).transform('sum')
df['oven_power_missing_len'] = missing_run_length(df['oven_power'])
# 2) 短缺口线性插值,长缺口保留并打标
short_gap = df['oven_power_missing_len'] <= 3
df.loc[short_gap, 'oven_power'] = df.loc[short_gap, 'oven_power'].interpolate()
# 3) 日周期中位数填补(示例:按15分钟粒度)
df['slot'] = df.index.hour * 4 + df.index.minute // 15
slot_median = df.groupby(['device_id', 'slot'])['oven_power'].transform('median')
df['oven_power'] = df['oven_power'].fillna(slot_median)
异常检测:区分尖峰、阶跃与漂移
能耗序列的异常主要有三类:尖峰(单点突变,如启停冲击)、阶跃(电平永久跳变,如传感器量程切换)、漂移(缓慢偏移,如电表老化)。单一方法难以覆盖全部,工程上常用组合策略。
对尖峰,采用基于滑动窗口的稳健Z分数:以中位数和MAD(中位数绝对偏差)代替均值和标准差,阈值取3.5,可显著降低异常点对统计量的污染。对阶跃,可用滑动窗口均值之差检测;对漂移,则需结合同型号设备的横向对比或定期校准记录。检测到异常后不要直接删除,而应标记为异常并置为缺失,再回到上一步的缺失处理流程,这样能保持时间序列的连续性。
平滑滤波:保留趋势,抑制噪声
平滑的目标不是“让曲线好看”,而是保留对能耗预测有用的趋势与周期成分。常用方法有三类:移动平均适合快速去除高频噪声,但会引入滞后;Savitzky-Golay滤波在平滑的同时保留峰值形状,适合保留启停过程的形态特征;指数平滑则对近期数据赋予更高权重,适合在线场景。
以15分钟采样的冷库压缩机功率为例,若关注日周期趋势,窗口取4~8个点(1~2小时)较合适;若关注启停瞬态,窗口应缩小到2~3个点,或改用Savitzky-Golay(窗口7、多项式阶数2)。平滑后的序列与原序列的差值本身也是一种有用特征,可反映瞬时波动强度。
时域与频域特征:把序列压缩成可建模的向量
时域特征直接刻画统计规律:均值、标准差、最大值、最小值、峰峰值、偏度、峰度、过零率、自相关系数等。对能耗预测而言,滑动窗口内的均值与标准差往往是最有效的两个特征;峰度和过零率则有助于区分“平稳运行”与“频繁启停”两种工况。
频域特征通过FFT或功率谱密度提取。食品设备能耗具有明显的日周期(24小时)和周周期(7天),因此低频段的能量占比、主频位置、谱熵是常用特征。计算时需注意:先去除均值(去趋势),再加窗(如Hann窗)以减少频谱泄漏;采样间隔必须均匀,否则应先重采样。
from scipy.signal import savgol_filter
from scipy.stats import skew, kurtosis
# 时域特征(滑动窗口,窗口=8个采样点)
win = 8
df['power_mean'] = df['oven_power'].rolling(win).mean()
df['power_std'] = df['oven_power'].rolling(win).std()
df['power_ptp'] = df['oven_power'].rolling(win).max() - df['oven_power'].rolling(win).min()
df['power_skew'] = df['oven_power'].rolling(win).apply(skew, raw=True)
# 平滑(Savitzky-Golay,窗口7,2阶)
df['power_sg'] = savgol_filter(df['oven_power'].interpolate(), 7, 2)
# 频域特征:对去均值后的窗口做FFT,取低频能量占比
seg = df['oven_power'].interpolate().values[:1024]
seg = seg - seg.mean()
spec = np.abs(np.fft.rfft(seg * np.hanning(len(seg))))
freqs = np.fft.rfftfreq(len(seg), d=15*60) # 15分钟采样
low_band = (freqs > 0) & (freqs <= 1/86400) # 日周期以下
low_ratio = spec[low_band].sum() / spec[1:].sum()
下图概括了本节的数据处理链:原始多通道数据经过缺失判定与异常标记后,分流为“填补/剔除”和“平滑”两条路径,最终在特征层汇合为时域与频域特征向量,供能耗预测模型使用。
处理顺序与验证要点
上述步骤的顺序会影响结果:应先做异常检测再插值,否则异常值会污染插值结果;应先插值再平滑,否则平滑窗口内混入缺失会引入偏差;频域特征应在平滑之后、但保留原始序列用于时域波动特征。验证时,可对比处理前后序列的均值漂移、标准差变化和日周期自相关强度,若平滑后日周期自相关明显下降,说明窗口过长,需缩短。
最终交付给模型的特征表应包含:各通道的滑动均值、标准差、峰峰值、偏度,平滑后序列的低频能量占比与主频,以及缺失时长、异常计数等质量指示列。这些列共同构成能耗预测模型的特征输入,也为后续模型解释提供依据。