4、探索性数据分析(EDA):描述性统计、可视化分析、相关性分析

拿到一份空气质量数据,别急着往上套模型。我个人的习惯是,先跟数据「聊聊天」。

什么叫聊天?就是做探索性数据分析,简称 EDA。说白了,就是看看数据长什么样,有没有脏数据,分布是否合理,变量之间有没有暧昧关系。这一步做扎实了,后面的建模才能心里有底。

4.1 描述性统计:先摸清数据的底细

描述性统计是最基础的一步。我一般会先看这几个指标:均值、中位数、标准差、最小值、最大值,还有四分位数。

举个例子,PM2.5 的浓度。如果均值是 80,中位数是 60,说明什么?说明数据右偏,有几天爆表了,把平均值拉上去了。我在项目里遇到过这种情况,一看数据,好家伙,某天 PM2.5 飙到 500+,明显是沙尘暴或者燃放烟花爆竹导致的。

核心指标速查表:
指标含义关注点
均值平均水平易受极端值影响
中位数中间位置的值更能代表「典型」情况
标准差波动程度越大说明数据越不稳定
偏度分布对称性正偏表示右尾长

代码实现也很简单。用 pandas 的 describe() 方法,一行搞定:

import pandas as pd

df = pd.read_csv('air_quality.csv')
print(df.describe())

输出结果里,你会看到每个数值列的统计量。嗯,这里要注意:如果某个特征的 count 明显少于其他列,说明有缺失值,得先处理。

4.2 可视化分析:一图胜千言

光看数字不够直观。我习惯画三张图:箱线图、直方图、时间序列图。这三张图能帮你快速定位问题。

4.2.1 箱线图:揪出异常值

箱线图能直观展示数据的分布和异常点。箱体代表四分位距,中间的线是中位数,两边的须是正常范围,超出须的点就是异常值。

我曾经在一个项目中,用箱线图一眼就发现了问题:某个监测站的 PM10 数据,箱线图的上须之外密密麻麻全是点。后来一查,是那个站点的传感器坏了,连续几天输出异常值。

import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(10, 6))
sns.boxplot(data=df[['PM2.5', 'PM10', 'NO2', 'SO2']])
plt.title('空气质量指标箱线图')
plt.show()
小技巧:如果异常值太多,别急着删除。先看看是不是季节性因素导致的。比如北方冬季供暖期,PM2.5 普遍偏高,这不叫异常,这叫规律。

4.2.2 直方图:看分布形态

直方图告诉你数据是正态分布、偏态分布还是双峰分布。这对后续选择算法很重要。比如,如果 PM2.5 的分布严重右偏,做线性回归前最好做对数变换。

你想想看,如果数据是双峰分布,说明什么?说明可能存在两种不同的状态。比如工作日和周末的交通排放不同,或者白天和夜间的工业活动不同。

plt.figure(figsize=(10, 6))
df['PM2.5'].hist(bins=50, edgecolor='black')
plt.title('PM2.5 浓度分布直方图')
plt.xlabel('浓度 (μg/m³)')
plt.ylabel('频数')
plt.show()

4.2.3 时间序列图:看趋势和周期

空气质量数据是典型的时间序列。画一条时间轴上的折线图,能看出长期趋势、季节性波动和突发异常。

我记得有一次,时间序列图上突然出现一个尖峰,持续了三天。一开始以为是数据错误,后来查新闻,原来是附近发生了森林火灾。这个发现直接帮我们修正了异常检测的阈值。

plt.figure(figsize=(14, 6))
plt.plot(df['date'], df['PM2.5'], color='steelblue', linewidth=0.8)
plt.title('PM2.5 时间序列图')
plt.xlabel('日期')
plt.ylabel('浓度 (μg/m³)')
plt.xticks(rotation=45)
plt.show()
避坑指南:画时间序列图时,一定要确保日期列是 datetime 类型,并且按时间排序。我曾经因为没排序,画出来的图乱七八糟,排查了半天才发现是数据顺序乱了。

4.3 相关性分析:变量之间的「暧昧关系」

相关性分析回答一个问题:哪些变量之间有关系?关系有多强?

最常用的是皮尔逊相关系数,取值范围在 -1 到 1 之间。绝对值越接近 1,相关性越强。正相关表示同增同减,负相关表示此消彼长。

corr_matrix = df[['PM2.5', 'PM10', 'NO2', 'SO2', 'CO', 'O3']].corr()
print(corr_matrix)

输出是一个矩阵,对角线全是 1。我一般重点关注非对角线的值。比如 PM2.5 和 PM10 的相关系数通常很高,0.8 以上,因为它们来源相似。而 O3 和 NO2 往往是负相关,因为臭氧在夜间会被 NO 消耗。

为了更直观,我习惯画热力图:

plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('空气质量指标相关性热力图')
plt.show()
关键提醒:相关性不等于因果性。两个变量相关,不代表一个导致另一个。比如冰淇淋销量和溺水人数正相关,但真正的原因是夏天到了。在空气质量分析中,也要警惕这种「伪相关」。

做完相关性分析,你就能筛选出冗余特征。比如 PM2.5 和 PM10 高度相关,建模时可以考虑只保留一个,或者做 PCA 降维。这能减少模型复杂度,还能避免多重共线性问题。

好了,EDA 这一步走完,你对数据就有了全局认知。接下来就可以放心地做数据预处理和建模了。记住,EDA 不是走过场,它是你与数据之间的第一次深度对话。聊得越透,后面的路越顺。