# 统计量

*统计量用单个数值概括数据，捕捉其离散程度、位置、形状和关联。本节涵盖方差、标准差、四分位数、偏度、峰度、协方差、相关和 z 分数——这是探索性数据分析和机器学习特征工程的基础工具集。*

- 在上一节中，我们介绍了矩作为一组概括性统计量家族。在此，我们展开讨论从矩中衍生出的实用工具：度量离散程度、位置、形状和关联的统计量。

- **离散程度**回答了这样一个问题：数据的分布有多分散？两个班级的平均考试成绩可能相同，但其分散程度却可能大相径庭。

![均值相同但离散程度不同的两个分布](../images/variance_spread.svg)

- 窄（蓝色）分布的方差较小：大部分数值紧密聚集在均值周围。宽（红色）分布的方差较大：数值散布得更远。

- **方差**是距均值距离的平方的平均值。取平方是为了避免正负偏差相互抵消。

$$\sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2$$

- 当处理样本（而非整个总体）时，我们用 $N - 1$ 而不是 $N$ 来除。这种修正（称为**贝塞尔校正**）是因为样本往往会低估真实的变异性：

$$s^2 = \frac{1}{N-1} \sum_{i=1}^{N} (x_i - \bar{x})^2$$

- **标准差**是方差的平方根：$\sigma = \sqrt{\sigma^2}$。它将度量单位恢复为原始单位。如果数据的单位是厘米，方差的单位是 cm$^2$，而标准差的单位又回到了 cm。

- **平均绝对偏差（MAD）**是一个更简单的替代方案。它不取平方，而是取每个偏差的绝对值：

$$\text{MAD} = \frac{1}{N} \sum_{i=1}^{N} |x_i - \mu|$$

- MAD 对方差而言对异常值更稳健，因为它不会通过平方来放大大的偏差。然而，方差在数学上更便利（在证明和机器学习优化中更容易分解）。

- **位置**回答了一个不同的问题：特定数值相对于其余数据的位置在哪里？

- **四分位数**将排序后的数据分成四个相等的部分。Q1（第 25 百分位数）是低于该值的数据占 25% 的值。Q2 是中位数（第 50 百分位数）。Q3 是第 75 百分位数。

- **四分位距（IQR）**是 $Q3 - Q1$。它捕捉了中间 50% 数据的离散程度，排除了极端值。

![显示 Q1、中位数、Q3、IQR、须线和异常值的箱线图](../images/quartiles_boxplot.svg)

- **箱线图**是统计学中最有用的可视化工具之一。箱体从 Q1 延伸到 Q3，中间的线为中位数，须线延伸到最远的非异常值，而须线之外的点则为异常值。

- **百分位数**是四分位数的推广。第 $p$ 百分位数是低于该值的观测值占 $p\%$ 的值。Q1 是第 25 百分位数，中位数是第 50 百分位数，Q3 是第 75 百分位数。

- **z 分数**告诉你一个值距均值有多少个标准差：

$$z = \frac{x - \mu}{\sigma}$$

- z 分数为 2 表示该值高于均值 2 个标准差。z 分数为 $-1.5$ 表示低于均值 1.5 个标准差。这也称为**标准化**，在机器学习中广泛用于特征缩放，因为它将任何分布变换为均值为 0、标准差为 1。

- **形状**描述了分布超出其中心和离散程度之外的几何特征。

- **偏度**（上一节中的标准化三阶矩）衡量不对称性。像正态曲线这样完全对称的分布，其偏度为零。正偏度表示右尾较长（如收入分布）。负偏度表示左尾较长（如退休年龄分布）。

$$\text{偏度} = \frac{1}{N} \sum_{i=1}^{N} \left(\frac{x_i - \mu}{\sigma}\right)^3$$

- **峰度**（标准化四阶矩）衡量尾部厚度。正态分布的峰度为 3。尾部更厚（更容易出现异常值）的分布的峰度大于 3。

$$\text{峰度} = \frac{1}{N} \sum_{i=1}^{N} \left(\frac{x_i - \mu}{\sigma}\right)^4$$

- **相关**衡量两个变量之间关系的强度和方向。它回答了：当一个变量上升时，另一个变量倾向于上升、下降，还是基本不变？

![三个散点图，分别显示正相关、无相关和负相关](../images/correlation_scatter.svg)

- **皮尔森相关**（$r$）衡量*线性*关联。其取值范围从 $-1$（完全负相关）到 $0$（无相关）再到 $+1$（完全正相关）。

$$r = \frac{\sum_{i=1}^{N} (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2} \cdot \sqrt{\sum (y_i - \bar{y})^2}}$$

- 如果你还记得第 1 章中的点积，皮尔森相关本质上就是 $\mathbf{x}$ 和 $\mathbf{y}$ 均值中心化之后的余弦相似度。

- **斯皮尔曼相关**（$\rho$）衡量*单调*关联。它不使用原始值，而是先对它们进行排序，然后在排序上计算皮尔森相关。这使得它对异常值稳健，并且即使关系是非线性的，只要是一致递增或递减的，也能正常工作。

- **几何平均数**是当数值相互乘除时（如增长率）合适的平均值。如果你的投资分别增长了 10%、20% 和 30%，那么平均增长因子并不是这些增长率的算术平均数。而是：

$$\bar{x}_{\text{geo}} = \left(\prod_{i=1}^{N} x_i\right)^{1/N}$$

- 具体到增长率，先将百分比转换为因子（1.10、1.20、1.30），计算几何平均数，再减去 1。

- **指数移动平均（EMA）**赋予最近观测值更高的权重。与简单移动平均中窗口内所有点权重相等不同，EMA 呈指数衰减：

$$\text{EMA}_t = \alpha \cdot x_t + (1 - \alpha) \cdot \text{EMA}_{t-1}$$

- 平滑因子 $\alpha$（介于 0 和 1 之间）控制旧观测值失去影响的速度。$\alpha$ 越大，对近期变化的响应越灵敏；$\alpha$ 越小，曲线越平滑。在机器学习中，EMA 被用于 Adam 等优化器以及批归一化的运行统计中。

- **异常值检测**识别出与其余数据异常遥远的数点。两种常用方法：
    - **IQR 法**：如果一个点低于 $Q1 - 1.5 \times \text{IQR}$ 或高于 $Q3 + 1.5 \times \text{IQR}$，则为异常值
    - **Z 分数法**：如果 $|z| > 3$（距均值超过 3 个标准差），则为异常值

- IQR 法更稳健，因为它不假设正态分布。Z 分数法在数据近似正态时效果良好，但当分布高度偏斜时可能失效。

## 编程练习（使用 CoLab 或 notebook）

1. 计算数据集的方差、标准差和 MAD，并进行比较。观察添加极端异常值时发生的变化。
```python
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("原始数据：")
print(f"  方差：{variance:.3f}，标准差：{std:.3f}，MAD：{mad:.3f}")

# 添加一个异常值并重新计算
data_outlier = jnp.append(data, 100.0)
mean2 = jnp.mean(data_outlier)
print(f"\n添加异常值（100）后：")
print(f"  方差：{jnp.var(data_outlier):.3f}，标准差：{jnp.std(data_outlier):.3f}，MAD：{jnp.mean(jnp.abs(data_outlier - mean2)):.3f}")
```

2. 计算两个变量之间的皮尔森相关和斯皮尔曼相关。尝试不同的关系。
```python
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"皮尔森 r：  {pearson(x, y):.4f}")
print(f"斯皮尔曼 ρ：{spearman(x, y):.4f}")
```

3. 分别使用 IQR 和 Z 分数方法实现异常值检测，然后比较它们在偏斜数据上的结果。
```python
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 边界：[{lower:.1f}, {upper:.1f}]")
print(f"IQR 异常值：{iqr_outliers}")

# Z 分数方法
z_scores = (data - jnp.mean(data)) / jnp.std(data)
z_outliers = data[jnp.abs(z_scores) > 3]
print(f"\nZ 分数：{z_scores}")
print(f"Z 分数异常值（|z| > 3）：{z_outliers}")
```

4. 在不同平滑因子下计算并绘制带噪声数据的指数移动平均。
```python
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="原始数据", 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")
plt.show()
```
