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度。嗯,这里要注意:异常值不一定是错误,也可能是真实事件(比如阵风)。
我常用的检测方法:
- 3σ原则——数据在均值±3倍标准差之外,视为异常。适合正态分布的数据。
- 箱线图法——用四分位数判断。IQR(四分位距)之外的算异常。对偏态分布更鲁棒。
- 领域知识法——比如风速不可能超过当地历史极值。我一般会查一下该地区50年一遇的最大风速。
- 时间序列异常检测——用滑动窗口计算局部均值和标准差,检测突变点。
避坑指南:我曾经犯过一个错误——把所有异常值都删掉了。结果模型在极端天气下完全失效。后来才明白,有些「异常」其实是极端工况,对风电预测恰恰是最重要的。所以,先标记,再判断,别一刀切。
代码示例:
# 箱线图法检测异常值
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)
好了,这一章的内容就这些。数据预处理看着琐碎,但真到了实战中,你会发现这些步骤能帮你省下大把调试模型的时间。记住一句话:垃圾进,垃圾出。把数据收拾干净了,模型自然就听话了。