2.5 PCA(主成分分析)
从大量数据中找到最重要的"隐藏方向",用少量综合变量解释大部分信息。
一、先说协方差
PCA 的输入是协方差矩阵。如果你还不熟悉协方差,先从这里看起。
1.1 直觉——两个东西的"同向运动"程度
| 含义 | ||
|---|---|---|
| 大盘涨 | 茅台涨 | 正相关 → 协方差 > 0 |
| 大盘涨 | 黄金跌 | 负相关 → 协方差 < 0 |
| 大盘涨 | 随机数 | 无关系 → 协方差 ≈ 0 |
1.2 手算协方差
公式:
(而不是 )是因为样本方差的无偏修正——自由度(degrees of freedom)调整。计算方差时除以 而非 ,是因为用样本均值代替总体均值损失了一个自由度。
算一个:两只股票 4 天的日收益率(%)
| 天 | 茅台 | 五粮液 |
|---|---|---|
| 1 | +2 | +1 |
| 2 | -1 | 0 |
| 3 | +3 | +2 |
| 4 | 0 | -1 |
Step 1:计算均值
Step 2:计算每组的离差乘积
| 天 | 乘积 | ||
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 |
Step 3:求和、除以
含义:茅台的日收益率变动 +1% 时,五粮液平均同向变动约 +0.67%(,除以茅台方差)。两只白酒股一起涨跌。
1.3 协方差矩阵
有 个变量时,把所有两两之间的协方差排成矩阵:
关键性质:
- 对称: →
- 半正定:对任何向量 ,(组合方差不能为负)
- 这两个性质保证了 一定有实数特征值、且全部 ——所以可以对它做特征值分解
Quant Link:协方差矩阵是现代投资组合理论的核心。Markowitz 用均值向量 估计预期收益、用 估计风险,"最优组合"就是在收益和 (组合方差)之间做权衡。
二、核心思想
2.1 直觉
你有 100 只股票的收益率数据,每只都有独立的风险。但它们不是完全独立的——当大盘涨时,大部分股票都涨。也就是说,存在一些"共同因子"驱动着所有股票。
PCA 就是自动找出这些因子的方法。
2.2 PCA = 特征值分解
PCA 在数学上等价于对协方差矩阵做特征值分解:
- (最大特征值)对应第 1 主成分——数据波动最大的方向
- (次大)对应第 2 主成分——与 PC1 正交,次大波动方向
- (对应的特征向量)告诉你 PC1 在各资产上的权重
2.3 算一个
沿用上面的两只股票,假设协方差矩阵是:
对角线上 是茅台自己收益率的方法(从手算数据得), 是五粮液的方法。对称,且是半正定。
对这个矩阵做特征值分解(方法见 2.4 节):
特征值 ,
含义:
- PC1(解释 总方差):两只股票同方向、各占 权重——这是"白酒板块因子",当板块整体涨时第一主成分得分高
- PC2(解释 方差):一只做多、一只做空——这是"价差因子",当茅台跑赢五粮液时得分高
为什么 ? PCA 在标准化后的数据(相关系数矩阵)上做,特征值之和等于变量个数,所以各主成分的特征值都在 1 附近。这里用协方差矩阵(未标准化),特征值可以大于变量数。
三、PCA 步骤
- 标准化:每个特征减去均值、除以标准差(标准化后的协方差矩阵 = 相关系数矩阵,各变量量纲统一)
- 算协方差(或相关)矩阵
- 特征值分解,特征值从大到小排列
- 选择前 个:通常取能解释 80-90% 方差的数量
- 投影:原始数据 前 个特征向量 = 降维后的数据
四、量化应用
4.1 利率曲线
国债收益率曲线有几十个期限(1 个月到 30 年),但 PCA 发现前 3 个主成分就能解释 95% 以上的变动:
| 主成分 | 名称 | 解释比例 | 含义 |
|---|---|---|---|
| PC1 | 水平 | ~70% | 所有期限利率同时升降 |
| PC2 | 倾斜 | ~20% | 短端和长端反向变动 |
| PC3 | 曲率 | ~5% | 中间相对于两端变动 |
应用:不需要跟踪每个期限的利率风险,只需要管理 3 个因子的暴露。
4.2 协方差矩阵去噪
样本协方差矩阵包含大量噪声。用 PCA 只保留前 个主成分来做组合优化,效果往往更好:
def pca_denoise(Sigma, k):
eigvals, eigvecs = np.linalg.eigh(Sigma)
eigvals = eigvals.copy()
eigvals[:-k] = 0 # 小特征值归零
return eigvecs @ np.diag(eigvals) @ eigvecs.T五、Python 实践
import numpy as np
from sklearn.decomposition import PCA
# 模拟 50 只股票、500 天的数据
np.random.seed(42)
n_assets, n_days = 50, 500
returns = np.random.randn(n_days, n_assets)
# 用 sklearn 做 PCA
pca = PCA(n_components=10)
pca.fit(returns)
# 看前几个主成分解释了多少方差
var = pca.explained_variance_ratio_
print(f"PC1: {var[0]:.2%}")
print(f"PC2: {var[1]:.2%}")
print(f"PC3: {var[2]:.2%}")
print(f"前5个累计: {var[:5].sum():.2%}")
# 手动实现 PCA
cov = np.cov(returns.T)
eigvals, eigvecs = np.linalg.eigh(cov)
idx = np.argsort(eigvals)[::-1]
eigvals, eigvecs = eigvals[idx], eigvecs[:, idx]
print(f"\n第一主成分(前5个权重): {eigvecs[:5, 0].round(4)}")\n> 下一步:继续学习 2.6 矩阵微积分