第四章:探索性数据分析(EDA)——让数据开口说话
拿到风电数据后,别急着建模。我个人的习惯是,先花至少一半的时间做探索性分析。为什么?因为数据不会骗人,但它会「藏东西」。你想想看,如果数据里有异常点,模型学到的就是错误的规律,那预测结果能准吗?
这一章,我们就来聊聊怎么让数据「开口说话」。核心就四件事:看分布、找关联、画曲线、揪异常。
4.1 数据分布可视化——先摸清数据的「脾气」
数据分布,说白了就是每个特征值出现的频率。比如风速,是集中在3-8m/s,还是经常飙到20m/s以上?这直接决定了你的模型要应对什么工况。
核心要点:用直方图 + 核密度估计(KDE)看单变量分布,用箱线图看离群点。
我在项目中遇到过一件事:某风场的历史数据里,风速超过25m/s的样本特别多。一开始我以为数据没问题,后来一查,原来是传感器在强风时出现了「卡滞」现象,重复记录了同一个值。嗯,这就是分布分析的价值——帮你发现数据质量问题。
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 加载数据
df = pd.read_csv('wind_farm_data.csv')
# 设置画布
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 风速分布
sns.histplot(df['wind_speed'], bins=50, kde=True, ax=axes[0,0])
axes[0,0].set_title('风速分布(含KDE)')
# 功率分布
sns.histplot(df['active_power'], bins=50, kde=True, ax=axes[0,1])
axes[0,1].set_title('有功功率分布(含KDE)')
# 风向分布(极坐标更直观)
sns.histplot(df['wind_direction'], bins=36, ax=axes[1,0])
axes[1,0].set_title('风向分布(36扇区)')
# 温度分布
sns.boxplot(x=df['temperature'], ax=axes[1,1])
axes[1,1].set_title('温度箱线图')
plt.tight_layout()
plt.show()
小技巧:风速分布通常呈威布尔分布,如果看到双峰或多峰,大概率是数据混合了不同季节或不同机型的记录。
4.2 相关性分析(热力图)——找到「谁跟谁是一伙的」
相关性分析,就是看两个变量之间有没有「默契」。比如风速和功率,理论上应该是正相关——风速越大,功率越高。但实际数据里,这种关系可能被其他因素干扰,比如空气密度、桨距角等。
我建议用皮尔逊相关系数,取值范围-1到1。绝对值越接近1,相关性越强。正数表示同向变化,负数表示反向变化。
# 计算相关系数矩阵
corr_matrix = df[['wind_speed', 'active_power', 'wind_direction',
'temperature', 'pressure', 'humidity']].corr()
# 绘制热力图
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm',
fmt='.2f', linewidths=0.5)
plt.title('特征相关性热力图')
plt.show()
| 特征对 | 相关系数 | 解读 |
|---|---|---|
| 风速 vs 功率 | 0.85~0.95 | 强正相关,核心关系 |
| 温度 vs 功率 | -0.2~0.1 | 弱负相关,空气密度影响 |
| 风向 vs 功率 | 0.0~0.3 | 弱相关,受地形影响大 |
| 湿度 vs 功率 | -0.1~0.0 | 几乎无关 |
注意:相关系数只衡量线性关系。风速和功率在切入风速以下和切出风速以上是非线性的,所以热力图只能作为参考,不能替代后面的风速-功率曲线分析。
4.3 风速-功率曲线绘制——风电分析的「灵魂」
风速-功率曲线,是所有风电预测模型的基石。它描述了风机在不同风速下的理论发电能力。但实际数据画出来的曲线,往往跟厂家提供的标准曲线有偏差。
为什么会这样?原因很多:叶片结冰、偏航误差、尾流效应、电网限电……我见过最夸张的一次,某风场因为长期处于限电状态,功率曲线整体下移了15%。如果不做EDA直接建模,模型学到的就是「限电模式」,而不是真实的风能转换能力。
# 绘制风速-功率散点图 + 拟合曲线
plt.figure(figsize=(12, 6))
# 散点图(采样10%避免过密)
sample_df = df.sample(frac=0.1, random_state=42)
plt.scatter(sample_df['wind_speed'], sample_df['active_power'],
alpha=0.3, s=1, c='steelblue')
# 分箱均值曲线(更稳健)
bin_edges = np.arange(0, 30, 0.5)
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
df['speed_bin'] = pd.cut(df['wind_speed'], bins=bin_edges)
bin_means = df.groupby('speed_bin')['active_power'].mean()
plt.plot(bin_centers, bin_means.values, 'r-', linewidth=2,
label='分箱均值曲线')
plt.xlabel('风速 (m/s)')
plt.ylabel('有功功率 (kW)')
plt.title('风速-功率曲线(含分箱均值)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
关键判断标准:
- 切入风速附近(通常3-4m/s):曲线应快速上升
- 额定风速附近(通常10-12m/s):曲线趋于平缓
- 切出风速附近(通常20-25m/s):功率应降为0
- 如果曲线出现「平台」或「断层」,大概率是数据质量问题
4.4 异常模式识别——揪出「害群之马」
异常模式,就是那些不符合物理规律的数据点。比如风速10m/s但功率为0,或者风速3m/s但功率达到额定值。这些数据如果不处理,会严重拉低模型精度。
我曾经接手一个项目,模型预测误差一直降不下来。后来做EDA时发现,有大约5%的样本在风速15m/s以上时功率反而下降了。排查后发现,是某台风机在强风时频繁启停,数据记录出现了时间错位。嗯,这就是典型的「数据对齐」问题。
常用的异常检测方法:
- 基于物理规则:功率不能超过额定值,风速不能为负
- 基于统计方法:Z-score、IQR(四分位距)
- 基于模型方法:孤立森林、LOF(局部异常因子)
# 基于IQR的异常检测
def detect_outliers_iqr(data, column, factor=1.5):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - factor * IQR
upper_bound = Q3 + factor * IQR
outliers = data[(data[column] < lower_bound) |
(data[column] > upper_bound)]
return outliers
# 检测功率异常
power_outliers = detect_outliers_iqr(df, 'active_power', factor=3)
print(f'检测到 {len(power_outliers)} 个功率异常点')
# 可视化异常点
plt.figure(figsize=(12, 6))
plt.scatter(df['wind_speed'], df['active_power'],
alpha=0.3, s=1, c='gray', label='正常数据')
plt.scatter(power_outliers['wind_speed'], power_outliers['active_power'],
alpha=0.8, s=10, c='red', label='异常点')
plt.xlabel('风速 (m/s)')
plt.ylabel('有功功率 (kW)')
plt.title('异常模式识别:基于IQR的功率异常点')
plt.legend()
plt.show()
避坑指南:我曾经直接用3倍IQR过滤所有异常点,结果把正常的高风速满发数据也删掉了。后来学乖了——先按风速分箱,再在每个箱内做异常检测。这样既保留了物理规律,又剔除了真正的异常。
知识体系总览
下面这张图,帮你把本章的核心逻辑串起来。EDA不是孤立的一步,而是贯穿整个建模流程的「体检」环节。
做完这四步,你对数据的理解应该已经超过80%的人了。接下来要做的,就是根据EDA的发现,决定哪些特征要保留、哪些异常要剔除、哪些关系需要做非线性变换。记住,EDA不是一次性工作——每次特征工程迭代后,都应该回头再做一次验证。
本章小结:
- 数据分布可视化帮你发现数据「长什么样」
- 相关性分析帮你找到特征间的「亲疏关系」
- 风速-功率曲线是风电分析的「灵魂画作」
- 异常模式识别帮你揪出「害群之马」
公众号:蓝海资料掘金营,微信deep3321