ggplot2 把图形拆成数据、映射和图层。最常见的困惑不是某个参数叫什么,而是它应该放在 aes() 内还是外,以及一个图层应当使用全部数据还是局部数据。
映射与固定外观
aes() 描述变量如何映射到颜色、形状、大小等视觉属性。属性随数据变化时放在 aes() 内:
library(ggplot2)
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point()
这里不同 class 得到不同颜色,并生成相应图例。
固定外观写在 aes() 外:
ggplot(mpg, aes(displ, hwy)) +
geom_point(color = "blue", size = 3, shape = 16)
如果误将固定颜色写入映射:
ggplot(mpg, aes(displ, hwy, color = "blue")) +
geom_point()
"blue" 会被当成只有一个取值的分类变量,图中出现图例,实际颜色则来自离散色标。判断方法很简单:属性是否来自数据列?是则放进 aes(),否则放在外面。
两种方式可以组合:
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point(shape = 21, fill = "white", size = 3)
color 随类别变化,fill、shape 和 size 固定。
位置调整
图层的 position 决定几何对象发生重叠时如何摆放:
| 值 | 效果 | 常见场景 |
|---|---|---|
"identity" |
保持原始位置 | 透明图层叠加 |
"stack" |
堆叠 | 条形图 |
"fill" |
按比例堆叠 | 百分比构成 |
"dodge" |
并排错开 | 分组条形图 |
"jitter" |
添加微小扰动 | 重叠散点 |
例如,让两组密度图在原位置叠加:
ggplot(mpg, aes(hwy, fill = drv)) +
geom_density(position = "identity", alpha = 0.25)
position = "identity" 不会自动解决遮挡,因此通常与透明度配合使用。
填充、边框与透明度
对同时支持内部填充和边框的几何对象,fill = NA 表示内部透明:
ggplot(mpg, aes(displ, hwy)) +
geom_point(shape = 21, fill = NA, color = "black", size = 3)
点形状 21–25 可以分别控制 fill 与 color。alpha 的范围是 0 到 1,数值越小越透明:
ggplot(mpg, aes(hwy, fill = class)) +
geom_density(alpha = 1 / 5)
透明度有助于观察重叠,但过低会让稀疏观测几乎消失。它解决的是遮挡,不替代合理的分组和位置设计。
点形状
geom_point() 使用 shape 控制符号。ggplot2 可接受 0–25 的内置形状编号,也可以使用单个字符。
查看图片ggplot2 点形状编号

将组别映射到形状:
set.seed(42)
points <- data.frame(
x = rep(1:5, each = 3),
y = rnorm(15),
group = factor(rep(letters[1:3], times = 5))
)
ggplot(points, aes(x, y, shape = group, color = group)) +
geom_point(size = 4)
使用手动标度可以明确每个类别的符号:
ggplot(points, aes(x, y, shape = group, color = group)) +
geom_point(size = 4) +
scale_shape_manual(values = c(a = 15, b = 17, c = 19))
形状在黑白打印或色觉差异场景中尤其有用。类别很多时,不应把 20 多种形状全部塞进一张图;即使技术上可以手动指定,读者也很难辨认。
默认离散 shape 标度只选择少量容易区分的形状。超出默认容量时,可以显式指定:
many_groups <- data.frame(
x = rep(1:5, 4),
y = rnorm(20),
group = factor(rep(letters[1:10], each = 2))
)
ggplot(many_groups, aes(x, y, shape = group, color = group)) +
geom_point(size = 3) +
scale_shape_manual(values = 0:9)
但当类别数量本身已经妨碍阅读时,更好的选择通常是分面、筛选重点类别,或重新设计比较方式。
使用多个图层突出子集
每个 geom 都可以拥有自己的 data。先画完整数据,再只对目标子集增加图层,可以在保留上下文的同时突出重点:
library(dplyr)
two_seaters <- filter(mpg, class == "2seater")
ggplot(mpg, aes(displ, hwy)) +
geom_point(color = "grey70") +
geom_point(
data = two_seaters,
color = "red"
) +
geom_point(
data = two_seaters,
shape = 1,
size = 3,
color = "red"
)
第一层交代总体分布,第二层改变目标点颜色,第三层增加外圈。异常值、关注群体和特定类别都可以采用这一模式。
如果子集条件较复杂,先在图外生成一个命名对象,比在多个图层中重复筛选条件更容易核查。图层叠加的重点不是“多画一次”,而是让每层承担明确的视觉职责。