PCA应用于协方差矩阵的复现疑问及方向标准差提取求助
关于PCA提取标准差与流程复现的疑问解答
嘿,我看到你在复现这个PCA相关的流程时遇到了两个关键疑问,咱们一步步来拆解清楚:
第一个疑问:是否该直接用prcomp()的sdev,以及是否需要缩放数据?
首先要指出你当前代码里的一个关键误区:不要把协方差矩阵直接传给prcomp()!prcomp()的设计逻辑是直接接收原始数据集,它会自动帮你计算协方差矩阵(当设置scale.=FALSE时)。你现在传入covar.df(协方差矩阵),相当于对协方差矩阵本身做PCA,这和对原始坐标数据做PCA的结果完全不是一回事。
关于缩放的问题:如果你的x和y已经处于同一尺度(比如都是相同量级的数值、同一单位),确实不需要开启scale.=TRUE。使用prcomp(df, scale. = FALSE)会基于原始数据的协方差矩阵计算PCA,此时输出的sdev正好就是你需要的主成分方向的标准差——因为sdev的本质就是特征值的平方根(pca.results$sdev^2就是对应特征值λ₁、λ₂),完全符合原流程里σ₁和σ₂的定义。
第二个疑问:如何提取对应方向的标准差?
首先要明确:你要的不是原始x/y坐标轴方向的标准差(那个直接用sd(df$x)和sd(df$y)就能得到),而是PCA主成分方向的标准差,也就是prcomp()输出的sdev向量里的两个值:
pca.results$sdev[1]:第一主成分方向的标准差σ₁(对应方差最大的方向,即特征值λ₁的平方根)pca.results$sdev[2]:第二主成分方向的标准差σ₂(对应与第一主成分正交的方向,即特征值λ₂的平方根)
要绘制原流程里的正交线段,你还需要结合pca.results$rotation(也就是特征向量u和v),以及数据的均值colMeans(df)来计算线段的端点。
修正后的完整复现代码
# 可复现数据 set.seed(1) x <- rnorm(10, 50, 4) y <- rnorm(10, 50, 7) df <- data.frame(x, y) # 正确的PCA:直接对原始数据执行,scale=FALSE因为数据尺度一致 pca.results <- prcomp(df, scale. = FALSE) # 提取流程所需的核心变量 mean_coords <- colMeans(df) # 坐标数据的均值中心 pc_sdev <- pca.results$sdev # 主成分方向的标准差σ₁和σ₂ pc_vectors <- pca.results$rotation # 主成分对应的特征向量u和v # 验证:sdev的平方等于协方差矩阵的特征值,确保逻辑正确 cov_matrix <- cov(df) eigen_values <- eigen(cov_matrix)$values all.equal(pc_sdev^2, eigen_values) # 应返回TRUE,说明计算一致 # 绘制符合原流程要求的图形 plot(df, main = "PCA主成分方向与标准差线段", pch = 16, col = "steelblue") points(mean_coords[1], mean_coords[2], pch = 19, col = "red", cex = 1.2) # 绘制第一主成分线段(长度为1个σ₁) segments( mean_coords[1] - pc_sdev[1] * pc_vectors[1,1], mean_coords[2] - pc_sdev[1] * pc_vectors[2,1], mean_coords[1] + pc_sdev[1] * pc_vectors[1,1], mean_coords[2] + pc_sdev[1] * pc_vectors[2,1], col = "darkorange", lwd = 2 ) # 绘制第二主成分线段(长度为1个σ₂) segments( mean_coords[1] - pc_sdev[2] * pc_vectors[1,2], mean_coords[2] - pc_sdev[2] * pc_vectors[2,2], mean_coords[1] + pc_sdev[2] * pc_vectors[1,2], mean_coords[2] + pc_sdev[2] * pc_vectors[2,2], col = "forestgreen", lwd = 2 )
额外补充
- 如果误开启了
scale.=TRUE,prcomp()会基于相关矩阵计算PCA,此时sdev就不是原流程要求的基于协方差的标准差了,所以一定要注意这个参数的设置。 - 原流程里“对协方差矩阵应用PCA”的操作,其实完全等价于对原始数据执行
prcomp(scale.=FALSE),因为prcomp底层就是通过协方差矩阵的特征分解来实现PCA的。
内容的提问来源于stack exchange,提问作者Jack Armstrong




