2. 环境变量探索性分析
拿到一份环境监测数据后,最忌讳的做法是直接套模型。PM2.5、气温、湿度这些变量各自的量纲不同、分布形态不同,彼此之间还可能存在非线性或滞后关系。探索性分析(EDA)的任务,就是在建模之前先把"数据长什么样"搞清楚:每个变量的中心位置和离散程度如何、分布是否偏斜、有没有异常值、变量两两之间是线性相关还是呈现某种曲线形态。本节围绕一张典型的环境监测表展开,说明如何用 pandas 计算描述统计、用 matplotlib 绘制分布图与散点图矩阵,并从中读出变量的基本特征与潜在关系。
一、示例数据与字段约定
假设我们有一份按小时采样的城市空气质量数据 env.csv,字段包括:pm25(PM2.5 浓度,μg/m³)、temp(气温,℃)、humidity(相对湿度,%)、wind(风速,m/s)、pressure(气压,hPa)。以下所有数值均为教学示例,用于说明分析方法,不代表任何真实站点的实测结果。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["SimHei"] # 中文显示
plt.rcParams["axes.unicode_minus"] = False
df = pd.read_csv("env.csv", parse_dates=["time"])
df = df.set_index("time").sort_index()
print(df.shape, df.dtypes)
print(df.isna().sum())
读入后先做两件事:确认时间索引已按升序排列,统计各列缺失数量。环境数据常见的问题是传感器掉线导致的整段缺失,以及 -999 之类的哨兵值混入。后者不会出现在 isna() 里,需要结合描述统计的最小值来判断。
二、描述统计:先看位置、离散与偏态
df.describe() 给出计数、均值、标准差、最小值、四分位数和最大值。但仅看这些还不够,环境变量往往右偏严重,均值会被高值拉高,此时中位数更能代表"典型水平"。因此建议同时输出偏度和峰度:
stats = df.describe().T
stats["median"] = df.median()
stats["skew"] = df.skew()
stats["kurt"] = df.kurt()
print(stats[["mean", "median", "std", "min", "max", "skew"]].round(2))
以示例数据为例,可以预期看到这样的特征:pm25 的均值明显高于中位数,偏度为正(例如偏度约 1.5),说明存在少数重污染时段把分布拉出长尾;humidity 的取值范围被限制在 0~100,分布相对集中;temp 与 pressure 近似对称。判断依据很直接:若 mean > median 且 skew > 1,就应警惕右偏,后续做相关分析时考虑对 pm25 取对数 np.log1p 再观察。另外要检查 min 是否出现 -999 或明显不合理的负值,若有则先替换为缺失再处理。
三、分布图:直方图与核密度
直方图用于观察分布形状和异常值位置。对偏态变量,把原始尺度和对数尺度并排画出来,能直观看出变换是否有效:
fig, axes = plt.subplots(1, 2, figsize=(10, 3.5))
axes[0].hist(df["pm25"].dropna(), bins=40, color="#4C9BE8", edgecolor="white")
axes[0].set_title("PM2.5 原始分布")
axes[1].hist(np.log1p(df["pm25"].dropna()), bins=40,
color="#3FB984", edgecolor="white")
axes[1].set_title("PM2.5 对数变换后")
plt.tight_layout(); plt.show()
读图时关注三点:主峰位置是否与中位数吻合;右侧长尾延伸多远(对应重污染事件);对数变换后是否更接近钟形。若变换后仍明显双峰,往往意味着数据中混入了不同季节或不同监测站点的样本,需要按分组再分别分析,而不是强行合并建模。
四、散点图矩阵:一次看完全部两两关系
变量多于三个时,逐个画散点图效率低。pandas 的 scatter_matrix 可以在一个网格中同时呈现所有变量两两之间的散点关系,对角线放各变量的分布:
from pandas.plotting import scatter_matrix
cols = ["pm25", "temp", "humidity", "wind"]
scatter_matrix(df[cols].dropna(), figsize=(9, 9),
diagonal="kde", alpha=0.3, color="#E8834C")
plt.suptitle("环境变量散点图矩阵")
plt.show()
这张矩阵图是本节信息密度最高的产出。以示例数据为例,可以读出几类典型模式:pm25 与 wind 呈明显的负向带状分布——风速增大时 PM2.5 普遍下降,符合污染物扩散的物理直觉;pm25 与 humidity 在高湿区间出现上翘,提示可能存在非线性或阈值效应;temp 与 humidity 呈较宽的负相关云团,但离散度大,说明单靠气温解释湿度并不充分。这些观察直接决定了后续回归中是否需要引入交互项或分段处理。
五、从图形到数值:相关系数的补充确认
散点图给出形态,相关系数给出强度。对线性关系用 Pearson,对单调但非线性的关系用 Spearman 更稳妥:
pear = df[cols].corr(method="pearson").round(2)
spear = df[cols].corr(method="spearman").round(2)
print(pear); print(spear)
若 pm25 与 humidity 的 Spearman 系数明显高于 Pearson 系数,说明二者更接近单调而非线性关系,这与散点图中"高湿上翘"的形态一致。此时在回归阶段应考虑对湿度做分段或引入平方项,而不是简单套用线性模型。需要提醒的是,相关系数只反映统计关联,PM2.5 与气温的相关可能被季节共同驱动,不能直接解读为因果。
六、异常值与缺失的处理顺序
探索阶段发现的问题要在进入建模前处理干净,建议按以下顺序:先用物理量程筛出不可能值(如湿度大于 100、风速为负),替换为缺失;再对偏态变量做对数变换;最后决定缺失样本是删除还是插值。插值只适合短时间断档,长时间缺失应保留为缺失,避免人为制造平滑的假相关。处理完成后重新跑一遍描述统计和散点矩阵,确认分布形态与相关结构没有因处理而被人为改变,这一步是保证后续回归结论可信的前提。