R语言PCA主成分分析全流程及源码追溯:factoextra vs 基础方法对比解析
一、PCA分析流程回顾
library(ggplot2)
library(factoextra)
library(patchwork)
data(iris)
df <- iris[, 1:4]
# 标准化数据并进行 PCA
pca_res <- prcomp(df, scale. = TRUE)
# 可视化:主成分方差解释比例
fviz_eig(pca_res)
# 可视化:样本个体图,按物种着色
p1 <- fviz_pca_ind(pca_res,
geom.ind = "point",
col.ind = iris$Species,
palette = "npg",
addEllipses = TRUE,
legend.title = "Species")
# 可视化:变量贡献度图
p2 <- fviz_pca_var(pca_res,
col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE)
# 可视化:双标图,综合个体与变量信息
p3 <- fviz_pca_biplot(pca_res,
col.ind = iris$Species,
palette = "jco",
col.var = "black",
addEllipses = TRUE,
legend.title = "Species")
# 三图组合展示
p1 + p2 + p3二、factoextra 包对变量贡献度计算的源码追溯
factoextra::get_pca_var() 函数其实基于 prcomp 的输出,先计算变量与主成分的相关系数矩阵,然后基于相关性的平方(cos²)计算贡献度,核心代码如下:
if (inherits(res.pca, "prcomp")) {
# 计算变量载荷乘以主成分标准差,得到相关系数矩阵
var_cor_func <- function(var.loadings, comp.sdev) {
var.loadings * comp.sdev
}
var.cor <- t(apply(res.pca$rotation, 1, var_cor_func, res.pca$sdev))
# 计算贡献度、相关性等指标
var <- .get_pca_var_results(var.cor)
}其中,计算贡献度的函数定义为:
.get_pca_var_results <- function(var.coord) {
var.cor <- var.coord
var.cos2 <- var.cor^2 # 相关性平方,即cos²
comp.cos2 <- apply(var.cos2, 2, sum) # 每个主成分的总cos²
contrib <- function(var.cos2, comp.cos2) {
var.cos2 * 100 / comp.cos2 # 贡献度百分比
}
var.contrib <- t(apply(var.cos2, 1, contrib, comp.cos2))
# 命名列名
colnames(var.coord) <- colnames(var.cor) <- colnames(var.cos2) <- colnames(var.contrib) <- paste0("Dim.", 1:ncol(var.coord))
list(coord = var.coord, cor = var.cor, cos2 = var.cos2, contrib = var.contrib)
}三、提取主成分得分和载荷
为方便对比,我们用基础 prcomp 输出,按上述方法手动计算变量的相关系数和贡献度:
#1 提取变量
var_res <- factoextra::get_pca_var(pca_res)
#载荷
var_res$coord
var.cor <- pca_res$rotation %*% diag(pca_res$sdev) %>% as.data.frame() #base提取法
#变量对主成分的贡献
var_res$contrib
#变量在主成分方向带相关性平方
var_res$cos2
#2 提取样本相关信息
#样本主成分得分
smp_res <- factoextra::get_pca_ind(pca_res)
smp_res$coord
pca_res$x #base提取法
# Contributions to the PCs
smp_res$contrib
# Quality of representation
smp_res$cos2
#3 主成分的贡献
summary_res <- summary(pca_res)
summary_res$importance ##base提取法
factoextra::get_eigenvalue(pca_res)