使用Sklearn绘制的主成分存在角度偏移,求排查问题原因
问题与解决方法
问题描述
我生成了200,000个服从多元正态分布的样本数据,想用Sklearn的PCA查看捕获最大方差的主成分形态。对PCA得到的特征向量做归一化后,按特征值平方根缩放绘图,发现主成分未如预期平分数据分布,反而存在角度偏移,无法定位问题。
原代码
import numpy as np import seaborn as sns import matplotlib.pyplot as plt from sklearn.decomposition import PCA # generate synthetic data with a linear relationship np.random.seed(0) mean = [0, 0] # mean of both features cov = [[1, 0.8], [0.8, 1]] # covariance matrix to sample from data = np.random.multivariate_normal(mean, cov, 200000) # sample data points with given parameters # feature cols feature_1 = data[:, 0] feature_2 = data[:, 1] # perform PCA pca = PCA(n_components=2) principal_components = pca.fit_transform(data) # principal component vectors eigenvectors = pca.components_ eigenvalues = pca.explained_variance_ eigenvectors_unit = eigenvectors / np.linalg.norm(eigenvectors, axis=1, keepdims=True) # computing unit eigenvectors pc1 = eigenvectors_unit[0, :] * np.sqrt(eigenvalues[0]) pc2 = eigenvectors_unit[1, :] * np.sqrt(eigenvalues[1]) # plot principal components plt.figure(figsize=(8, 6)) sns.scatterplot(x=feature_1, y=feature_2, color='black', alpha=0.8) plt.title('Principal Components') origin = np.zeros(2) plt.quiver(origin[0], origin[1], pc1[0], pc1[1], color='red', scale=5) plt.quiver(origin[0], origin[1], pc2[0], pc2[1], color='blue', scale=5) plt.tight_layout() plt.show()
问题根源与修正
问题主要出在两个核心点:
plt.quiver的scale参数:设置scale=5会强制缩放箭头长度,导致主成分向量的显示比例失真,视觉上出现角度偏移。- 坐标轴比例不一致:默认绘图时x、y轴刻度间距可能不同,即使向量角度正确,视觉上也会显得偏移。
- 额外的归一化操作:Sklearn的
PCA.components_返回的已经是单位特征向量,无需手动重复归一化(这步不影响结果,但属于冗余操作)。
修正后的代码
import numpy as np import seaborn as sns import matplotlib.pyplot as plt from sklearn.decomposition import PCA # 生成带线性关系的合成数据 np.random.seed(0) mean = [0, 0] # 两个特征的均值 cov = [[1, 0.8], [0.8, 1]] # 采样用的协方差矩阵 data = np.random.multivariate_normal(mean, cov, 200000) # 生成指定参数的样本点 # 提取特征列 feature_1 = data[:, 0] feature_2 = data[:, 1] # 执行PCA pca = PCA(n_components=2) principal_components = pca.fit_transform(data) # 主成分向量(已为单位向量) eigenvectors = pca.components_ eigenvalues = pca.explained_variance_ # 按特征值平方根缩放,对应数据在该主成分上的标准差 pc1 = eigenvectors[0, :] * np.sqrt(eigenvalues[0]) pc2 = eigenvectors[1, :] * np.sqrt(eigenvalues[1]) # 绘图 plt.figure(figsize=(8, 6)) sns.scatterplot(x=feature_1, y=feature_2, color='black', alpha=0.8) plt.title('主成分展示') origin = np.zeros(2) # 去掉scale参数,保留向量真实长度 plt.quiver(origin[0], origin[1], pc1[0], pc1[1], color='red') plt.quiver(origin[0], origin[1], pc2[0], pc2[1], color='blue') plt.axis('equal') # 设置坐标轴等比例,避免视觉角度偏移 plt.tight_layout() plt.show()
关键修正点说明
- 移除
plt.quiver的scale参数,让箭头长度完全匹配计算出的主成分向量长度(对应数据在该维度的标准差)。 - 添加
plt.axis('equal'):强制x、y轴使用相同的刻度间距,确保向量角度的视觉呈现与实际一致。 - 移除冗余的归一化步骤,直接使用
PCA.components_提供的单位特征向量。
内容的提问来源于stack exchange,提问作者Neil Panchal
相关产品推荐
相关产品推荐

