4. 单变量特征分析:描述性统计、分布可视化与偏度峰度
单变量分析是特征工程的起点,其核心目标是通过统计量与图形快速评估单个特征的数据质量、分布形态与异常倾向。本节将系统讲解描述性统计指标、直方图与箱线图的解读方法,以及偏度与峰度的计算与业务含义。
4.1 描述性统计:集中趋势与离散程度
描述性统计用一组简洁的数值概括特征的全貌,通常分为集中趋势与离散程度两类指标。
| 类别 | 指标 | 定义 | 适用场景 |
|---|---|---|---|
| 集中趋势 | 均值(Mean) | 所有观测值的算术平均 | 对称分布、无极端值 |
| 中位数(Median) | 排序后位于中间位置的值 | 偏态分布、含异常值 | |
| 众数(Mode) | 出现频率最高的值 | 分类特征、离散型数值 | |
| 离散程度 | 标准差(Std) | 数据偏离均值的平均程度 | 衡量波动性 |
| 四分位距(IQR) | Q3 - Q1,中间50%数据的跨度 | 稳健离散度量,不受异常值影响 | |
| 极差(Range) | 最大值 - 最小值 | 快速了解数据跨度 |
关键判断规则: 当均值与中位数差异超过标准差的 0.1 倍时,应警惕分布偏斜或异常值干扰。
4.2 分布可视化:直方图与箱线图
图形比数字更直观地揭示分布形态。直方图展示数据的整体形状(对称、左偏、右偏、双峰),箱线图则聚焦于异常值与分位数结构。
4.2.1 直方图解读要点
- 箱宽(bin width):过小导致噪声,过大掩盖细节;常用 Freedman-Diaconis 规则:
bin_width = 2 * IQR * n^(-1/3)。 - 分布形态:正态分布呈钟形;右偏(正偏)时尾部在右侧;左偏(负偏)时尾部在左侧。
- 多峰:暗示数据可能来自不同子群体,需进一步分层分析。
4.2.2 箱线图异常值判定
箱线图使用四分位距(IQR)定义异常值边界:
下边界 = Q1 - 1.5 * IQR
上边界 = Q3 + 1.5 * IQR
温和异常值:落在 [下边界, Q1) 或 (Q3, 上边界] 区间
极端异常值:超出上下边界
实践建议: 对于业务特征,不要直接删除异常值。先标记异常值对应的样本,结合业务逻辑判断是数据录入错误、真实极端事件还是传感器故障。
4.3 偏度与峰度计算
偏度(Skewness)与峰度(Kurtosis)是量化分布形态的两个高阶矩统计量,在特征工程中用于判断正态性偏离程度与尾部风险。
4.3.1 偏度(Skewness)
偏度衡量分布不对称性的方向与程度:
偏度公式:γ₁ = (1/n) * Σ[(xᵢ - μ)³] / σ³
判断标准:
γ₁ = 0 → 对称分布(如正态分布)
γ₁ > 0 → 右偏(正偏),尾部在右侧,均值 > 中位数
γ₁ < 0 → 左偏(负偏),尾部在左侧,均值 < 中位数
经验阈值:
|γ₁| < 0.5 → 近似对称
0.5 ≤ |γ₁| < 1 → 中等偏态
|γ₁| ≥ 1 → 高度偏态,需考虑变换
4.3.2 峰度(Kurtosis)
峰度衡量分布尾部厚度与峰部尖锐度,通常使用超额峰度(Excess Kurtosis):
峰度公式:γ₂ = (1/n) * Σ[(xᵢ - μ)⁴] / σ⁴ - 3
判断标准:
γ₂ = 0 → 正态分布(Mesokurtic)
γ₂ > 0 → 尖峰厚尾(Leptokurtic),异常值风险高
γ₂ < 0 → 平峰薄尾(Platykurtic),分布更均匀
业务含义:
金融特征中,峰度 > 3 常预示极端波动风险
传感器特征中,峰度 > 5 可能指示噪声或故障
常见误区: 峰度高 ≠ 分布更“尖”。峰度主要由尾部极端值贡献,而非中心峰值高度。两个分布中心高度相同,但尾部更厚的分布峰度更高。
4.4 单变量分析流程总结
以下流程将描述性统计、可视化与偏度峰度整合为可复用的分析管线:
- 快速扫描:计算均值、中位数、标准差、IQR,检查均值与中位数差异。
- 分布可视化:绘制直方图(观察整体形状)与箱线图(标记异常值)。
- 量化偏斜:计算偏度,若 |γ₁| ≥ 1 则记录为“需变换特征”。
- 评估尾部风险:计算超额峰度,若 γ₂ > 2 则标记为“厚尾特征”。
- 决策记录:将分析结果写入特征元数据表,供后续特征变换(如对数变换、Box-Cox)参考。
4.5 实战代码示例
以下 Python 代码演示单变量分析的完整流程,使用 pandas、scipy.stats 与 matplotlib:
import pandas as pd
import numpy as np
from scipy.stats import skew, kurtosis
import matplotlib.pyplot as plt
def univariate_analysis(df, feature):
"""
对指定特征执行单变量分析,返回统计指标与诊断建议。
"""
data = df[feature].dropna()
n = len(data)
mean = np.mean(data)
median = np.median(data)
std = np.std(data, ddof=1)
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
skew_val = skew(data)
kurt_val = kurtosis(data, fisher=True) # 超额峰度
# 诊断规则
mean_median_diff = abs(mean - median) / std
skew_flag = "高度偏态" if abs(skew_val) >= 1 else ("中等偏态" if abs(skew_val) >= 0.5 else "近似对称")
kurt_flag = "尖峰厚尾" if kurt_val > 0 else ("平峰薄尾" if kurt_val < 0 else "正态峰度")
# 异常值计数
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outlier_count = np.sum((data < lower_bound) | (data > upper_bound))
result = {
"特征": feature,
"样本量": n,
"均值": round(mean, 4),
"中位数": round(median, 4),
"标准差": round(std, 4),
"偏度": round(skew_val, 4),
"偏度诊断": skew_flag,
"超额峰度": round(kurt_val, 4),
"峰度诊断": kurt_flag,
"异常值个数": outlier_count,
"异常值占比(%)": round(outlier_count / n * 100, 2)
}
return result
# 使用示例
# result = univariate_analysis(df, "age")
# print(result)
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].hist(df["age"], bins=30, color="#3498db", edgecolor="#2980b9")
axes[0].set_title("直方图")
axes[1].boxplot(df["age"].dropna(), vert=True, patch_artist=True,
boxprops=dict(facecolor="#1abc9c", color="#16a085"))
axes[1].set_title("箱线图")
plt.tight_layout()
plt.show()
输出解读: 若偏度诊断结果为“高度偏态”且峰度诊断结果为“尖峰厚尾”,则该特征不适合直接输入线性模型,建议优先尝试对数变换或 Box-Cox 变换。