数据预处理基础:缺失值处理、时间序列对齐、重采样与插值、数据质量控制方法

各位同学好,我是老张。在风电行业摸爬滚打了十几年,今天咱们聊聊数据预处理。

说实话,很多刚入行的朋友拿到再分析数据就急着建模。我当年也犯过这毛病。结果呢?模型跑出来一塌糊涂。后来才明白,数据预处理占整个项目60%以上的工作量。你想想看,数据质量不行,后面再牛的算法也是白搭。

这一章,我把最常用的四个预处理手段掰开揉碎讲清楚。都是实战中反复用到的,没有花架子。

一、缺失值处理:别让空值毁了你的模型

再分析数据里,缺失值太常见了。卫星信号中断、仪器故障、数据传输丢包……原因五花八门。我遇到过最夸张的一次,某风电场测风塔数据连续缺失了72小时,就是因为网络交换机烧了。

处理缺失值,我一般分三步走:

  1. 先诊断——搞清楚缺失模式
  2. 再决策——选处理方法
  3. 后验证——检查处理效果

核心原则:缺失率超过30%的变量,我建议直接放弃。强行插值反而引入噪声,得不偿失。

常用方法对比

方法 适用场景 我的经验
删除法 缺失率<5%,随机缺失 简单粗暴,但数据宝贵,慎用
均值/中位数填充 短期缺失,变量波动小 风速数据用中位数更稳,均值容易被极端值带偏
线性插值 连续缺失不超过3个点 我最常用的方法,简单有效
前向/后向填充 缓慢变化的变量(如温度) 注意:风速突变时千万别用,会出大问题
KNN插值 多维特征,缺失率10%-20% 效果好但慢,数据量大时慎用
# 我常用的缺失值处理代码
import pandas as pd
import numpy as np

# 读取再分析数据
df = pd.read_csv('era5_wind_2023.csv', parse_dates=['time'])

# 第一步:看看缺失情况
print(df.isnull().sum() / len(df) * 100)

# 第二步:风速数据用线性插值
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')

# 温度数据用前向填充(变化慢)
df['temperature'] = df['temperature'].fillna(method='ffill')

# 风向数据用中位数填充(周期性变量)
df['wind_direction'] = df['wind_direction'].fillna(df['wind_direction'].median())

我的小技巧:处理风向数据时,别直接用数值插值。0度和360度其实是一个方向。我习惯先把风向拆成sin和cos分量,分别插值,再合成回来。这样不会出现方向跳变。

二、时间序列对齐:让不同数据源说同一种语言

做风电项目,你手里往往有多个数据源:ERA5再分析数据是1小时间隔,测风塔数据是10分钟间隔,SCADA数据可能是1分钟间隔。时间戳还不一样,有的用UTC,有的用本地时间。

这就麻烦了。不对齐,没法分析。

我个人习惯的做法是:

  • 统一时间基准——全部转成UTC,避免时区混乱
  • 统一时间频率——根据业务需求选一个基准频率
  • 处理时间戳异常——重复、跳变、乱序

我曾经踩过的坑:有一次做风功率预测,ERA5数据和实测数据差了1小时没对齐。模型训练时看起来挺好,上线后预测值总是滞后。查了两天才发现是时区问题。从那以后,我所有项目第一件事就是检查时间基准。

# 时间对齐的标准流程
# 1. 统一转UTC
df['time_utc'] = pd.to_datetime(df['time']).dt.tz_convert('UTC')

# 2. 设置时间索引
df.set_index('time_utc', inplace=True)

# 3. 重采样到目标频率(比如1小时)
df_hourly = df.resample('1H').mean()

# 4. 对齐两个数据源
era5_aligned = era5.reindex(obs.index, method='nearest')

三、重采样与插值:灵活变换时间尺度

重采样说白了就是把数据从一个时间频率变到另一个。做风电的人天天用。

比如:ERA5是1小时间隔,但你要做10分钟的功率预测,就得降尺度。反过来,测风塔10分钟数据太密,想看看日变化趋势,就得升尺度。

这里有个关键点:降尺度(高频→低频)用聚合,升尺度(低频→高频)用插值

重采样方法选择

操作 方法 适用场景
降尺度 均值、中位数、最大值 风速用均值,阵风用最大值
升尺度 线性插值、三次样条 短期预测用线性,长期趋势用样条
不规则时间 最近邻、前向填充 SCADA数据经常不规则,用前向填充最稳
# 重采样实战
# 从10分钟重采样到1小时(降尺度)
df_hourly = df_10min.resample('1H').agg({
    'wind_speed': 'mean',
    'wind_gust': 'max',
    'temperature': 'mean',
    'pressure': 'last'
})

# 从1小时插值到15分钟(升尺度)
df_15min = df_hourly.resample('15T').interpolate(method='cubic')

注意:升尺度时别用太复杂的插值方法。我试过用多项式插值,结果在风速突变处出现了振荡,产生了根本不存在的风速值。线性插值虽然简单,但最可靠。

四、数据质量控制:把坏数据揪出来

数据质量控制,说白了就是「找茬」。再分析数据虽然比实测数据干净,但也不是完美的。我见过太多人直接拿数据就跑模型,结果被几个异常值带偏了。

质量控制我一般做四件事:

  1. 范围检查——风速不可能负值,气压不可能1000hPa以下
  2. 变化率检查——1秒内风速从5m/s跳到25m/s,这肯定有问题
  3. 一致性检查——风速和风向应该匹配,北风时风速大,南风时风速小?不一定,但要有物理依据
  4. 时空一致性——相邻网格点的数据应该平滑变化

我的经验:质量控制不是越严格越好。太严格会把正常数据也删掉,尤其是极端天气事件。比如台风过境时,风速变化率就是很大,这时候要放宽阈值。

# 质量控制代码示例
def quality_control(df):
    # 1. 范围检查
    df = df[(df['wind_speed'] >= 0) & (df['wind_speed'] <= 50)]
    df = df[(df['pressure'] >= 900) & (df['pressure'] <= 1100)]
    
    # 2. 变化率检查(相邻点变化不超过10m/s)
    df['speed_change'] = df['wind_speed'].diff().abs()
    df = df[df['speed_change'] <= 10]
    
    # 3. 标记可疑数据,不直接删除
    df['qc_flag'] = 0  # 0:正常, 1:可疑, 2:异常
    df.loc[df['speed_change'] > 8, 'qc_flag'] = 1
    
    return df

我曾经犯过的错:早期做项目时,我直接删除了所有标记为异常的数据。结果模型在极端天气下表现极差。后来学乖了,异常数据先标记,人工复核后再决定是删除还是修正。尤其是那些物理上可能但统计上异常的点,往往是最有价值的数据。

知识体系总览

下面这张图是我自己整理的,把数据预处理的四个核心模块串起来了。你照着这个流程走,基本不会出大问题。

数据预处理核心流程 原始再分析数据 缺失值处理 诊断→决策→验证 时间序列对齐 统一基准→统一频率 重采样与插值 降尺度/升尺度 数据质量控制 范围/变化率/一致性 清洗后的标准数据集 模型输入/分析使用 注意:四个模块可以并行处理,但建议按顺序执行

嗯,以上就是数据预处理最核心的内容。说实话,这些方法看起来简单,但真正用好需要大量实战经验。我建议你拿到数据后,先花时间做一遍完整的预处理,把每一步都记录下来。这样后面建模时心里才有底。

记住一句话:数据预处理花的时间,会在模型训练和部署阶段加倍还给你

蓝海数据掘金营,专注资料整理