3. 特征工程实战:风速、风向、湍流强度、温度、气压等特征构造与选择

特征工程这事儿,说白了就是「喂给模型什么饭吃」。我见过太多团队,模型结构搞得花里胡哨,结果特征没处理好,预测精度就是上不去。嗯,咱们今天就把风电功率预测里最核心的几个特征,一个一个掰开揉碎了讲。

核心观点:特征工程决定了模型性能的上限,算法只是在逼近这个上限。你想想看,如果输入的都是垃圾特征,再牛的Transformer也白搭。

3.1 风速特征:不只是瞬时值那么简单

风速是功率预测里最重要的特征,没有之一。但我个人习惯,从来不会只用瞬时风速。为什么?因为风机的桨距角调节、惯性响应都有延迟,瞬时风速和功率之间并不是简单的函数关系。

我在项目中遇到过这样的情况:某次台风过境,瞬时风速达到了25m/s,但风机已经切出停机了。如果你只看瞬时风速,模型会误以为还能发很多电,结果预测值直接飞上天。所以,我们需要构造更丰富的风速特征。

3.1.1 滑动窗口统计特征

我建议至少构造以下滑动窗口特征:

  • 过去10分钟平均风速:反映近期风能资源水平
  • 过去30分钟风速标准差:衡量风速波动剧烈程度
  • 过去60分钟风速最大值/最小值:捕捉极端事件
  • 风速变化率(1分钟差分):反映风速变化趋势
# 这是我常用的风速特征构造代码
import pandas as pd
import numpy as np

def build_wind_features(df, col='wind_speed'):
    df = df.copy()
    # 滑动窗口均值
    df['ws_mean_10min'] = df[col].rolling(window=10, min_periods=1).mean()
    df['ws_mean_30min'] = df[col].rolling(window=30, min_periods=1).mean()
    # 滑动窗口标准差
    df['ws_std_10min'] = df[col].rolling(window=10, min_periods=1).std()
    # 风速变化率
    df['ws_diff'] = df[col].diff()
    # 过去1小时最大值
    df['ws_max_60min'] = df[col].rolling(window=60, min_periods=1).max()
    return df

小技巧:窗口大小的选择要结合数据采样频率。如果数据是1分钟一条,10分钟窗口就是10个点。如果是10分钟一条,那10分钟窗口就只有1个点,意义不大。我一般会先看数据的时间粒度再定。

3.2 风向特征:别直接扔给模型

风向是角度数据,0°和360°其实是一个方向。如果你直接把角度值扔给模型,模型会认为0°和360°差得很远,这显然不对。我曾经见过有人用原始风向训练模型,结果预测结果在0°附近出现奇怪的跳变。

3.2.1 风向的正弦/余弦编码

正确的做法是把风向拆解成两个分量:

def encode_wind_direction(df, col='wind_direction'):
    df = df.copy()
    # 角度转弧度
    rad = np.deg2rad(df[col])
    # 正弦和余弦分量
    df['wd_sin'] = np.sin(rad)
    df['wd_cos'] = np.cos(rad)
    return df

这样处理后,0°和360°在正弦余弦空间里是同一个点,模型就不会搞混了。你想想看,是不是这个道理?

3.2.2 主风向区间特征

我个人习惯还会加一个「主风向区间」的类别特征。比如把360°分成12个30°的扇区,然后做one-hot编码。为什么?因为风电场的地形、尾流效应会导致某些风向上的发电效率明显不同。

经验之谈:我在西北某风电场做过测试,加入主风向区间特征后,模型在复杂地形场景下的预测误差降低了约8%。尤其是当风向与山谷走向一致时,风速会被加速,功率明显偏高。

3.3 湍流强度:被很多人忽略的关键特征

湍流强度(Turbulence Intensity, TI)是风速标准差与平均风速的比值。说白了,它衡量的是风的「暴躁程度」。同样的平均风速,湍流强度高的时候,风机承受的疲劳载荷更大,发电效率也会下降。

我记得有一次,某风场连续几天预测偏差都很大。排查后发现,那几天正好是冷锋过境,湍流强度异常高。模型没考虑这个特征,自然就预测不准了。

3.3.1 湍流强度计算公式

def calc_turbulence_intensity(df, col='wind_speed', window=10):
    df = df.copy()
    # 滑动窗口内的标准差和均值
    std = df[col].rolling(window=window, min_periods=1).std()
    mean = df[col].rolling(window=window, min_periods=1).mean()
    # 湍流强度 = 标准差 / 均值,避免除零
    df['ti'] = np.where(mean > 0, std / mean, 0)
    return df

注意:当平均风速接近0时,湍流强度会变得非常大甚至无穷。我一般会做截断处理,把TI限制在0到1之间。超过1的按1处理,因为实际物理意义也不大了。

3.4 温度与气压特征:密度修正的关键

空气密度直接影响风能的大小。功率公式 P = 0.5 * ρ * A * v³ 里,ρ就是空气密度。而温度和气压是计算空气密度的关键输入。

3.4.1 空气密度计算

我建议直接构造空气密度特征,而不是把温度和气压分开扔给模型。因为模型要自己去学理想气体状态方程,这有点强人所难了。

def calc_air_density(temp_c, pressure_hpa):
    # 温度从摄氏度转开尔文
    temp_k = temp_c + 273.15
    # 气压从hPa转Pa
    pressure_pa = pressure_hpa * 100
    # 理想气体状态方程,R=287.058 J/(kg·K)
    rho = pressure_pa / (287.058 * temp_k)
    return rho

3.4.2 温度梯度特征

我个人习惯还会构造温度的时间梯度特征。为什么?因为温度变化往往预示着天气系统的变化。比如温度骤降,可能是冷锋过境,风速会增大;温度骤升,可能是热低压发展,风速也会变化。

特征名称 构造方法 物理意义
空气密度 由温度和气压计算 直接影响风能大小
温度1小时变化率 当前温度 - 1小时前温度 反映天气系统变化
气压趋势 过去3小时气压滑动平均 反映高压/低压系统移动
温度-气压交叉项 温度 × 气压(归一化后) 捕捉非线性耦合关系

3.5 特征选择:别贪多,要精

特征不是越多越好。我见过有人一口气构造了200多个特征,结果模型训练时间翻了好几倍,精度反而下降了。这就是典型的「维度灾难」。

3.5.1 我的特征选择流程

  1. 先做相关性分析:剔除与目标变量(功率)相关性低于0.1的特征
  2. 再做多重共线性检查:用VIF(方差膨胀因子)剔除高度相关的特征
  3. 最后用特征重要性排序:用随机森林或XGBoost跑一次,只看Top 20的特征

避坑指南:我曾经在某个项目里,把风速的10分钟均值和30分钟均值都保留了,结果VIF高达15以上。这两个特征高度共线,模型权重分配变得很不稳定。后来我只保留了10分钟均值,效果反而更好。

3.5.2 最终推荐的特征集

基于我的实战经验,以下特征组合在大多数风电场都能取得不错的效果:

  • 风速:瞬时值 + 10分钟均值 + 1分钟变化率
  • 风向:正弦分量 + 余弦分量 + 主风向区间(12类)
  • 湍流强度:10分钟窗口计算
  • 空气密度:由温度和气压计算
  • 时间特征:小时、月份(捕捉日变化和季节变化)

总共大约20个特征左右。这个量级,既保证了信息量,又不会让模型过拟合。

3.6 本章知识体系总览

下面这张图,是我梳理的特征工程核心逻辑。你可以看到,所有特征最终都指向一个目标:让模型更准确地理解「风能到电能」的转换过程。

风电功率预测特征工程知识体系 原始数据层 风速 | 风向 | 温度 | 气压 | 时间戳 特征构造层 风速特征 均值/标准差/变化率 风向特征 正弦/余弦/扇区 湍流强度 标准差/均值 温压特征 空气密度/梯度 特征选择层 相关性分析 → 多重共线性检查 → 特征重要性排序 最终特征集(约20个特征) 输入到功率预测模型

好了,特征工程这部分就讲到这里。记住一句话:好的特征,能让简单的模型变强;烂的特征,能让复杂的模型变傻。动手试试吧,把上面的代码跑一遍,看看你的数据里能挖出什么有意思的东西来。