第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 知识体系总览

下面这张图是我自己整理的本章知识体系,方便大家对照学习:

特征工程与数据探索知识体系 风电功率预测特征工程 1. 特征分析:风速、风向、温度、气压 2. 相关性分析:皮尔逊 vs 斯皮尔曼 3. 特征重要性排序:树模型/Permutation 4. 时序重采样:降采样与聚合函数 5. 滑窗构建:窗口大小与步长 💡 核心原则 • 数据质量 > 模型复杂度 • 多特征协同 > 单一特征 • 时序依赖不可忽略 • 重采样要选对聚合函数

这张图把本章的五个核心模块串起来了。从特征分析开始,到相关性分析、特征重要性排序,再到重采样和滑窗构建,每一步都是为后续建模打基础。我个人建议,在实际项目中按照这个顺序来操作,不容易遗漏关键步骤。


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