代码说明
-
数据准备:代码首先生成了一些具有相关性的示例因子数据,模拟真实世界中的因子相关性。
-
PCA正交化:
- 使用
sklearn
库的PCA实现主成分分析 - 可选对数据进行标准化处理(通常推荐)
- 返回正交化的主成分因子
- 使用
-
结果分析:
- 显示各主成分解释的方差比例
- 绘制方差解释累积图
- 验证主成分间的正交性(相关性应为0)
- 显示主成分与原始因子的关系(因子载荷)
PCA在因子投资中的应用
与格拉姆-施密特正交化相比,PCA有以下特点:
- 无顺序依赖性:PCA结果不依赖于因子输入顺序
- 最大化方差解释:主成分按解释方差能力降序排列
- 数据驱动:完全基于数据特征,不依赖先验经济理论
- 可能失去经济含义:主成分可能是原始因子的线性组合,经济解释可能不直观
在实际应用中,PCA常用于:
- 降维:保留大部分方差的同时减少因子数量
- 处理高度相关的因子
- 构建正交因子组合
- 风险模型中的因子构建
python
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
def factor_pca_orthogonalization(factor_data, n_components=None, standardize=True):
"""
使用主成分分析(PCA)对因子进行正交化处理
参数:
factor_data: numpy数组或DataFrame, 形状为 (n_samples, n_factors)
n_components: 保留的主成分数量,默认为None(保留所有)
standardize: 是否对数据进行标准化,默认为True
返回:
pca_components: 主成分(正交化后的因子)
explained_variance: 各主成分解释的方差比例
pca_model: 训练好的PCA模型
"""
# 转换为numpy数组
if isinstance(factor_data, pd.DataFrame):
factor_values = factor_data.values
factor_names = factor_data.columns.tolist()
else:
factor_values = factor_data
factor_names = [f'Factor_{i}' for i in range(factor_data.shape[1])]
# 数据标准化(可选)
if standardize:
scaler = StandardScaler()
factor_values = scaler.fit_transform(factor_values)
# 执行PCA
pca = PCA(n_components=n_components)
pca_components = pca.fit_transform(factor_values)
# 计算解释方差比例
explained_variance = pca.explained_variance_ratio_
# 创建主成分名称
component_names = [f'PC_{i+1}' for i in range(pca_components.shape[1])]
# 转换为DataFrame
pca_df = pd.DataFrame(pca_components, columns=component_names)
return pca_df, explained_variance, pca
def plot_variance_explained(explained_variance):
"""
绘制主成分解释方差比例的累积图
"""
cumulative_variance = np.cumsum(explained_variance)
plt.figure(figsize=(10, 6))
plt.bar(range(1, len(explained_variance) + 1), explained_variance,
alpha=0.6, color='g', label='Individual explained variance')
plt.step(range(1, len(cumulative_variance) + 1), cumulative_variance,
where='mid', label='Cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.title('Variance Explained by Principal Components')
plt.grid(True)
plt.show()
# 示例用法
if __name__ == "__main__":
# 生成示例因子数据(5个因子,1000个观测值)
np.random.seed(42)
n_samples = 1000
n_factors = 5
# 创建一些相关的因子
factor1 = np.random.normal(0, 1, n_samples)
factor2 = factor1 * 0.7 + np.random.normal(0, 0.5, n_samples) # 与factor1相关
factor3 = np.random.normal(0, 1, n_samples)
factor4 = factor3 * 0.6 - factor1 * 0.3 + np.random.normal(0, 0.4, n_samples) # 与factor1和factor3相关
factor5 = np.random.normal(0, 1, n_samples)
# 创建因子数据矩阵
factor_data = np.column_stack([factor1, factor2, factor3, factor4, factor5])
factor_names = ['Value', 'Quality', 'Momentum', 'LowVol', 'Size']
factor_df = pd.DataFrame(factor_data, columns=factor_names)
print("原始因子数据(前5行):")
print(factor_df.head())
# 计算因子间的相关性
print("\n因子间相关性矩阵:")
print(factor_df.corr())
# 使用PCA进行因子正交化
pca_factors, explained_variance, pca_model = factor_pca_orthogonalization(
factor_df, n_components=None, standardize=True
)
print("\n主成分解释方差比例:")
for i, var in enumerate(explained_variance):
print(f"PC_{i+1}: {var:.4f} ({var*100:.2f}%)")
print("\n累积解释方差比例:")
cumulative_var = np.cumsum(explained_variance)
for i, var in enumerate(cumulative_var):
print(f"前{i+1}个主成分: {var:.4f} ({var*100:.2f}%)")
# 绘制方差解释图
plot_variance_explained(explained_variance)
# 检查主成分间的正交性(相关性应为0或接近0)
print("\n主成分间的相关性矩阵:")
print(pca_factors.corr().round(4))
# 显示主成分与原始因子的关系(因子载荷)
print("\n主成分与原始因子的关系(因子载荷):")
loadings = pd.DataFrame(
pca_model.components_.T,
columns=[f'PC_{i+1}' for i in range(pca_model.components_.shape[0])],
index=factor_names
)
print(loadings)