数据预处理实战:缺失值处理、异常值检测与归一化

说实话,做负荷预测这么多年,我踩过最大的坑就是——数据没洗干净就直接建模。你想想看,模型再牛,喂进去的是垃圾数据,出来的结果能看吗?今天咱们就聊聊数据预处理这块硬骨头。我个人习惯把预处理分成三步走:先处理缺失值,再揪出异常值,最后做归一化或标准化。顺序别搞反了,不然容易出问题。

一、缺失值处理:插值法实战

实际项目中,传感器掉线、通信中断、存储故障,都会导致数据缺失。我在某园区多能互补项目里遇到过,一天内缺失了连续6个小时的光伏出力数据。怎么办?直接删掉太浪费,用均值填充又太粗糙。这时候插值法就派上用场了。

常用的插值方法有三种:

  • 线性插值:适合变化平缓的数据,比如环境温度
  • 多项式插值:适合有曲线趋势的数据,比如负荷波动
  • 样条插值:适合平滑度要求高的场景,比如光伏出力曲线

下面是我在项目中常用的代码模板:

import pandas as pd
import numpy as np
from scipy.interpolate import interp1d

# 读取数据
df = pd.read_csv('energy_data.csv', parse_dates=['timestamp'])
df.set_index('timestamp', inplace=True)

# 线性插值
df['load_linear'] = df['load'].interpolate(method='linear')

# 样条插值(适合曲线数据)
df['pv_spline'] = df['pv_power'].interpolate(method='spline', order=3)

# 检查插值效果
print(f"缺失值数量(处理前): {df['load'].isna().sum()}")
print(f"缺失值数量(处理后): {df['load_linear'].isna().sum()}")
我的小技巧: 插值前先看看数据缺失的pattern。如果是随机缺失,线性插值就够了;如果是连续大段缺失,建议用样条插值,效果更自然。

二、异常值检测:3σ原则

异常值这东西,说白了就是数据里的「刺头」。我在做某工厂负荷预测时,发现凌晨3点的负荷突然飙到正常值的3倍,后来一查是设备检修时误触了传感器。这种异常值如果不处理,模型会被带偏。

3σ原则的核心逻辑:

  • 数据服从正态分布(或近似正态)
  • 均值 ± 3倍标准差以外的数据视为异常
  • 正常数据占比约99.7%

嗯,这里要注意:3σ原则对极端值敏感。如果数据本身有多个极端值,标准差会被拉大,导致漏检。我曾经遇到过这种情况,后来改用「修正的Z分数法」才搞定。

def detect_outliers_3sigma(data, column):
    mean = data[column].mean()
    std = data[column].std()
    
    lower_bound = mean - 3 * std
    upper_bound = mean + 3 * std
    
    outliers = data[(data[column] < lower_bound) | 
                    (data[column] > upper_bound)]
    
    print(f"检测到异常值: {len(outliers)} 条")
    print(f"正常范围: [{lower_bound:.2f}, {upper_bound:.2f}]")
    
    return outliers

# 使用示例
outliers = detect_outliers_3sigma(df, 'load')
df_clean = df[~df.index.isin(outliers.index)]
避坑指南: 我曾经在风电数据上直接用3σ原则,结果把正常的阵风波动也当异常删掉了。后来我加了个条件——只有连续3个点都超出范围才判定为异常。这样既保留了真实波动,又剔除了传感器故障。

三、数据归一化与标准化

为什么需要归一化?你想想看,负荷数据可能是几千千瓦,温度数据只有几十度,风速数据是个位数。这些量纲不同的数据直接喂给模型,数值大的特征会主导训练过程。说白了,就是「大数欺负小数」。

两种主流方法对比:

方法 公式 输出范围 适用场景
Min-Max归一化 (x - min) / (max - min) [0, 1] 数据有明确边界,如温度、湿度
Z-score标准化 (x - mean) / std 均值为0,标准差为1 数据有异常值,或分布未知

我个人习惯:如果后续要用神经网络,优先用Min-Max归一化;如果用SVM或线性模型,Z-score标准化更稳妥。

from sklearn.preprocessing import MinMaxScaler, StandardScaler

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

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

# 验证结果
print(f"归一化后范围: [{df['load_norm'].min():.3f}, {df['load_norm'].max():.3f}]")
print(f"标准化后均值: {df['load_std'].mean():.3f}, 标准差: {df['load_std'].std():.3f}")
关键提醒: 归一化/标准化的参数(min、max、mean、std)一定要用训练集计算,然后应用到验证集和测试集。千万别在整个数据集上统一做,否则会造成数据泄露,模型评估结果会虚高。

四、知识体系总览

下面这张图是我梳理的数据预处理核心流程,你可以把它当作操作手册来用:

数据预处理核心流程 原始数据 步骤1:缺失值处理(插值法) 步骤2:异常值检测(3σ原则) 步骤3:归一化 / 标准化 方法选择 线性插值 → 平缓数据 样条插值 → 曲线数据 3σ原则 → 正态分布 Min-Max → 神经网络 Z-score → SVM/线性 ⚠ 参数只用训练集

这张图把今天讲的三步流程串起来了。你从原始数据开始,先处理缺失值,再检测异常值,最后做归一化或标准化。每一步都有对应的算法和注意事项。我在实际项目中,光预处理这一步就要花掉整个项目30%的时间。别嫌麻烦,数据洗得越干净,模型效果越稳。

最后说一句: 预处理没有银弹。不同能源系统的数据特性差异很大——光伏数据波动大、负荷数据有周期性、风电数据有随机性。我的建议是:先可视化看看数据长什么样,再决定用哪种方法。数据不会骗人,但你的直觉可能会。

专注资料整理