03使用 facet_wrap 与 facet_grid 分面

按一个或两个分类变量拆分子图,并控制布局、共享坐标轴和跨面板比较。

2026-03-02
Rggplot2Facetingfacet_wrapfacet_grid
本章目录 · 5

分面将同一种图形按分类变量拆成多个面板。所有面板共享图层设计,因此读者可以把注意力放在组间差异,而不必逐张理解不同的绘图规则。

facet_wrap():一个变量铺成多个面板

facet_wrap() 适合按一个变量拆分,并让面板自动换行排列:

library(ggplot2)

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(~class)

使用 nrowncol 控制布局:

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(~class, ncol = 3)

当类别数量较多、没有天然的行列结构时,facet_wrap() 通常最直接。

facet_grid():行与列共同定义结构

facet_grid() 适合两个变量形成有意义的行列组合:

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_grid(drv ~ cyl)

drv 决定面板行,cyl 决定面板列。只按一个方向排列时,可以用点号表示空维度:

facet_grid(. ~ class)
facet_grid(drv ~ .)

如果两个变量之间存在实验设计或业务结构,例如 treatment × tissue,网格布局能保留这一结构。若只是想查看两个变量组合后的若干组,也可以先组合变量再使用 facet_wrap()

是否共享坐标轴

scales 控制各面板的坐标范围:

x 轴 y 轴
"fixed" 共享 共享
"free" 独立 独立
"free_x" 独立 共享
"free_y" 共享 独立

默认的固定坐标轴最适合跨面板比较绝对位置和变化幅度:

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(~class, scales = "fixed")

不同组的数值范围差异很大时,自由坐标轴可以显示各组内部结构:

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(~class, scales = "free_y")

代价是面板间的高度不再代表同一个数值范围,读者不能只凭点的位置直接比较绝对值。使用自由坐标轴时,应保留清晰的刻度,并确认问题关注的是组内模式而不是组间量级。

分面与颜色分组怎样选择

颜色映射把各组画在同一坐标系中,适合直接观察重叠与相对位置;分面减少遮挡,适合逐组检查结构。

# 同图比较
ggplot(mpg, aes(displ, hwy, color = drv)) +
  geom_point()

# 分面比较
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(~drv)

两者也可以表达不同变量:

ggplot(mpg, aes(displ, hwy, color = drv)) +
  geom_point() +
  facet_wrap(~class)

这时每个面板内仍按 drv 着色,而面板之间比较 class。只有当两个分组维度都服务于明确问题时才应叠加,否则面板和图例会同时增加阅读负担。

面板顺序与空组合

分面顺序由变量的因子 levels 控制。需要业务顺序时,先整理因子:

mpg2 <- mpg |>
  transform(
    drv = factor(drv, levels = c("f", "r", "4"))
  )

facet_grid() 中某些行列组合可能没有数据。空面板有时正好揭示实验设计缺口,有时则只是浪费空间;是否保留应根据展示目的决定,而不是一律删除。

分面的核心价值是让每个面板遵循相同的视觉规则。布局和自由坐标轴都应服务于比较任务,而不只是把一张拥挤的图拆散。