第4章:数据采集与预处理

智能电表数据,说白了就是需求响应算法的“口粮”。

我见过不少团队,模型结构设计得漂漂亮亮,结果数据一喂进去,训练直接崩了。为什么?数据太脏。今天咱们就聊聊,怎么把这堆原始读数,变成模型能吃的干净数据。

4.1 智能电表数据采集

先说说数据从哪来。智能电表现在基本是标配了,15分钟一个采样点,一天96个点。我习惯把采集频率分成三类:

  • 高频采集:1分钟或更短,适合做负荷预测的精细模型
  • 中频采集:15分钟,这是最常见的,也是我项目里用得最多的
  • 低频采集:1小时,适合做长期趋势分析

采集的时候,有个坑你得注意——时间戳对齐。不同电表可能有时差,甚至有的电表会跳变。我遇到过一台电表,每天凌晨3点准时跳快5分钟,排查了好久才发现是固件bug。

我的小习惯:采集数据时,我会额外记录一个“采集状态码”。0表示正常,1表示通信异常,2表示电表故障。这样后期清洗时,可以直接按状态码过滤,省事很多。

4.2 数据清洗:缺失值处理

缺失值,是数据预处理里最头疼的问题。你想想看,一个96点的日负荷曲线,中间缺了3个点,模型就不知道那段时间发生了什么。

我一般按三步走:

  1. 检测缺失:直接看时间戳是否连续。不连续的地方,就是缺失。
  2. 判断缺失类型:是随机缺失,还是连续缺失一大段?
  3. 选择填充方法
缺失情况 推荐方法 我的经验
单个点缺失 线性插值 简单有效,我90%的情况都用它
连续2-5个点缺失 三次样条插值 比线性插值更平滑,适合负荷曲线
连续超过5个点缺失 用前一天同一时刻的值填充 注意:如果前一天也缺失,就取前一周的平均值
整段缺失(比如停电) 直接删除该时段 别硬填,填了反而误导模型
# 这是我常用的线性插值代码
import pandas as pd

# 假设df是DataFrame,'power'列是负荷数据
df['power_interpolated'] = df['power'].interpolate(method='linear')

# 如果连续缺失超过5个点,改用前一天数据
df['power_filled'] = df['power_interpolated'].fillna(
    df['power'].shift(96)  # 96个点对应前一天同一时刻
)
注意:千万别对所有缺失值都用同一种方法。我曾经犯过这个错,结果模型训练出来,预测值在缺失段附近全是平的,完全没反应。后来才发现,是插值把真实波动给抹平了。

4.3 数据清洗:异常值处理

异常值,比缺失值更隐蔽。电表读数突然飙到10000千瓦,你信吗?反正我不信。

我总结了几种常见的异常:

  • 物理限值异常:比如居民电表读数超过50kW,基本是错的
  • 突变异常:相邻两个点变化超过30%,可能是电表跳变
  • 持续不变异常:连续十几个点读数一模一样,电表可能卡住了

处理异常值,我推荐用3σ原则或者箱线图法。3σ适合数据分布比较正态的情况,箱线图更鲁棒。

# 3σ异常检测
mean = df['power'].mean()
std = df['power'].std()
df['is_outlier_3sigma'] = (df['power'] - mean).abs() > 3 * std

# 箱线图法(更推荐)
Q1 = df['power'].quantile(0.25)
Q3 = df['power'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['is_outlier_iqr'] = (df['power'] < lower_bound) | (df['power'] > upper_bound)
避坑指南:我曾经遇到过一个案例,某工厂的电表在每天中午12点准时出现一个尖峰,用3σ检测出来全是异常。后来去现场一看,人家中午是电炉集中加热时间,那个尖峰是真实的。所以,异常值不一定是错误值,一定要结合业务逻辑判断。

4.4 数据归一化与标准化

数据洗干净了,接下来就是让模型“看懂”数据。不同电表的量纲不一样,有的最大负荷100kW,有的1000kW。如果不做归一化,模型会天然偏向数值大的特征。

我常用的两种方法:

方法 公式 适用场景
Min-Max归一化 (x - min) / (max - min) 数据分布有明确上下界,比如负荷率0-1
Z-score标准化 (x - mean) / std 数据分布近似正态,或者有异常值
from sklearn.preprocessing import MinMaxScaler, StandardScaler

# Min-Max归一化
scaler_minmax = MinMaxScaler()
df['power_norm'] = scaler_minmax.fit_transform(df[['power']])

# Z-score标准化
scaler_std = StandardScaler()
df['power_std'] = scaler_std.fit_transform(df[['power']])

我个人习惯:如果后续要用神经网络,优先用Min-Max归一化,把数据压到[0,1]区间,模型收敛更快。如果是树模型,其实归一化影响不大,但为了统一流程,我还是会做。

一个小细节:归一化的时候,一定要用训练集的min和max去变换验证集和测试集。千万别把整个数据集一起归一化,那会造成数据泄露,模型评估结果会虚高。我见过有人犯这个错,上线后模型表现直接打对折。

4.5 本章知识体系

下面这张图,是我梳理的数据预处理全流程。你可以把它当成一个检查清单,每次做数据预处理时,对着走一遍。

数据预处理全流程 ① 数据采集 ② 缺失值处理 单个缺失 线性插值 连续缺失 前一天填充 ③ 异常值处理 3σ原则 箱线图法 ④ 归一化/标准化 Min-Max 归一化 Z-score 标准化 ⑤ 干净数据集

嗯,数据预处理这块,说白了就是“脏活累活”,但也是决定模型上限的关键一步。我见过太多项目,算法调参调了几个月,效果还不如把数据好好洗一遍来得明显。所以,别嫌麻烦,每一步都走扎实了,后面模型训练才能顺风顺水。

专注资料整理