在数据科学的探索性分析(EDA)阶段,当我们面对包含大量变量的数据集时,经常会感到无从下手。变量太多不仅会增加计算负担,还会导致“维度灾难”,使得数据可视化变得异常困难。这时,主成分分析 就成了我们的得力助手,它能帮助我们简化数据结构,找出数据中最核心的变化趋势。
但是,在运行 PCA 之前或之后,你是否有这样的疑问:“我究竟应该保留多少个主成分才算合适?”如果保留太少,可能会丢失关键信息;如果保留太多,又失去了降维的意义。为了解决这个问题,今天我们将一起深入探讨一种经典的可视化工具——碎石图。
在这篇文章中,我们将不仅仅学会如何画图,还会深入理解其背后的统计原理。我们将使用 R 语言中最强大的可视化包 ggplot2,结合经典的鸢尾花数据集,带你从零开始,手把手绘制出专业、美观的碎石图。无论你是刚入门 R 语言的新手,还是希望提升可视化质量的资深开发者,我相信你都会在接下来的内容中找到实用的技巧。
目录
为什么碎石图如此重要?
在开始敲代码之前,让我们先达成一个共识:理解数据是分析的第一步。PCA 的核心思想是将原始的相关变量转化为一系列不相关的主成分,这些主成分按照方差解释量的大小进行排序。
- 第一主成分(PC1):捕获了数据中最大的方差。
- 第二主成分(PC2):捕获了剩余方差中最大的部分,以此类推。
碎石图通过以主成分序号为横坐标,以方差解释量为纵坐标,展示了每个主成分对数据信息的贡献程度。通过观察这条曲线(通常像陡峭的悬崖后拖着一条尾巴,因此得名“Scree Plot”),我们可以利用“肘部法则”来决定保留多少个主成分——即找到曲线由陡峭变平缓的那个转折点。
步骤 1:数据预处理与清洗
PCA 对数据类型很敏感,它仅适用于数值型数据。如果你的数据集中包含分类变量(如“男/女”、“A/B/C”),直接运行 PCA 会报错或产生无意义的结果。
让我们以 R 语言内置的 INLINECODEe11a9318(鸢尾花)数据集为例。这个数据集包含 5 个列,其中 INLINECODE1efad2a5(物种)是分类变量,其余 4 个列是花萼和花瓣的长宽数值。
加载必要的库
首先,我们需要加载 INLINECODE296f825b 库。如果你还没安装,请先运行 INLINECODE5d86ab5b。
# 加载 ggplot2 可视化库
library(ggplot2)
# 为了方便数据处理,我们也加载 dplyr 库(可选,但推荐)
library(dplyr)
清理非数值列
我们需要剔除 INLINECODE3ba81e03 列。这里我们使用 base R 中的 INLINECODE4382b30d 函数,或者使用 INLINECODEee344f95 的 INLINECODE633f5d2f 函数。为了保持代码的兼容性,我们采用 subset() 方法。
# 查看原始数据结构
str(iris)
# 从原始数据集中移除 Species 列
# subset 是筛选数据最直观的方式之一
num_iris <- subset(iris, select = -c(Species))
# 查看清洗后的数据前几行
head(num_iris)
输出示例:
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
3 4.7 3.2 1.3 0.2
4 4.6 3.1 1.5 0.2
5 5.0 3.6 1.4 0.2
6 5.4 3.9 1.7 0.4
现在,num_iris 就是一个纯数值矩阵了,非常适合进行 PCA 分析。
步骤 2:计算主成分分析 (PCA)
有了干净的数据,接下来就是计算 PCA。在 R 语言中,prcomp() 函数是进行主成分分析的标准函数,它基于奇异值分解(SVD)算法,数值稳定性非常好。
代码实现
我们需要注意 INLINECODE3266f176 参数(注意这里有个点,通常简写为 INLINECODE9e9aab48)。如果你的变量单位不同(比如一个变量是几万元,另一个是几厘米),为了消除量纲影响,务必设置 scale = TRUE。
# 使用 prcomp 函数计算主成分
# scale. = TRUE 表示对数据进行标准化(减去均值,除以标准差)
pca_result <- prcomp(num_iris, scale. = TRUE)
# 打印 PCA 结果摘要
# 这里包含了标准差、方差贡献比例和累计方差贡献比例
summary(pca_result)
输出解释:
summary() 的输出非常重要,它告诉我们每个主成分解释了多少百分比的方差。例如,PC1 可能解释了 73% 的方差,PC2 解释了 22% 等等。我们要绘制的碎石图,其实就是将这些数字可视化。
步骤 3:计算方差贡献值
虽然 summary() 能看到结果,但为了绘图,我们需要提取出具体的数值。
原理解析
在 INLINECODEf7266a12 返回的对象中,INLINECODEcaf79781 包含了每个主成分的标准差。方差(Variance)就是标准差的平方。方差贡献比例 等于该主成分的方差除以总方差。
公式如下:
$$ \text{Variance Explained} = \frac{ ext{PC}_i \text{ Variance}}{\text{Total Variance}} $$
代码提取
# 提取标准差
std_dev <- pca_result$sdev
# 计算方差(标准差的平方)
variance <- std_dev^2
# 计算每个主成分解释的方差比例
# 这就是我们要在 Y 轴上展示的数据
variance_ratio <- variance / sum(variance)
# 打印结果看看
print(variance_ratio)
输出结果示例:
[1] 0.729624454 0.228507618 0.036689219 0.005178709
这里我们看到,第一个数字(PC1)解释了约 73% 的数据差异,第二个(PC2)解释了约 23%。
步骤 4:使用 ggplot2 绘制碎石图
现在到了最激动人心的部分!我们将使用 ggplot2 将上述计算结果绘制成图表。为了让你掌握更多技能,我将展示三种不同风格的绘图方式:基础折线图、柱状图以及更高级的数据框映射法。
方法一:使用 qplot 快速绘制折线图
INLINECODE11a45065 是 INLINECODE40f92b8c 的快速绘图函数,语法类似于 base R 的 plot 函数,非常适合快速原型设计。
在这个例子中,我们绘制 PC 序号(1到4)与方差比例的关系,并添加折线和点。
# 确保已加载 ggplot2
library(ggplot2)
# 使用 qplot 快速绘图
# c(1:4) 生成主成分的序号,即 X 轴
qplot(c(1:4), variance_ratio,
geom = c("line", "point"), # 同时绘制线和点
size = I(4), # 设置点的大小
xlab = "Principal Component",
ylab = "Variance Explained",
main = "Scree Plot - Basic Line") +
ylim(0, 1) + # 设置 Y 轴范围
theme_minimal() # 使用简洁主题
实用见解:通过观察这幅图,你会发现在 PC2 之后,线条迅速变平。这意味着 PC3 和 PC4 对整体方差的贡献微乎其微。在实际项目中,我们可能会选择只保留前两个主成分,从而将数据从 4 维压缩到 2 维。
方法二:绘制柱状图形式的碎石图
有时候,柱状图能更直观地展示每个主成分方差贡献的“分量”。我们可以使用 geom_col() 来实现。
# 绘制柱状图
qplot(c(1:4), variance_ratio) +
geom_col(fill = "steelblue", color = "black") + # 填充颜色和边框
xlab("Principal Component") +
ylab("Variance Explained - Proportion") +
ggtitle("Scree Plot - Bar Chart") +
ylim(0, 1) +
theme_bw() + # 黑白主题,显得更学术
geom_text(aes(label = round(variance_ratio, 2)), vjust = -0.5) # 添加数值标签
这里我添加了一个小技巧:geom_text()。它能直接在柱子上方显示具体的数值,这在制作报告图表时非常有用,观众不需要去对着 Y 轴猜数值。
方法三:专业级绘制(数据框映射法)
在实际工作中,我们通常不会直接使用向量绘图,而是先创建一个结构化的数据框。这样做的好处是代码更易读,且更容易扩展(例如添加主成分名称)。
# 创建一个专门用于绘图的数据框
# PC 列代表主成分编号,Variance 列代表方差比例
plot_data <- data.frame(
PC = paste0("PC", 1:length(variance_ratio)),
Variance = variance_ratio
)
# 使用 ggplot() 函数进行标准绘图
p <- ggplot(plot_data, aes(x = PC, y = Variance, group = 1)) +
# 绘制折线
geom_line(color = "#00AFBB", size = 1.5) +
# 绘制点
geom_point(color = "#FC4E07", size = 4, shape = 21, fill = "white") +
# 添加主题和标签
theme_classic() +
labs(
title = "PCA Scree Plot with ggplot2",
subtitle = "Visualizing Variance Explained by Each Component",
x = "Principal Components",
y = "Proportion of Variance Explained"
) +
# 添加一条参考线,帮助识别肘部
geom_hline(yintercept = 0.10, linetype = "dashed", color = "gray")
# 展示图表
print(p)
在这个进阶示例中,我们使用了 ggplot() 的标准图层语法,并添加了一条虚线作为参考线(比如 10% 的阈值),这能帮助我们更清晰地判断哪些主成分是不重要的。
深入探讨与最佳实践
如何解读碎石图?
绘制出图表只是第一步,解读它才是关键。
- 寻找肘部:观察曲线,找到它由陡峭变为平缓的转折点。在鸢尾花数据集中,PC2 到 PC3 之间就是一个明显的转折。
- 累计方差贡献:除了单个方差,我们通常还需要计算累计方差。如果你发现前两个主成分加起来解释了 95% 的方差,那么这两个成分就足以代表原始数据了。
常见错误与解决方案
- 忘记标准化数据:如果你在 INLINECODE8c0a1da4 中没有设置 INLINECODE2ec607b0,且你的变量单位不同,碎石图可能会误导你。方差最大的变量可能会主导 PC1,仅仅是因为它的数值很大(比如工资 vs 年龄)。最佳实践:对于包含不同量纲变量的数据集,始终开启标准化。
- X 轴标签问题:很多新手直接用
1:4作为 X 轴,导致图表上显示的是数字 1, 2, 3, 4 而不是 "PC1", "PC2"。通过上述“方法三”中的数据框映射,你可以轻松解决这个问题,使图表更专业。
性能优化建议
对于像鸢尾花这样的小数据集,计算速度可以忽略不计。但当你处理数百万行的大数据时,PCA 可能会变慢。建议在调用 prcomp 之前,先移除方差极小(即几乎不变)的列,因为这些列对主成分没有贡献,只会增加计算开销。
总结与后续步骤
在本文中,我们一起学习了如何使用 R 语言和 ggplot2 库来绘制碎石图。我们不仅完成了从数据清洗、PCA 计算、方差提取到最终绘制的全过程,还深入探讨了如何通过可视化结果来决定保留多少个主成分。
核心要点回顾:
- PCA 适合数值数据,记得预处理(删除非数值列,标准化)。
- 碎石图 帮助我们直观地看到每个主成分的重要性。
- ggplot2 提供了灵活的绘图方式,从
qplot到完整的数据框映射,能满足不同场景的需求。
下一步建议
既然你已经掌握了碎石图的绘制方法,我建议你接下来尝试:
- 绘制双标图:在碎石图的基础上,尝试绘制 PCA 的 Biplot,它能在同一张图上展示样本点和变量向量。
- 应用实战:找一个高维数据集(例如手写数字识别数据集 MNIST 的一部分),运行 PCA 并绘制碎石图,看看能减少多少维度而不失真。
希望这篇文章对你有所帮助!如果你在练习中遇到任何问题,欢迎随时查阅 R 语言的相关文档或在社区中交流。祝你的数据科学之旅越走越远!