第3章 特征工程与数据探索:风速、风向、温度、气压等特征分析
各位好,我是老张。今天咱们聊聊特征工程和数据探索。说实话,很多刚入行的朋友喜欢一上来就调模型参数,结果效果总是不理想。我个人的习惯是,花70%的时间在数据理解和特征构建上,剩下的30%才给模型。为什么?因为数据质量决定了模型的天花板,算法只是逼近这个天花板的手段。
3.1 风电特征的基础认知
风电功率预测,说白了就是搞清楚「风」和「电」之间的关系。但风这个东西,它不是一个简单的变量,而是由多个因素共同决定的。
我遇到过不少项目,团队只拿了风速一个特征就去建模型,结果预测误差大得离谱。你想想看,同样的风速,夏天和冬天能一样吗?白天和晚上能一样吗?
常见的风电特征包括:
- 风速:最核心的特征,但要注意是瞬时风速还是平均风速
- 风向:决定了风电机组是否处于最佳迎风角度
- 温度:影响空气密度,进而影响风能转化效率
- 气压:同样影响空气密度,高气压下空气更「稠密」
- 湿度:湿度大时空气密度降低,功率会下降
核心观点:不要只盯着风速看。风速是「油门」,但风向、温度、气压这些是「路况」。路况不好,油门踩到底也跑不快。
3.2 相关性分析:皮尔逊 vs 斯皮尔曼
拿到数据后,第一件事就是看特征和目标变量(功率)之间的关系。这里我常用两种方法:皮尔逊相关系数和斯皮尔曼秩相关系数。
3.2.1 皮尔逊相关系数
皮尔逊衡量的是线性关系。它的值在-1到1之间,绝对值越大表示线性相关性越强。
import pandas as pd
import numpy as np
# 假设df是包含风速、风向、温度、气压、功率的数据框
corr_matrix = df.corr(method='pearson')
print(corr_matrix['power'].sort_values(ascending=False))
我记得有一次,一个同事拿着皮尔逊结果来找我,说「风速和功率的相关系数只有0.6,是不是数据有问题?」我一看,原来是数据里包含了大量低风速时段(风速低于切入风速3m/s),这时候功率本来就是0,自然拉低了相关性。所以,做相关性分析前,一定要先做数据筛选。
3.2.2 斯皮尔曼秩相关系数
斯皮尔曼不要求线性关系,它看的是单调性。说白了,就是风速增大时功率是否也增大,至于增大的幅度是不是线性的,它不管。
# 斯皮尔曼相关系数
corr_spearman = df.corr(method='spearman')
print(corr_spearman['power'].sort_values(ascending=False))
我的经验:风速和功率之间,皮尔逊和斯皮尔曼的值往往很接近。但风向和功率之间,斯皮尔曼通常比皮尔逊高。因为风向对功率的影响不是线性的——比如风向偏离30度时功率下降20%,偏离60度时可能下降50%,这个关系用单调性描述更准确。
3.3 特征重要性排序
相关性分析只能看两两之间的关系。但实际中,特征之间会互相影响。这时候就需要特征重要性排序了。
我常用的方法有两种:
- 基于树模型的特征重要性:比如随机森林、XGBoost,训练完后直接看feature_importances_
- Permutation Importance:打乱某个特征的值,看模型性能下降多少,下降越多说明特征越重要
from sklearn.ensemble import RandomForestRegressor
from sklearn.inspection import permutation_importance
# 训练随机森林
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 特征重要性
importance = pd.DataFrame({
'feature': X_train.columns,
'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)
# Permutation Importance
perm_importance = permutation_importance(rf, X_test, y_test, n_repeats=10)
print(perm_importance.importances_mean)
避坑指南:我曾经犯过一个错误——直接用原始特征做重要性排序,结果发现「温度」的重要性很高。后来一查,是因为温度和风速有很强的共线性(某个季节风速大且温度低)。所以,做特征重要性排序前,最好先做共线性检测。
3.4 时序数据重采样
风电数据通常是高频采样的,比如每10秒一条记录。但预测任务往往需要分钟级或小时级的数据。这时候就需要重采样了。
重采样的核心是:降采样时用什么聚合函数?
- 风速:用均值,但要注意保留最大值(用于安全预警)
- 风向:用角度均值,但角度均值不能直接算平均数(比如350度和10度的平均是0度,不是180度)
- 功率:用均值,同时保留标准差(反映波动性)
# 假设数据是10秒采样,重采样到10分钟
df_resampled = df.resample('10T').agg({
'wind_speed': 'mean',
'wind_direction': lambda x: np.arctan2(
np.mean(np.sin(np.radians(x))),
np.mean(np.cos(np.radians(x)))
),
'temperature': 'mean',
'pressure': 'mean',
'power': ['mean', 'std']
})
小技巧:风向的角度均值计算,我习惯先转成sin和cos分量,分别求均值,再用atan2转回来。这样处理后的风向才是物理上合理的。
3.5 滑窗构建:让模型看到「历史」
风电功率预测本质上是一个时序问题。当前时刻的功率,不仅和当前的风速有关,还和过去几分钟的风速变化趋势有关。滑窗就是用来捕捉这种时序依赖的。
滑窗的核心参数有两个:
- 窗口大小:看多长的历史数据?我一般用6-12个时间步(比如60-120分钟)
- 步长:滑窗移动的间隔,通常等于预测步长
def create_sliding_window(data, window_size, step=1):
"""
构建滑窗特征
data: 原始时序数据
window_size: 窗口大小(时间步数)
step: 步长
"""
X, y = [], []
for i in range(0, len(data) - window_size, step):
X.append(data[i:i+window_size])
y.append(data[i+window_size])
return np.array(X), np.array(y)
# 示例:用过去6个时间步(60分钟)预测下一个时间步
window_size = 6
X, y = create_sliding_window(df['power'].values, window_size)
嗯,这里要注意一点:滑窗构建后,样本量会大幅减少。比如1000条数据,窗口大小6,步长1,最后只有994个样本。如果步长设得太大,样本量会更少。我一般会根据数据量来权衡——数据量大时步长可以设大一些,数据量小时步长设小一些。
3.6 知识体系总览
下面这张图是我自己整理的本章知识体系,方便大家对照学习:
这张图把本章的五个核心模块串起来了。从特征分析开始,到相关性分析、特征重要性排序,再到重采样和滑窗构建,每一步都是为后续建模打基础。我个人建议,在实际项目中按照这个顺序来操作,不容易遗漏关键步骤。
公众号:蓝海资料掘金营,微信deep3321