图层语法:ggplot2 的核心思维
ggplot2 之所以好用,是因为它把"画图"拆成了一层一层叠加的结构。你先用 ggplot() 铺上数据底座,再用加号一层一层往上加几何对象、颜色规则、标题主题——就像搭图层,每加一层都在前一层之上。
library(ggplot2)
data(mtcars)
# 最简骨架:数据 + 映射 + 一个几何对象
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point()
记住这个三段式:数据(哪个数据框)→ 映射(哪几列对应横轴纵轴颜色)→ 几何对象(画成点、柱还是线)。所有 ggplot2 的图都是在这个骨架上不断叠加,而不是另起炉灶。
ggplot2 的图是"搭"出来的:底座是数据,往上一层层叠几何对象和规则。想加什么效果,就再往后面加一个加号。
- 图层语法ggplot 铺底,加号逐层叠加 geom 与规则。
- aes把数据列映射到 x、y、颜色、分组等视觉通道。
- geom_*点用 point、柱用 col、折线用 line、分布用 boxplot。
- facet按某个分类列拆成一排小图,做多组对比。
aes 映射:把数据列绑到视觉通道
aes() 是整张图的"接线板":你在这里声明数据框的哪一列对应横轴、哪一列对应纵轴、哪一列用来决定颜色。一旦接好,后面的每一层几何对象都会自动用这套映射。
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 3)
这里 color = factor(cyl) 的意思是:按气缸数给点上色。注意 factor()——cyl 虽然是数字,但我们想把它当成"类别"着色,而不是连续渐变。数字当类别还是当连续量,是 ggplot2 新手最常混淆的一点:要分组着色就转 factor,要连续渐变就保留数字。
几何对象 geom:散点、柱状、折线怎么选
"画成什么样"由 geom_* 决定。选哪个,取决于你想展示什么关系:
| 几何对象 | 适合展示 | 典型场景 |
|---|---|---|
| geom_point | 两个连续变量的关系 | 散点、相关性 |
| geom_col / geom_bar | 类别对应的数值高低 | 各部门销售额对比 |
| geom_line | 随时间变化的趋势 | 月度访问量走势 |
| geom_boxplot | 各组数值的分布与离群点 | 不同班级成绩分布 |
| geom_smooth | 给散点加一条拟合趋势线 | 看整体走向 |
# 散点上加一条拟合趋势线,两层叠在一起
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)
一个常见错误是:把本该用柱状对比的类别关系,硬塞成散点。先想清楚"我要让读者一眼看到什么",再选对应的 geom,图才不会喧宾夺主。
颜色与分组:让图自己讲故事
颜色不只是好看,它是在表达"这几组数据有什么不同"。在 aes 里把某列映射给 color 或 fill,ggplot 会自动分配配色、并生成图例。
ggplot(mtcars, aes(x = factor(cyl), y = mpg, fill = factor(cyl))) +
geom_boxplot() +
labs(x = "气缸数", y = "油耗", fill = "气缸数")
注意区分 color 和 fill:点、线的边框用 color;柱状、箱线这类实心形状的填充用 fill。用错了,图上颜色可能纹丝不动,这是新手常踩的小坑。
分面 facet:一行小图看多组对比
当分组太多、全挤在一张图里糊成一团时,用 facet_wrap 把它拆成一排小图,每组一张,共用同一套坐标。对比起来一目了然。
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
facet_wrap(~ factor(cyl)) # 每个气缸数单独一张小图
分面特别适合"在统一尺度下看各组各自的形状"。它比在一张图里堆很多颜色点更清晰——尤其当你要比较三个以上分组时,优先考虑分面而不是堆更多颜色。
主题与标签:让图能直接放进报告
默认图离"能放进报告"还差最后一步:标题、坐标轴说明、干净的背景。这些都靠叠加 labs 和主题层来完成。
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 2) +
labs(title = "车重与油耗的关系",
x = "车重(千磅)", y = "油耗(英里/加仑)",
color = "气缸数") +
theme_minimal()
labs() 负责把所有文字标签改成人话,theme_minimal() 之类的主题一键去掉杂色背景。花一两行把标题和轴标签写清楚,图的可读性立刻上一个台阶——别让读者自己猜坐标轴是什么。
实战:一张完整的分组对比图
把全部层叠起来:散点、拟合线、分组着色、标题主题,一次成型。这就是一张可以直接贴进分析报告的图。
library(ggplot2)
data(mtcars)
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 2.5) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "不同气缸数下,车重越大油耗越低",
x = "车重(千磅)", y = "油耗(英里/加仑)",
color = "气缸数") +
theme_bw()
读图者一眼能看到三层信息:整体趋势是负相关、三种气缸数各自成团、每组都有一条自己的拟合线。图的目的不是炫技,而是把数据里的关系用最少的语言讲清楚——这张图做到了。
可视化检查清单
把 ggplot2 记成一条叠加链:数据 → aes 映射 → geom 选形 → 颜色分组 → 分面 → 主题标签。出图前对照自查:
- 横轴纵轴各自代表哪一列数据,写进 labs 了吗?
- 类别变量着色时,是否用 factor() 把数字转成了类别?
- 实心形状用的是 fill、轮廓用的是 color,有没有混用?
- 分组很多时,是不是改用了分面而不是堆颜色?
- 标题和坐标轴标签是否说清了单位(如"千磅""英里/加仑")?
- 选的 geom 和想表达的关系匹配吗?对比高低用柱、看趋势用线、看分布用箱线。
好图是减出来的:每加一层都要问自己,它帮读者看清了什么。多余的颜色和装饰,只会掩盖真正的关系。
下一篇我们回到 C++:当你想写一份能适配任意类型的通用代码,模板与泛型会帮你把重复彻底收敛掉。