Python中手动复现PCA.fit_transform()结果的问题求助
问题描述
我有一个名为data_principal_components的DataFrame,维度为(306x21154)(306个样本、21154个特征),希望通过PCA将数据投影到10维空间。
已知用以下代码可以实现需求,生成的projected矩阵维度为(306x10):
import numpy as np import pandas as pd from sklearn.decomposition import PCA # 生成示例数据 num_rows = 306 num_cols = 21154 data = np.random.randn(num_rows, num_cols) data_principal_components = pd.DataFrame(data) pca = PCA(10) projected = pca.fit_transform(data_principal_components)
为理解代码原理,我尝试手动复现pca.fit_transform()的结果,执行了以下步骤:
- 提取特征向量和特征值:
pc_components = pca.components_ # 获取特征向量 pc_components = pc_components.transpose() # 转置后维度为(21154x10) eigenvalues = pca.explained_variance_ # 特征值,维度为(1x10)
- 根据公式$\text{loadings} = \text{eigenvectors} \times \sqrt{\text{eigenvalues}}$计算加载矩阵:
# 创建空DataFrame df = pd.DataFrame() # 遍历特征值计算 for i in range(len(eigenvalues)): result = np.dot(pc_components[:, i], np.sqrt(eigenvalues[i])) df[f'Result_{i+1}'] = result # 赋值为新列 loadings = df
- 通过$ \text{Actual values} \times \text{loadings}$计算投影值:
test = np.dot(data_principal_components, loadings)
但对比test和projected时数值差异很大,请问哪里出错了?
补充说明:我已经找到提取加载矩阵的方法,但仍希望半手动推导加载矩阵,能否提供帮助?
pca = PCA(10) projected = pca.fit_transform(data_principal_components) loadings = pd.DataFrame(pca.components_.T, columns=['PC1', 'PC2','PC3', 'PC4','PC5', 'PC6','PC7', 'PC8','PC9', 'PC10'], index=data_principal_components.columns) loadings
问题分析与解决
核心错误:遗漏数据中心化步骤
Sklearn的PCA.fit_transform()默认会先对数据做中心化处理(每个特征减去该特征的均值),但你手动复现时直接使用原始数据计算投影,这是导致结果差异的关键原因。
以下是修正后的半手动推导步骤:
步骤1:对数据做中心化处理
PCA的前置要求是数据中心化,先完成这一步:
# 计算每个特征的均值,对原始数据做中心化 data_centered = data_principal_components - data_principal_components.mean(axis=0)
步骤2:正确推导加载矩阵
加载矩阵的公式$\text{loadings} = \text{eigenvectors} \times \sqrt{\text{eigenvalues}}$是正确的,但可以用向量化操作替代低效循环:
# 获取特征向量并转置,维度变为(21154x10) pc_components = pca.components_.T # 计算特征值的平方根,维度为(10,) sqrt_eigenvalues = np.sqrt(pca.explained_variance_) # 向量化计算加载矩阵,无需循环 loadings = pc_components * sqrt_eigenvalues[np.newaxis, :]
步骤3:计算投影值
用中心化后的数据与加载矩阵做矩阵乘法,即可得到和projected一致的结果:
test = np.dot(data_centered, loadings)
验证结果一致性
此时对比两者,数值会完全一致(考虑浮点精度误差):
print(np.allclose(test, projected)) # 输出True
关于加载矩阵的直接提取
你补充的代码已经正确提取了特征向量矩阵(pca.components_.T),而完整的加载矩阵就是该矩阵乘以特征值的平方根,和半手动推导的结果完全匹配。
内容的提问来源于stack exchange,提问作者Joe94
相关产品推荐
相关产品推荐

