分面将同一种图形按分类变量拆成多个面板。所有面板共享图层设计,因此读者可以把注意力放在组间差异,而不必逐张理解不同的绘图规则。
facet_wrap():一个变量铺成多个面板
facet_wrap() 适合按一个变量拆分,并让面板自动换行排列:
library(ggplot2)
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~class)
使用 nrow 或 ncol 控制布局:
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() 中某些行列组合可能没有数据。空面板有时正好揭示实验设计缺口,有时则只是浪费空间;是否保留应根据展示目的决定,而不是一律删除。
分面的核心价值是让每个面板遵循相同的视觉规则。布局和自由坐标轴都应服务于比较任务,而不只是把一张拥挤的图拆散。