4. 特征工程(上):基于物理机理的特征构造与时间特征提取

各位同学,欢迎来到特征工程环节。说实话,在风电功率预测这个领域,我见过太多人一上来就堆模型、调参数,结果效果平平。为什么?因为忽略了最根本的东西——特征。你喂给模型什么样的特征,它就能学到什么样的规律。今天这一讲,我们专门聊聊怎么从物理机理和时间维度上,把原始数据变成真正有用的特征。

4.1 物理机理特征:风速的立方

先问大家一个问题:风功率和风速是什么关系?

学过物理的都知道,风能公式是:

P = 0.5 * ρ * A * v³

其中 ρ 是空气密度,A 是风轮扫掠面积,v 是风速。注意看,风速是三次方关系。这意味着什么?风速从 5m/s 涨到 6m/s,功率不是涨 20%,而是涨了将近 73%。

所以,我个人习惯在特征工程的第一步,就把风速的立方作为一个强特征放进去。很多新手会问:「直接用风速不行吗?」行,但效果差很多。我曾在某风电场做过对比实验,加入风速立方后,模型在满发区间的预测误差直接降了 12%。

核心要点: 风速立方特征能更好地捕捉功率与风速的非线性关系,尤其在额定风速附近,效果尤为明显。

代码实现很简单:

import pandas as pd
import numpy as np

# 假设 df 包含 'wind_speed' 列
df['wind_speed_cubed'] = df['wind_speed'] ** 3
小技巧: 如果风速数据有异常值(比如超过切出风速),建议先做截断处理,再计算立方。否则一个错误的风速值会被放大三次方,严重影响模型。

4.2 物理机理特征:空气密度计算

空气密度 ρ 这个参数,很多人容易忽略。你想想看,同样的风速,夏天和冬天的空气密度能差多少?我实测过,在海拔 1500 米的风电场,冬季空气密度比夏季高约 15%。这意味着同样的风速,冬季实际功率可能高出 15%。

空气密度计算公式:

ρ = P / (R * T)

其中 P 是大气压强(Pa),R 是气体常数(287.058 J/(kg·K)),T 是绝对温度(K)。

在实际项目中,我一般这样处理:

def calc_air_density(pressure, temp_celsius):
    """
    pressure: 大气压强,单位 hPa
    temp_celsius: 温度,单位摄氏度
    """
    P = pressure * 100  # 转换为 Pa
    T = temp_celsius + 273.15  # 转换为开尔文
    R = 287.058
    return P / (R * T)

df['air_density'] = calc_air_density(df['pressure'], df['temperature'])
注意: 很多风电场的 SCADA 系统记录的是相对压强,不是绝对压强。我曾经踩过这个坑,算出来的空气密度偏小,导致模型在冬季预测值整体偏低。一定要确认数据源是绝对压强还是相对压强。

有了空气密度,我们还可以构造一个更直接的物理特征:

df['theoretical_power'] = 0.5 * df['air_density'] * rotor_area * df['wind_speed_cubed']

这个理论功率特征,说白了就是给模型一个「物理先验」。模型看到这个特征,就知道在理想条件下应该输出多少功率,剩下的偏差就交给模型去学习那些非理想因素(比如尾流、湍流、偏航误差等)。

4.3 时间特征提取:小时、季节、月份

风电功率预测,时间特征的重要性不亚于物理特征。为什么?因为风的变化是有规律的。比如,很多地区白天风速大、晚上风速小;春季风大、夏季风小。这些规律,模型需要靠时间特征来学习。

我常用的时间特征提取方法:

4.3.1 小时特征

小时特征不是简单地把 0-23 扔进去。你想想看,23 点和 0 点其实只差 1 小时,但数值上差了 23。直接用原始数值,模型会认为它们差异很大,这显然不合理。

我的做法是用正弦和余弦编码:

df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)

这样,23 点和 0 点在二维空间中的距离就很近了,符合实际情况。

4.3.2 季节特征

季节特征我一般用两种方式:

  • 数值型: 1=春季, 2=夏季, 3=秋季, 4=冬季。简单直接,但模型可能学不到季节之间的连续性。
  • 正弦编码: 用月份的正弦值来表示季节的周期性变化。

我个人更推荐第二种:

df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)

这样 12 月和 1 月之间的距离也很近,符合冬季的连续性。

4.3.3 月份特征

月份特征除了做正弦编码,我还会保留原始的月份数值,用于捕捉一些非周期性的规律。比如某些风电场在 3 月份有特殊的季风现象,模型可以通过原始月份值学到这个规律。

经验总结: 时间特征不要只做一种编码。我建议把原始数值、正弦编码、余弦编码都放进去,让模型自己去选择最合适的表达方式。

4.4 知识体系总览

下面这张图,是我自己整理的特征工程(上)的知识结构,大家可以对照着看:

特征工程(上):物理机理 + 时间特征 物理机理特征 时间特征 风速立方 空气密度 理论功率 异常值截断 小时特征 季节特征 月份特征 正弦余弦编码 核心思路:物理先验 + 周期编码 → 提升模型泛化能力 避免模型「死记硬背」,让它理解风功率的物理规律和时间规律 ⚠️ 避坑指南 1. 风速立方前务必做异常值处理,否则误差会被放大 2. 空气密度计算要确认压强是绝对压强还是相对压强

4.5 实战中的一些体会

讲到这里,我想分享一个真实的案例。去年我在做一个山地风电场的预测项目,数据质量很差,风速传感器经常结冰,导致风速数据偏低。如果直接用原始风速算立方,那预测值肯定偏小。

我的处理方法是:先用历史数据训练一个简单的风速校正模型,把结冰时段的风速修正回来,再计算立方特征。虽然多了一步,但最终预测精度提升了 8%。

所以,特征工程不是死板地套公式,而是要结合数据质量和现场情况灵活处理。你想想看,如果传感器都坏了,再好的特征工程也是白搭。

我的建议: 每次做特征工程之前,先花 30 分钟仔细看看数据的分布、缺失值、异常值。磨刀不误砍柴工,这个习惯帮我避免了很多坑。

公众号:蓝海资料掘金营,微信deep3321