Statistical Measures(统计度量)¶
统计度量用单个数字汇总数据,捕捉数据的分散性、位置、形状和关联性。本节涵盖方差、标准差、四分位数、偏度、峰度、协方差、相关性和 z-score——这是 ML 探索性数据分析和特征工程的工具集。
-
在上一节我们将矩作为一类汇总统计量来介绍。本节将深入探讨由此衍生的实用工具:离散度、位置、形状和关联性的度量。
-
离散度(dispersion)回答这个问题:数据的分散程度如何?两个班级可能有相同的平均考试成绩,但分散程度截然不同。
-
窄(蓝色)分布的方差低:大多数值紧密聚集在均值附近。宽(红色)分布的方差高:值更分散。
-
方差(variance)是与均值的平均平方距离。平方是为了避免正负偏差相互抵消。
- 处理样本(而非整体总体)时,用 \(N - 1\) 而非 \(N\) 做分母。这一修正(称为 Bessel 修正)考虑了样本往往低估真实变异性这一事实:
-
标准差(standard deviation)是方差的平方根:\(\sigma = \sqrt{\sigma^2}\),将度量单位还原为原始单位。若数据单位为厘米,则方差单位为 cm\(^2\),但标准差单位回到 cm。
-
平均绝对偏差(MAD, Mean Absolute Deviation)是更简单的替代方案。不平方,而是对每个偏差取绝对值:
-
MAD 对异常值比方差更鲁棒,因为它不通过平方放大大偏差。但方差在数学上更方便(在证明和 ML 优化中分解性质更好)。
-
位置(position)回答另一个问题:某个特定值相对于其余数据处于什么位置?
-
四分位数(quartiles)将排序后的数据分为四等份。Q1(第 25 百分位)是 25% 的数据低于它的值。Q2 是中位数(第 50 百分位)。Q3 是第 75 百分位。
-
四分位距(IQR, Interquartile Range)为 \(Q3 - Q1\),捕捉数据中间 50% 的分散程度,忽略极端值。
-
箱线图是统计学中最有用的可视化之一。箱体从 Q1 延伸到 Q3,内部线为中位数,须线延伸到最远的非异常值,须线之外的点是异常值。
-
百分位数(percentiles)是四分位数的推广。第 \(p\) 百分位是 \(p\%\) 的观测值低于它的值。Q1 是第 25 百分位,中位数是第 50 百分位,Q3 是第 75 百分位。
-
z-score 告诉你某个值距均值有多少个标准差:
-
z-score 为 2 表示该值比均值高 2 个标准差。z-score 为 \(-1.5\) 表示低 1.5 个标准差。这也称为标准化(standardisation),在 ML 特征缩放中大量使用,它将任意分布变换为均值 0、标准差 1 的分布。
-
形状(shape)描述分布在中心和分散程度之外的几何特性。
-
偏度(skewness)(上一节的标准化第 3 阶矩)度量不对称性。正态曲线等完全对称分布的偏度为零。正偏度意味着右尾更长(如收入分布)。负偏度意味着左尾更长(如退休年龄分布)。
- 峰度(kurtosis)(标准化第 4 阶矩)度量尾部厚重程度。正态分布的峰度为 3。尾部更重(更易出现异常值)的分布峰度大于 3。
- 相关性(correlation)度量两个变量之间关系的强度和方向,回答:当一个变量增大时,另一个是倾向于增大、减小还是无关?
- Pearson 相关系数(\(r\))度量线性关联。范围从 \(-1\)(完全负相关)经 \(0\)(无关)到 \(+1\)(完全正相关)。
-
回顾第 1 章的点积(dot product),Pearson 相关系数本质上是 \(\mathbf{x}\) 和 \(\mathbf{y}\) 去中心化版本之间的余弦相似度。
-
Spearman 相关系数(\(\rho\))度量单调关联。它先对数据排名,再对排名计算 Pearson 相关系数。这使其对异常值更鲁棒,即使关系非线性,只要单调递增或递减即可有效。
-
几何平均数(geometric mean)适用于乘法组合的量,如增长率。若投资依次增长 10%、20%、30%,平均增长因子不是这些比率的算术平均,而是:
-
对于增长率,先将百分比转化为因子(1.10、1.20、1.30),计算几何平均数,再减去 1。
-
指数移动平均(EMA, Exponential Moving Average)给近期观测值更高的权重。不同于简单移动平均中窗口内所有点权重相等,EMA 按指数衰减:
-
平滑因子 \(\alpha\)(取值 0 到 1 之间)控制旧观测值失去影响的速度。\(\alpha\) 越大,对近期变化响应越灵敏;\(\alpha\) 越小,曲线越平滑。在 ML 中,EMA 用于 Adam 等优化器以及 batch normalization 的运行统计中。
-
异常值检测(outlier detection)识别距其余数据异常远的数据点。两种常用方法:
- IQR 法:数据点落在 \(Q1 - 1.5 \times \text{IQR}\) 以下或 \(Q3 + 1.5 \times \text{IQR}\) 以上时为异常值
- z-score 法:\(|z| > 3\) 时为异常值(距均值超过 3 个标准差)
-
IQR 法更鲁棒,因为它不假设正态分布。z-score 法在数据近似正态时效果好,但分布严重偏斜时可能失效。
编程练习(使用 CoLab 或 notebook)¶
-
计算一个数据集的方差、标准差和 MAD 并进行比较。观察添加一个极端异常值后发生的变化。
import jax.numpy as jnp data = jnp.array([4, 8, 6, 5, 3, 7, 9, 5, 6, 7], dtype=jnp.float32) mean = jnp.mean(data) variance = jnp.var(data) std = jnp.std(data) mad = jnp.mean(jnp.abs(data - mean)) print("Original data:") print(f" Variance: {variance:.3f}, Std: {std:.3f}, MAD: {mad:.3f}") # 添加异常值并重新计算 data_outlier = jnp.append(data, 100.0) mean2 = jnp.mean(data_outlier) print(f"\nWith outlier (100):") print(f" Variance: {jnp.var(data_outlier):.3f}, Std: {jnp.std(data_outlier):.3f}, MAD: {jnp.mean(jnp.abs(data_outlier - mean2)):.3f}") -
计算两个变量之间的 Pearson 和 Spearman 相关系数。尝试不同的关系形式。
import jax import jax.numpy as jnp # 完全线性关系 x = jnp.array([1, 2, 3, 4, 5, 6, 7, 8], dtype=jnp.float32) y = 2 * x + 1 # 尝试修改这里! def pearson(a, b): a_c = a - jnp.mean(a) b_c = b - jnp.mean(b) return jnp.sum(a_c * b_c) / (jnp.sqrt(jnp.sum(a_c**2)) * jnp.sqrt(jnp.sum(b_c**2))) def spearman(a, b): rank_a = jnp.argsort(jnp.argsort(a)).astype(jnp.float32) rank_b = jnp.argsort(jnp.argsort(b)).astype(jnp.float32) return pearson(rank_a, rank_b) print(f"Pearson r: {pearson(x, y):.4f}") print(f"Spearman ρ: {spearman(x, y):.4f}") -
用 IQR 法和 z-score 法实现异常值检测,并在偏斜数据上比较结果。
import jax.numpy as jnp data = jnp.array([2, 3, 3, 4, 5, 5, 5, 6, 6, 7, 50], dtype=jnp.float32) # IQR 法 q1, q3 = jnp.percentile(data, 25), jnp.percentile(data, 75) iqr = q3 - q1 lower, upper = q1 - 1.5 * iqr, q3 + 1.5 * iqr iqr_outliers = data[(data < lower) | (data > upper)] print(f"IQR bounds: [{lower:.1f}, {upper:.1f}]") print(f"IQR outliers: {iqr_outliers}") # z-score 法 z_scores = (data - jnp.mean(data)) / jnp.std(data) z_outliers = data[jnp.abs(z_scores) > 3] print(f"\nZ-scores: {z_scores}") print(f"Z-score outliers (|z| > 3): {z_outliers}") -
用不同平滑因子对含噪数据计算指数移动平均(EMA)并绘图。
import jax.numpy as jnp import matplotlib.pyplot as plt # 生成含噪数据 key = __import__("jax").random.PRNGKey(0) noise = __import__("jax").random.normal(key, shape=(50,)) signal = jnp.linspace(0, 5, 50) + noise def ema(data, alpha): result = jnp.zeros_like(data) result = result.at[0].set(data[0]) for t in range(1, len(data)): result = result.at[t].set(alpha * data[t] + (1 - alpha) * result[t - 1]) return result plt.figure(figsize=(10, 4)) plt.plot(signal, "o", alpha=0.3, label="raw data", color="#999") for alpha, color in [(0.1, "#e74c3c"), (0.3, "#3498db"), (0.7, "#27ae60")]: plt.plot(ema(signal, alpha), label=f"α={alpha}", color=color, linewidth=2) plt.legend() plt.title("EMA with different smoothing factors") plt.show()