3. 气象数据预处理:缺失值处理、异常值检测、时间序列重采样、空间插值方法

各位同学,咱们今天聊点实在的。

气象数据预处理,说白了就是给模型「喂饭」之前,先把食材洗干净、把烂叶子摘掉。我做了这么多年风电功率预测,见过太多因为数据没处理好,模型直接「翻车」的案例。你想想看,如果输入的数据本身就有问题,再牛的算法也白搭。

核心观点:数据预处理占整个建模工作量的60%以上。别急着调参,先把数据收拾利索了。

3.1 缺失值处理

气象站的数据,经常会有缺失。为什么?传感器坏了、通信中断、维护期间没记录……原因多了去了。我遇到过最夸张的一次,某测风塔连续三天数据全空,后来发现是供电模块烧了。

处理缺失值,常用的方法有几种:

  • 直接删除法——简单粗暴,但会丢失信息。如果缺失比例小于5%,且是随机缺失,可以这么干。
  • 均值/中位数填充——用该列的平均值或中位数补上。适合风速、温度这类波动不大的变量。
  • 前向/后向填充——用上一个或下一个有效值填充。时间序列数据常用,比如气温在短时间内变化不大。
  • 插值法——线性插值、样条插值等。我个人比较推荐,尤其是风速数据,变化有连续性。
  • 模型预测填充——用其他变量做回归预测缺失值。精度高,但计算量大。

我的经验:对于风电场的风速数据,我一般先用前向填充,再用线性插值做二次修正。曾经有一次,我用均值填充后,模型预测偏差大了将近10%,后来发现是因为那段时间正好有台风过境,均值完全不能代表实际情况。

代码示例(Python):

import pandas as pd
import numpy as np

# 读取气象数据
df = pd.read_csv('wind_data.csv', parse_dates=['time'])

# 检查缺失值
print(df.isnull().sum())

# 前向填充 + 线性插值
df['wind_speed'] = df['wind_speed'].fillna(method='ffill').interpolate(method='linear')

# 如果缺失太多,用模型预测
from sklearn.linear_model import LinearRegression
# 假设用温度和气压预测风速
train = df.dropna(subset=['wind_speed', 'temperature', 'pressure'])
model = LinearRegression()
model.fit(train[['temperature', 'pressure']], train['wind_speed'])
# 预测缺失值
missing = df[df['wind_speed'].isnull()]
df.loc[missing.index, 'wind_speed'] = model.predict(missing[['temperature', 'pressure']])

3.2 异常值检测

异常值,就是那些明显不合理的数据点。比如风速突然飙到100m/s,或者温度从20度瞬间跳到50度。嗯,这里要注意:异常值不一定是错误,也可能是真实事件(比如阵风)。

我常用的检测方法:

  1. 3σ原则——数据在均值±3倍标准差之外,视为异常。适合正态分布的数据。
  2. 箱线图法——用四分位数判断。IQR(四分位距)之外的算异常。对偏态分布更鲁棒。
  3. 领域知识法——比如风速不可能超过当地历史极值。我一般会查一下该地区50年一遇的最大风速。
  4. 时间序列异常检测——用滑动窗口计算局部均值和标准差,检测突变点。

避坑指南:我曾经犯过一个错误——把所有异常值都删掉了。结果模型在极端天气下完全失效。后来才明白,有些「异常」其实是极端工况,对风电预测恰恰是最重要的。所以,先标记,再判断,别一刀切。

代码示例:

# 箱线图法检测异常值
Q1 = df['wind_speed'].quantile(0.25)
Q3 = df['wind_speed'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记异常值
df['is_outlier'] = (df['wind_speed'] < lower_bound) | (df['wind_speed'] > upper_bound)

# 查看异常值
print(df[df['is_outlier']])

3.3 时间序列重采样

气象数据的时间分辨率五花八门。有的10分钟一条,有的1小时一条,还有的只有日平均。做预测时,必须统一时间尺度。

重采样说白了就是「变节奏」:

  • 降采样——从高频到低频。比如10分钟数据变成1小时数据。常用聚合函数:均值、最大值、最小值。
  • 升采样——从低频到高频。比如小时数据变成10分钟数据。需要插值。

我个人习惯:做短期预测(未来4小时)用10分钟或15分钟分辨率;做中期预测(未来24小时)用1小时分辨率;做长期预测(未来7天)用3小时或6小时分辨率。

为什么?你想想看,时间粒度越细,数据量越大,模型训练越慢,而且噪声也越多。反过来,粒度太粗又会丢失细节。这个平衡点,得根据实际需求来调。

# 降采样:10分钟 -> 1小时
df_hourly = df.resample('1H', on='time').agg({
    'wind_speed': 'mean',
    'wind_direction': 'mean',
    'temperature': 'mean',
    'pressure': 'mean'
})

# 升采样:1小时 -> 15分钟(线性插值)
df_15min = df_hourly.resample('15T').interpolate(method='linear')

3.4 空间插值方法

风电场往往只有几个测风塔,但我们需要知道整个场区的风资源分布。这时候就得靠空间插值了。

常用的方法:

方法 原理 适用场景
反距离加权法(IDW) 距离越近,权重越大 地形平坦、测点均匀
克里金法(Kriging) 考虑空间自相关性 地形复杂、测点稀疏
样条插值 用平滑曲面拟合 数据变化平缓
径向基函数(RBF) 用基函数组合逼近 精度要求高

我在项目中遇到过一个问题:海上风电场,测风塔只有两座,但场区面积有50平方公里。用IDW插值出来的结果,跟实际测量偏差很大。后来改用克里金法,考虑了海面粗糙度和距离海岸线的因素,效果好了不少。

小技巧:做空间插值时,别忘了考虑地形影响。山地风电场,风速随海拔变化很大。我一般会先用数字高程模型(DEM)做地形修正,再插值。

# 使用scipy进行IDW插值
from scipy.interpolate import RBFInterpolator
import numpy as np

# 假设已知测风塔坐标和风速
points = np.array([[x1, y1], [x2, y2], [x3, y3]])  # 测风塔坐标
values = np.array([v1, v2, v3])  # 对应风速

# 创建插值器
interpolator = RBFInterpolator(points, values, kernel='linear')

# 预测新位置的风速
new_points = np.array([[x_new, y_new]])
predicted_wind = interpolator(new_points)
气象数据预处理 缺失值处理 删除 / 均值填充 前向填充 / 插值 模型预测填充 异常值检测 3σ原则 / 箱线图 领域知识法 时间序列异常检测 时间序列重采样 降采样(高频→低频) 升采样(低频→高频) 聚合函数选择 空间插值方法 IDW / 克里金法 样条插值 / RBF 地形修正 预处理质量直接影响预测精度 建议:先清洗,再插值,最后统一时间尺度

好了,这一章的内容就这些。数据预处理看着琐碎,但真到了实战中,你会发现这些步骤能帮你省下大把调试模型的时间。记住一句话:垃圾进,垃圾出。把数据收拾干净了,模型自然就听话了。

专注资料整理