R: R ggplot2 进阶:图层叠加与分面系统
最后更新:2026-08-26
上一课我们学了 ggplot2 三件套:
ggplot() + aes() + geom_xxx(),能画 5 种基础图。这一课进入ggplot2 进阶——掌握"图层叠加"和"分面系统",画任意复杂图。这是 ggplot2 真正强大的地方。
读完这一课你能画:多图层组合图、自动分面、统计叠加、坐标变换、注释标注——ggplot2 能做的 90% 都在这一课。
1. 你将学到
- ggplot2 7 层语法回顾
- 图层叠加(多个 geom + stat)
- facet_wrap() / facet_grid() 分面
- stat_summary() 统计图层
- 坐标变换(coord_flip / coord_polar / coord_fixed)
- annotation 注释
- 复杂组合图实战
2. 一个多维分析的故事
(1) 痛点:4 城市 4 季度 4 产品怎么画?
Bob要展示4 城市 × 4 季度 × 4 产品的销售数据——3 维信息一张图能装下吗?
(2) ggplot2 的解法
# 用 facet_wrap() 把"产品"分 4 张小图
ggplot(sales, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1) +
geom_point(size = 3) +
facet_wrap(~ product) + # 按产品分面
labs(title = "4 城市 × 4 产品 销售趋势")
# 一次画 4 张图,自动分面!
1 行 facet_wrap() 把 1 张图变 4 张。这就是分面的力量。
3. ggplot2 7 层语法回顾
(1) 7 层结构
graph TB
A[ggplot 图] --> B[1. 数据 layer<br/>ggplot data]
A --> C[2. 美学映射 layer<br/>aes]
A --> D[3. 几何对象 layer<br/>geom_xxx]
A --> E[4. 统计变换 layer<br/>stat_xxx]
A --> F[5. 坐标系 layer<br/>coord_xxx]
A --> G[6. 分面 layer<br/>facet_xxx]
A --> H[7. 主题 layer<br/>theme_xxx]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
style F fill:#ffe1e1
style G fill:#e1ffe1
style H fill:#ffe1d4
(2) 图层叠加示例
ggplot(df, aes(x, y)) + # 数据 + 美学
geom_point() + # 几何层 1:散点
geom_smooth() + # 几何层 2:趋势线
stat_summary() + # 统计层
scale_y_log10() + # 坐标变换
facet_wrap(~ group) + # 分面
theme_minimal() # 主题
+ 加一个图层,按从下到上顺序叠加。
4. facet_wrap() / facet_grid() 分面
(1) 什么是分面?
分面(facet)= 把数据按某个变量拆成多张子图:
# 没分面:1 张图
ggplot(sales, aes(x, y)) + geom_point()
# 分面后:4 张小图
ggplot(sales, aes(x, y)) + geom_point() + facet_wrap(~ category)
(2) facet_wrap() 一维分面
# 按 city 分面(4 张子图)
ggplot(sales_long, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line() +
facet_wrap(~ city) +
labs(title = "各城市季度销售")
# 控制分面布局:nrow / ncol
facet_wrap(~ city, nrow = 2) # 2 行
facet_wrap(~ city, ncol = 4) # 4 列
(3) facet_grid() 二维分面
# 按 city(行) 和 product(列) 二维分面
facet_grid(rows = vars(city), cols = vars(product))
# 简写
facet_grid(city ~ product)
# 公式语法(老版本)
facet_grid(. ~ category) # 仅按 category 列分面
facet_grid(category ~ .) # 仅按 category 行分面
(4) 分面对比
| 语法 | 用途 |
|---|---|
facet_wrap(~ var) |
一维分面(自动换行) |
facet_grid(var ~ .) |
一维(行) |
facet_grid(. ~ var) |
一维(列) |
facet_grid(row ~ col) |
二维(行 × 列) |
(5) 分面扩展
# 自由缩放(每张子图独立坐标)
facet_wrap(~ city, scales = "free") # 自由 x/y
facet_wrap(~ city, scales = "free_x") # 仅自由 x
facet_wrap(~ city, scales = "free_y") # 仅自由 y
# 加标签
facet_wrap(~ city, labeller = label_both) # 显示"city: 北京"
# 控制每行子图数
facet_wrap(~ city, nrow = 2)
5. stat_summary() 统计图层
(1) 自动统计
# 自动加均值点 + 误差线
ggplot(df, aes(x = group, y = value)) +
geom_boxplot() +
stat_summary(fun = mean, geom = "point", color = "red", size = 3)
(2) 常用 stat 函数
| 函数 | 作用 |
|---|---|
mean |
均值 |
median |
中位数 |
sd |
标准差 |
sum |
求和 |
function(x) mean(x) + sd(x) |
自定义 |
(3) 实战:均值 + 误差线
ggplot(df, aes(x = group, y = value, color = group)) +
geom_point(alpha = 0.3) + # 原始数据
stat_summary(fun = mean, geom = "point", size = 4, color = "red") +
stat_summary(fun.data = mean_se, geom = "errorbar", width = 0.2)
6. geom_smooth() 趋势线
(1) 基础用法
# 自动加 LOESS 趋势线
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth()
# 线性回归
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth(method = "lm")
# 关闭置信区间
geom_smooth(method = "lm", se = FALSE)
(2) 实战
# 按组分多条线
ggplot(sales_long, aes(x = quarter, y = sales, color = city, group = city)) +
geom_point() +
geom_line() +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.5)
7. 坐标变换
(1) coord_flip() 翻转
# 横向柱状图
ggplot(df, aes(x = city, y = sales)) +
geom_col() +
coord_flip() # x/y 互换
(2) coord_polar() 极坐标
# 饼图
ggplot(df, aes(x = 1, y = sales, fill = city)) +
geom_col() +
coord_polar(theta = "y")
# 玫瑰图
ggplot(df, aes(x = city, y = sales, fill = city)) +
geom_col() +
coord_polar()
(3) coord_fixed() 等比例
# 散点图 X/Y 等比例
ggplot(mpg, aes(cty, hwy)) +
geom_point() +
coord_fixed(ratio = 1) # 1:1 比例
(4) 坐标轴变换
# log 变换
ggplot(df, aes(x, y)) + geom_point() + scale_y_log10()
# 平方根变换
ggplot(df, aes(x, y)) + geom_point() + scale_y_sqrt()
8. 注释与文本
(1) geom_text() / geom_label()
# 数据点旁加标签
ggplot(df, aes(x, y)) +
geom_point() +
geom_text(aes(label = name), vjust = -0.5)
# 加圆框标签
ggplot(df, aes(x, y)) +
geom_point() +
geom_label(aes(label = name))
(2) annotate() 手动注释
# 标记关键点
ggplot(df, aes(x, y)) +
geom_point() +
annotate("text", x = 5, y = 8, label = "关键点", color = "red") +
annotate("rect", xmin = 4, xmax = 6, ymin = 7, ymax = 9,
alpha = 0.2, fill = "yellow")
(3) 箭头
library(ggrepel)
# 自动避让的标签
ggplot(df, aes(x, y, label = name)) +
geom_point() +
geom_text_repel() # 不重叠的标签
9. 多图组合
(1) patchwork 包
# 安装
install.packages("patchwork")
library(patchwork)
# 4 图合 1
(p1 + p2) / (p3 + p4) # 2 行 2 列
# 复杂布局
p1 + p2 + p3 + p4 + plot_layout(ncol = 2, heights = c(2, 1))
# 加标题
(p1 + p2) / (p3 + p4) + plot_annotation(title = "综合分析")
(2) cowplot 包
library(cowplot)
plot_grid(p1, p2, p3, p4, ncol = 2, labels = c("A", "B", "C", "D"))
10. 完整示例:4 城市 × 4 产品 多维分析
下面是一个完整工作流示例,把本课所有进阶特性串起来。
▶ 示例:多维销售数据综合可视化
# ============================================
# 4 城市 × 4 产品 多维销售分析
# 功能:分面 + 趋势线 + 注释 + 多图组合
# ============================================
library(ggplot2)
library(dplyr)
library(tidyr)
library(patchwork)
# 1. Prepare data
sales <- expand_grid(
city = c("北京", "上海", "广州", "深圳"),
quarter = c("Q1", "Q2", "Q3", "Q4"),
product = c("手机", "电脑", "平板", "耳机")
) |>
mutate(sales = round(runif(64, 500, 3000)))
cat("=== 数据规模:", nrow(sales), "行 ===\n")
print(head(sales, 5))
# 2. 图 1:4 城市总体趋势(基础)
p1 <- ggplot(sales, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
labs(title = "4 城市总销售趋势", color = "城市") +
theme_minimal() +
theme(legend.position = "bottom")
# 3. 图 2:按城市分面(facet_wrap)
p2 <- ggplot(sales, aes(x = quarter, y = sales, color = product, group = product)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
facet_wrap(~ city, nrow = 2) +
labs(title = "各城市产品销售", color = "产品") +
theme_minimal() +
theme(legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1))
# 4. 图 3:按城市分面(按产品堆叠)
p3 <- ggplot(sales, aes(x = quarter, y = sales, fill = product)) +
geom_col(position = "stack") +
facet_wrap(~ city, nrow = 2) +
labs(title = "各城市产品堆叠销售", fill = "产品") +
scale_fill_brewer(palette = "Set2") +
theme_minimal() +
theme(legend.position = "bottom")
# 5. 图 4:趋势线 + 原始点
p4 <- ggplot(sales, aes(x = quarter, y = sales, color = city, group = city)) +
geom_point(alpha = 0.3) +
geom_smooth(method = "lm", se = TRUE) +
labs(title = "线性趋势线 + 置信区间") +
theme_minimal() +
theme(legend.position = "bottom")
# 6. 图 5:箱线分布
p5 <- ggplot(sales, aes(x = product, y = sales, fill = product)) +
geom_boxplot() +
labs(title = "各产品销售分布") +
theme_minimal() +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1))
# 7. 图 6:热图(geom_tile)
sales_summary <- sales |>
group_by(city, product) |>
summarise(total = sum(sales), .groups = "drop")
p6 <- ggplot(sales_summary, aes(x = city, y = product, fill = total)) +
geom_tile() +
geom_text(aes(label = total), color = "white") +
labs(title = "城市 × 产品 总销售热图", fill = "总销售") +
scale_fill_gradient(low = "lightblue", high = "darkred") +
theme_minimal()
# 8. 多图组合(patchwork)
combined <- (p1 + p2) / (p3 + p4) / (p5 + p6) +
plot_annotation(
title = "4 城市 × 4 产品 销售综合分析",
subtitle = "数据模拟 | ggplot2 多图展示"
)
print(combined)
# 9. 保存为高分辨率 PNG
ggsave("multi_dimension_analysis.png", combined,
width = 16, height = 18, dpi = 300)
cat("\n=== 综合图已保存:multi_dimension_analysis.png ===\n")
预期输出:6 张子图组合的大图(用 patchwork 拼成 3×2 网格)。
❓ 常见问题
facet_wrap(~ var) 一维分面(自动换行),facet_grid(rows ~ cols) 二维分面(行列固定)。method = "lm"。theme(strip.background = element_blank())(去背景)+ theme(strip.text = element_text(color = "red"))(改文字色)。p1 + p2 并排,p1 / p2 上下,(p1 + p2) / (p3 + p4) 2x2。复杂布局用 plot_layout(ncol = 2)。ggrepel 包的 geom_text_repel() / geom_label_repel(),自动避开数据点。coord_flip 90 度旋转(横向图),coord_polar 转极坐标(饼图/玫瑰图)。📖 小节
- ggplot2 7 层语法:数据/美学/几何/统计/坐标/分面/主题
- 图层叠加用
+,从下到上顺序叠加 - 分面:
facet_wrap(~ var)一维 /facet_grid(rows ~ cols)二维 geom_smooth()自动加趋势线(LOESS/lm)stat_summary()加均值/误差线等统计图层- 坐标变换:
coord_flip翻转 /coord_polar极坐标 /scale_y_log10log 变换 - 注释:
geom_text数据标签 /annotate手动标注 /ggrepel自动避让 - 多图组合:
patchwork包(推荐)/cowplot包 - ggplot2 进阶 = 图层组合艺术——把简单图层叠成复杂图
📝 作业
-
基础题:构造 1 个数据框(30 行 4 列:x/y/group/category),用
facet_wrap(~ group)画散点图,验证分面效果;再用facet_grid(group ~ category)画二维分面。 -
基础题:用
mtcars,画wtvsmpg散点 +geom_smooth(method = "lm")趋势线 +geom_smooth(method = "lm", formula = y ~ poly(x, 2), color = "red")二次曲线,对比两种拟合。 -
基础题:画 1 个柱状图(5 个类别),用
coord_flip()转横向,再用coord_polar()转饼图,对比差异。 -
进阶题:构造 3 维数据(x/y/group),用 ggplot2 画 3 张分面图(
facet_wrap),最后用patchwork拼成 1x3 横向组合图,加总标题。 -
挑战题:综合挑战——画 1 张"出版级"组合图:① 用
mtcars4 维数据(wt/mpg/cyl/hp)② 用facet_wrap(~ cyl)分面(按气缸数)③ 加 LOESS 趋势线 ④ 用ggrepel标出最高/最低的 3 辆车 ⑤ 用patchwork加 1 个箱线图辅助 ⑥ 保存为高分辨率 PNG。