R: R ggplot2 进阶:图层叠加与分面系统

最后更新:2026-08-26

上一课我们学了 ggplot2 三件套:ggplot() + aes() + geom_xxx(),能画 5 种基础图。这一课进入ggplot2 进阶——掌握"图层叠加"和"分面系统",画任意复杂图。这是 ggplot2 真正强大的地方。

读完这一课你能画:多图层组合图、自动分面、统计叠加、坐标变换、注释标注——ggplot2 能做的 90% 都在这一课。

1. 你将学到



2. 一个多维分析的故事

(1) 痛点:4 城市 4 季度 4 产品怎么画?

Bob要展示4 城市 × 4 季度 × 4 产品的销售数据——3 维信息一张图能装下吗?

(2) ggplot2 的解法

R
# 用 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 层结构

100%
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) 图层叠加示例

R
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)= 把数据按某个变量拆成多张子图

R
# 没分面:1 张图
ggplot(sales, aes(x, y)) + geom_point()

# 分面后:4 张小图
ggplot(sales, aes(x, y)) + geom_point() + facet_wrap(~ category)

(2) facet_wrap() 一维分面

R
# 按 city 分面(4 张子图)
ggplot(sales_long, aes(x = quarter, y = sales, color = city, group = city)) +
  geom_line() +
  facet_wrap(~ city) +
  labs(title = "各城市季度销售")
R
# 控制分面布局:nrow / ncol
facet_wrap(~ city, nrow = 2)   # 2 行
facet_wrap(~ city, ncol = 4)   # 4 列

(3) facet_grid() 二维分面

R
# 按 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) 分面扩展

R
# 自由缩放(每张子图独立坐标)
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) 自动统计

R
# 自动加均值点 + 误差线
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) 实战:均值 + 误差线

R
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) 基础用法

R
# 自动加 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) 实战

R
# 按组分多条线
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() 翻转

R
# 横向柱状图
ggplot(df, aes(x = city, y = sales)) +
  geom_col() +
  coord_flip()  # x/y 互换

(2) coord_polar() 极坐标

R
# 饼图
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() 等比例

R
# 散点图 X/Y 等比例
ggplot(mpg, aes(cty, hwy)) +
  geom_point() +
  coord_fixed(ratio = 1)  # 1:1 比例

(4) 坐标轴变换

R
# 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()

R
# 数据点旁加标签
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() 手动注释

R
# 标记关键点
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) 箭头

R
library(ggrepel)

# 自动避让的标签
ggplot(df, aes(x, y, label = name)) +
  geom_point() +
  geom_text_repel()  # 不重叠的标签


9. 多图组合

(1) patchwork 包

R
# 安装
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 包

R
library(cowplot)
plot_grid(p1, p2, p3, p4, ncol = 2, labels = c("A", "B", "C", "D"))


10. 完整示例:4 城市 × 4 产品 多维分析

下面是一个完整工作流示例,把本课所有进阶特性串起来。

▶ 示例:多维销售数据综合可视化

R 📖 仅展示
# ============================================
# 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")
逻辑代码 63 行(超过 40 行限制,仅展示)

预期输出:6 张子图组合的大图(用 patchwork 拼成 3×2 网格)。


❓ 常见问题

Q facet_wrap 和 facet_grid 区别?
A facet_wrap(~ var) 一维分面(自动换行),facet_grid(rows ~ cols) 二维分面(行列固定)。
Q geom_smooth 默认是啥?
A 默认 LOESS 平滑(n < 1000)或 GAM(n ≥ 1000)。要线性回归加 method = "lm"
Q 怎么去掉分面标签的背景?
A theme(strip.background = element_blank())(去背景)+ theme(strip.text = element_text(color = "red"))(改文字色)。
Q patchwork 怎么用?
A p1 + p2 并排,p1 / p2 上下,(p1 + p2) / (p3 + p4) 2x2。复杂布局用 plot_layout(ncol = 2)
Q 怎么加文字标签不重叠?
Aggrepel 包的 geom_text_repel() / geom_label_repel(),自动避开数据点。
Q coord_polar 和 coord_flip 啥区别?
A coord_flip 90 度旋转(横向图),coord_polar 转极坐标(饼图/玫瑰图)。

📖 小节


📝 作业

  1. 基础题:构造 1 个数据框(30 行 4 列:x/y/group/category),用 facet_wrap(~ group) 画散点图,验证分面效果;再用 facet_grid(group ~ category) 画二维分面。

  2. 基础题:用 mtcars,画 wt vs mpg 散点 + geom_smooth(method = "lm") 趋势线 + geom_smooth(method = "lm", formula = y ~ poly(x, 2), color = "red") 二次曲线,对比两种拟合。

  3. 基础题:画 1 个柱状图(5 个类别),用 coord_flip() 转横向,再用 coord_polar() 转饼图,对比差异。

  4. 进阶题:构造 3 维数据(x/y/group),用 ggplot2 画 3 张分面图(facet_wrap),最后用 patchwork 拼成 1x3 横向组合图,加总标题。

  5. 挑战题:综合挑战——画 1 张"出版级"组合图:① 用 mtcars 4 维数据(wt/mpg/cyl/hp)② 用 facet_wrap(~ cyl) 分面(按气缸数)③ 加 LOESS 趋势线 ④ 用 ggrepel 标出最高/最低的 3 辆车 ⑤ 用 patchwork 加 1 个箱线图辅助 ⑥ 保存为高分辨率 PNG。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏