3、数据探索性分析(EDA):数据概览与统计描述、缺失值分布可视化、异常值初步识别、相关性热力图绘制

好,咱们进入正题。数据拿到手,别急着建模。我见过太多人上来就调参,结果模型跑出来一塌糊涂。为什么?因为数据本身有问题,你没发现。

EDA 这一步,说白了就是给你的数据「做体检」。体检报告出来了,你才知道哪些地方需要动手术,哪些地方可以保守治疗。我个人习惯,EDA 至少要做四件事:看概览、查缺失、找异常、看关联。咱们一个一个来。

3.1 数据概览与统计描述

先看看数据长什么样。用 df.head() 瞄一眼前几行,再用 df.info() 看看数据类型和内存占用。嗯,这里有个小细节——df.info() 能告诉你每列有多少非空值,如果非空数跟总行数对不上,那就有缺失了。

import pandas as pd
import numpy as np

# 加载风电数据
df = pd.read_csv('wind_power_data.csv')

# 数据概览
print("数据形状:", df.shape)
print("\n前5行数据:")
print(df.head())

# 统计描述
print("\n数值列统计描述:")
print(df.describe())

# 数据类型和缺失情况
print("\n数据类型及非空计数:")
print(df.info())

我一般还会加一句 df.describe(include='all'),这样连类别列也能看到统计信息。有一次我在项目里发现风速的最大值居然有 45 m/s,当时就觉得不对劲——风机的切出风速一般也就 25 m/s,这数据肯定有问题。

关键指标解读:
  • count:非空样本数,跟总行数对比就能发现缺失
  • mean / std:均值和标准差,判断数据分布是否合理
  • min / max:极值,异常值的初步线索
  • 25% / 50% / 75%:四分位数,看数据是否偏态

3.2 缺失值分布可视化

缺失值这东西,你不能只看总数。比如一列有 1000 个缺失,但它们是集中在某段时间,还是随机分布的?处理方式完全不同。

我习惯先画一个缺失值矩阵图。用 missingno 库,一行代码就能搞定。

import missingno as msno
import matplotlib.pyplot as plt

# 缺失值矩阵图
msno.matrix(df)
plt.title('缺失值分布矩阵')
plt.show()

# 缺失值条形图
msno.bar(df)
plt.title('各列缺失数量')
plt.show()

# 缺失值相关性热力图
msno.heatmap(df)
plt.title('缺失值之间的相关性')
plt.show()

你看这个矩阵图,白色条带就是缺失的位置。如果白色条带在时间轴上有规律,比如每天同一时段缺失,那可能是传感器定时故障。我曾经遇到过风速传感器在凌晨 2-4 点频繁缺失,后来发现是那个时段的温度太低,传感器冻住了。

我的经验: 缺失率低于 5% 的列,可以直接删除行或用均值填充。缺失率在 5%-20% 之间,建议用插值法。超过 20% 的,你要认真考虑这列还有没有保留价值。

3.3 异常值初步识别

异常值识别,我一般分两步走。第一步用箱线图快速扫一遍,第二步用领域知识做二次确认。

# 箱线图识别异常值
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

cols = ['风速', '有功功率', '桨距角']
for i, col in enumerate(cols):
    axes[i].boxplot(df[col].dropna())
    axes[i].set_title(f'{col} 箱线图')
    axes[i].set_ylabel(col)

plt.tight_layout()
plt.show()

# 基于3σ原则的异常值检测
def detect_outliers_zscore(data, threshold=3):
    z_scores = np.abs((data - data.mean()) / data.std())
    return z_scores > threshold

outliers_mask = detect_outliers_zscore(df['有功功率'])
print(f"有功功率异常值数量: {outliers_mask.sum()}")
print(f"异常值占比: {outliers_mask.sum()/len(df)*100:.2f}%")

箱线图里,超出 whisker 的点就是潜在异常值。但注意,风电数据里有些「异常」其实是正常的。比如风速突然飙升到 20 m/s,功率反而降了——这可能是风机为了保护自己而变桨了。你想想看,如果直接删掉这些点,反而丢失了重要信息。

避坑指南: 我曾经一刀切删掉了所有「风速高但功率低」的点,结果模型在极端天气下预测完全失效。后来才明白,这些点代表的是风机的保护策略,必须保留。

3.4 相关性热力图绘制

相关性分析,说白了就是看哪些变量之间有关系。风速和功率肯定强相关,但桨距角和功率呢?温度和湿度呢?一张热力图全给你画出来。

# 计算相关系数矩阵
corr_matrix = df.corr()

# 绘制热力图
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, 
            annot=True,        # 显示数值
            fmt='.2f',         # 保留两位小数
            cmap='RdBu_r',     # 红蓝配色
            center=0,          # 以0为中心
            square=True,       # 正方形格子
            linewidths=0.5,    # 格子间距
            cbar_kws={'shrink': 0.8})
plt.title('风电特征相关性热力图', fontsize=16)
plt.tight_layout()
plt.show()

# 找出与目标变量高度相关的特征
target_corr = corr_matrix['有功功率'].sort_values(ascending=False)
print("\n与有功功率相关性最高的前5个特征:")
print(target_corr.head(6))  # 包含自身

你看这个热力图,颜色越深表示相关性越强。红色是正相关,蓝色是负相关。我一般关注绝对值大于 0.5 的相关系数。如果两个特征之间的相关系数超过 0.8,那就要考虑多重共线性问题了——说白了就是这两个特征提供的信息差不多,留一个就行。

EDA 阶段的核心产出:
  1. 数据质量报告:缺失率、异常率、数据类型
  2. 缺失值处理策略:删除、填充、插值
  3. 异常值处理策略:保留、修正、删除
  4. 特征筛选依据:相关性分析结果

嗯,到这里 EDA 的基本流程就走完了。你可能会问,这些步骤能不能自动化?当然可以。我后来写了个 EDA 自动化脚本,跑一遍就能生成报告。但第一次做的时候,我建议你手动一步步来,这样才能真正理解你的数据。

记住一句话:数据质量决定了模型效果的上限,算法只是逼近这个上限的手段。EDA 做扎实了,后面的特征工程和建模才能事半功倍。


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