如何缩放行坐标散点图与相关圆合并为Biplot?附Sklearn PCA疑问
中心化未缩放数据的PCA Biplot问题解决及Sklearn PCA说明
一、prince包Biplot缩放问题解决
你遇到的散点图比例失调,是因为prince返回的行主坐标(主成分得分)和变量相关系数尺度不匹配:
- 行主坐标的方差等于对应主成分的特征值,尺度远大于相关系数的[-1,1]范围
- 变量相关系数(
column_correlations()返回值)是变量与主成分的线性相关程度,范围固定在[-1,1]
解决方法是对行主坐标进行缩放,使其尺度与相关系数匹配:
- 获取prince PCA模型的特征值(
pca.eigenvalues_) - 将每行的主成分得分除以对应特征值的平方根,让得分的方差变为1,和相关系数尺度对齐
示例代码:
import prince import matplotlib.pyplot as plt import pandas as pd # 假设data是已中心化的数据集 pca = prince.PCA(n_components=2, rescale_with_mean=True, rescale_with_std=False) pca.fit(data) # 获取行主坐标并缩放 row_scores = pca.transform(data) scaled_row_scores = row_scores / pca.eigenvalues_.values ** 0.5 # 获取变量与主成分的相关系数 var_corrs = pca.column_correlations() # 绘制Biplot fig, ax = plt.subplots(figsize=(8, 8)) # 绘制缩放后的行散点 ax.scatter(scaled_row_scores.iloc[:, 0], scaled_row_scores.iloc[:, 1], alpha=0.6) # 绘制变量箭头与标签 for idx, row in var_corrs.iterrows(): ax.arrow(0, 0, row[0], row[1], head_width=0.05, head_length=0.05, color='red') ax.text(row[0]+0.05, row[1]+0.05, idx, color='red') # 添加相关圆 circle = plt.Circle((0, 0), 1, color='gray', fill=False) ax.add_artist(circle) ax.set_xlim(-1.5, 1.5) ax.set_ylim(-1.5, 1.5) ax.set_xlabel(f'PC1 ({pca.explained_inertia_[0]:.1%})') ax.set_ylabel(f'PC2 ({pca.explained_inertia_[1]:.1%})') ax.grid(True) plt.show()
二、Sklearn PCA变量坐标错误的原因及解决方法
原因
Sklearn的PCA并没有计算错误,是你混淆了两个核心概念:
PCA.components_返回的是协方差矩阵的特征向量(载荷向量),这些向量是单位向量(L2范数为1),反映的是变量对主成分的协方差贡献,不是变量与主成分的相关系数- 相关圆需要的坐标是变量与主成分的线性相关系数,它需要结合特征值和变量标准差计算,公式为:
其中r(Xj, PCk) = (components_[k,j] * sqrt(explained_variance_[k])) / std(Xj)explained_variance_[k]是第k个主成分的特征值,std(Xj)是第j个变量的原始标准差
直接用components_绘制相关圆,会把协方差关系当成相关关系,导致变量的相关性显示错误(比如负相关变量显示正交)。
解决方法
手动计算变量与主成分的相关系数作为坐标,再绘制相关圆:
from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler import numpy as np import matplotlib.pyplot as plt # 中心化数据 scaler = StandardScaler(with_std=False) data_centered = scaler.fit_transform(data) # 训练Sklearn PCA pca = PCA(n_components=2) row_scores = pca.fit_transform(data_centered) # 计算变量与主成分的相关系数坐标 var_stds = data.std(axis=0) var_corrs = pca.components_.T * np.sqrt(pca.explained_variance_) / var_stds.values[:, np.newaxis] # 绘制Biplot(行得分同样需要缩放以匹配相关系数尺度) scaled_row_scores = row_scores / np.sqrt(pca.explained_variance_) fig, ax = plt.subplots(figsize=(8, 8)) ax.scatter(scaled_row_scores[:, 0], scaled_row_scores[:, 1], alpha=0.6) for i, (x, y) in enumerate(var_corrs): ax.arrow(0, 0, x, y, head_width=0.05, head_length=0.05, color='red') ax.text(x+0.05, y+0.05, data.columns[i], color='red') circle = plt.Circle((0, 0), 1, color='gray', fill=False) ax.add_artist(circle) ax.set_xlim(-1.5, 1.5) ax.set_ylim(-1.5, 1.5) ax.set_xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.1%})') ax.set_ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.1%})') ax.grid(True) plt.show()
内容的提问来源于stack exchange,提问作者ElMeTeOr
相关产品推荐
相关产品推荐

