ggplot2介绍

图形语法

基于 Wilkinson (2005) 创造的图形语法 (The Grammar of Graphics), Wickham (2009) 创造了分层图形语法 (Layered Grammar of Graphics). 其定义一个 plot 的组成部分:

  • 默认的数据集 (dataset) 和美学映射 (aesthetic mapping)
  • 每个美学映射对应的标度 (scale)
  • 一个或多个图层 (layer),每个图层包含一个几何对象 (geometric object), 一个统计变换 (statistical transformation), 一个位置调整 (position adjustment), 以及 (可能存在的) 各自的数据集和美学映射
  • 坐标系 (coordinate system)
  • 分面 (facet)

其中 aesthetic mapping 指定 dataset 中的变量在图中扮演的角色, 如位置, 颜色, 形状, 大小等. 而 scale 则决定如何将它们的值映射在图上, 如将位置值映射到对数坐标轴上, 将颜色值映射到某个调色盘上等. 最重要的是 layer, 其决定了图中元素的类型, 如点, 线等.

ggplot 示例

其实以 ggplot() 开头只是为了创建一个 ggplot 对象, 使得在其上可以进行自定义的 + 运算符. 在 ggplot2 >= 4.0.0 中, ggplot() 创建一个 S7 对象.

此外, ggplot() 也提供了一个全局设置的机会, 如设置默认的数据集和美学映射. 之后的每个图层都将继承这些设置.

Aesthetic Mapping

美学映射 (aesthetic mapping) 用于指定数据中的变量在图中对应什么, 如可以指定某列作为 x/y 轴坐标, 另一列作为点的颜色. 常用的包括:

  • 位置 (x, y): 坐标轴上的位置
  • 颜色 (color): 点/线的颜色
  • 填充 (fill): 内部的填充色
  • 形状 (shape): 形状
  • 大小 (size): 大小
  • 透明度 (alpha): 透明度

ggplot 将各种类型的图所需要的属性名称高度统一, 例如 x 和 y 总表示 x/y 轴坐标, color 总表示点/线的颜色, fill 总表示某种封闭结构内部的填充色. 这使得不同图层可以共用美学映射来简化代码.

ggplot 中的美学映射将 dataset 中的各列映射为某种视觉元素. 通常这个 dataset 是 tidy 的, 因此相当于是为其中每个 observation 指定不同的属性 (如之前为每个点按类别指定不同的颜色). 如果希望为所有 observation 指定同样的属性, 例如将所有点的颜色设置为红色, 可以直接在图层中直接指定一个常量值 (而不是在 aes() 中指定):

Layer

有时候, 我们需要在同一张图上绘制多个元素, 例如同时绘制点和线. ggplot 从中抽离出图层的概念.

ggplot 的一个图层基本相当于其他绘图系统中的一句绘图命令. 例如, 也可以在图层中指定数据集和美学映射:

这就相当于 base R 中的 plot(x = mtcars$wt, y = mtcars$mpg). 因此, 可以说图层是 ggplot 绘图的基本单位.

Geometric Object & Statistical Transformation

有些图需要从数据中计算出一些统计量, 例如箱线图需要计算出四分位数. 由于这种可能存在的计算是图层级别的 (只有箱线图需要计算分位数), ggplot 将这样的计算作为每个图层自己的特征.

ggplot 中的每个图层都由一个几何对象 (geometric object) 和一个统计变换 (statistical transformation) 构成. 其中几何对象表示该图层呈现数据的形式, 而统计变换则表示该图层需要进行的计算. 例如, 刚刚 geom_point() 的几何对象名为 "point" (画一些点), 而统计变换名为 "identity" (不进行任何计算); geom_boxplot() 的几何对象名为 "boxplot" (画一些箱子), 而统计变换名为 "boxplot" (计算分位数). 后者的几何对象和统计变换同名只是因为它们都是箱线图专用的.

图层中二者的地位是平等的, 因此创建一个图层有两种方式:

  • 使用 geom_*() 函数指定几何对象, 使用 stat 参数指定统计变换, 例如 geom_point(), geom_line(), geom_boxplot().
  • 使用 stat_*() 函数指定统计变换, 使用 geom 参数指定几何对象, 例如 stat_identity(), stat_boxplot().

ggplot 以默认参数的形式自动为 geom/stat 推断其缺失的另一半.

大多数时候使用 geom_*() 即可, 但 stat_*() 也有其用处, 比如我我们希望利用 geom_boxplot 计算出的分位数, 但将其展现为 errorbar 的形式. (geom_errorbar() 也是 ggplot2 提供的一种图层类型)

after_stat()

有时候, 我们希望利用统计变换计算出的值来绘图. 例如, 我们希望将概率密度图中的密度放缩到 [0, 1] 之间.

由于密度是 geom_density() 图层在自己的统计变换中计算的, 因此无法直接对其进行操作. 对于这种需求, ggplot 提供了 after_stat() 函数, 允许我们在图层中访问统计变换计算出的值. geom_density() 计算出的密度在内部被命名为 density, 因此我们可以通过 after_stat(density) 来访问它.

Scale

Aesthetic mapping 只是指定了 dataset 中各个变量对应图中哪个视觉元素, 而 scale 决定变量的值如何映射为图中展现的值.

对于 aes() 中指定的每个美学映射, 都需要有一个对应的 scale. 未指定时, ggplot 都会自动为其创建一个默认的 scale. 例如, 对于 aes(x = wt) (数值型变量), ggplot 会创建一个 scale_x_continuous() 来将 wt 的值映射到 x 轴上. 对于 aes(color = as.factor(cyl)) (因子型变量), ggplot 会创建一个 scale_color_discrete() 来将 cyl 的值映射到颜色上.

具体而言, scales 分为两大类:

  • position scales
    • continuous position scales
    • discrete position scales
    • date/time position scales
  • other Scales
    • color scales
      • continuous color scales
      • discrete color scales
    • size scales
    • shape scales
    • fill scales
    • alpha scales

Position Scales

position scales 包括

  • continuous position scales: scale_x_continuous(), scale_y_continuous()
  • discrete position scales: scale_x_discrete(), scale_y_discrete()

其中 x/y 仅仅表示该 scale 作用于 x/y 轴, 用法相同. 而 continuous/discrete 则表示该 scale 作用于连续/离散变量, 用法 (参数) 略有不同. 下介绍一些常用参数.

数据范围

  • limits: 控制坐标轴显示的数据范围,通常接受一个长度为 2 的向量
  • expand: 控制坐标轴两端与数据边际之间的留白

由于

  • 超过 limits 的数据点会被设为 NA
  • expand 的计算依赖于数据范围

因此这两个设置都可以视为对”如何将数据映射/展示到坐标轴上”的调整, 而不是对坐标轴本身的调整. 这也是它们被归类在 position scales 中的原因.

刻度标签

  • breaks/minor_breaks: 控制主/次刻度, 接受一个数值向量或一个函数
  • labels: 控制刻度标签, 接受一个字符向量或一个函数

其被归为 position scales 的原因是它们是对数据范围的一种视觉辅助, 依旧属于”如何将数据映射/展示到坐标轴上”的范畴.

轴变换

  • trans: 对坐标轴进行数学变换,接受一个字符串 (如 “log10”) 或一个 transformation object (如 scales::log_trans())

显然轴变换也是对”如何将数据映射/展示到坐标轴上”的调整, 因此被归为 position scales.

双轴图

scale_x_continuous()/scale_y_continuous() 的一个特殊参数是 sec.axis, 用于创建双轴图.

Hadley Wickham 本人并不提倡使用双轴图 (如双 y 轴图), 详见 stackoverflow 上的回答. 因此在很长一段时间内 ggplot2 中完全无法创建双轴图. 直到 ggplot2 2.2.0 中引入了 sec.axis 参数才打破这一限制.

尽管 ggplot2 向用户需求妥协了, 但其双轴图依然实现得极为别扭, 需要用户手动进行各种变换来使得两个轴的刻度对齐. 因此, 推荐使用 ggh4x 包提供的 help_secondary() 函数实现双轴图.

Other Scales

常见的 mapping 中除了位置 (x/y) 以外, 还有颜色 (color), 填充 (fill), 形状 (shape), 大小 (size), 透明度 (alpha) 等. 这里我们以 color scale 为例介绍.

color scale 也包括 continuous color scale 和 discrete color scale 两种类型, 分别对应 scale_color_continuous()scale_color_discrete().

与 position scales 相比, 其只是表现形式不同 (在图上成为图例而不是坐标轴) 但本质都是以”刻度”的形式展示数据值, 因此其参数也大同小异.

例如, 其也有 limits, breaks, labels 等参数, 用法与 position scales 中的相同.

color scale 接受一个特殊参数: palette (调色盘)

Guides

scales 将变量的值展示在图上时, 根据对应 mapping 的不同, 可能成为坐标轴 (position scales) 或图例等 (other scales). 这些统称为 guides.

从哲学角度看, guides 可以视作是 scales 的逆映射, 其将图上的坐标轴或图例 (通过人眼) 映射回变量的值. 如, 在 position scales 中, guides 将坐标轴上的刻度标签映射回数据中的数值; 在 color scales 中, guides 将图例中的颜色映射回数据中的类别.

可以在设置 scale 的时候传入 guide 参数来调整其外观, 如

除了 guide_axis()guide_legend() 以外, ggplot 还提供了 guide_colorbar() 对应 continuous color scales, guide_none() 对应不显示 guides, 等等.

labs() & lims()

在对 scale 的控制中, 常见的需求是控制标题 (轴标题/图例标题) 和控制范围 (x/y轴范围). ggplot 为此提供了 labs()lims() 函数.

以下是一个 labs() 的使用示例. 注意其也可以控制图的标题.

lims() 的使用方法类似. 注意其也会将超出范围的值变为 NA.

after_scale()

与 layer 的统计变换会在内部计算出一些值一样, scale 也会在内部计算出一些值, 如 color scale 会计算出每个颜色对应的 RGB 值.

有时我们希望使用这些值, 如在密度图中将填充色设为淡化的轮廓颜色.

after_stat() 类似, ggplot 提供了 after_scale() 函数来访问这些值.

Coordinate System

position scales 只负责将变量值映射到坐标轴上. 因此, 其不能控制坐标轴在图上的摆放方式.

ggplot 提供了坐标系以完成这个任务. 坐标系分为两大类

  • linear coordinate systems
    • coord_cartesian()
    • coord_flip()
    • coord_fixed()
  • non-linear coordinate systems
    • coord_polar()
    • coord_trans()
    • coord_map()/coord_quickmap()/coord_sf()

这里我们仅介绍两种最常用的坐标系: coord_cartesian()coord_polar()

coord_cartesian()

coord_cartesian() (笛卡尔坐标系) 是 ggplot 的默认坐标系. 对于没有显式指定坐标系的图, ggplot 会自动添加一个 coord_cartesian().

ggplot 提供了一系列参数以对 coord_cartesian() 进行微调

注意这里的 xlim/ylim 与 scale_x_continuous()/scale_y_continuous() 中不同. 前者只是调整坐标轴显示的范围, 不会丢弃超出范围的数据, 后者则会将超出范围的数据设为 NA.

coord_polar()

coord_polar() (极坐标系) 将笛卡尔坐标系转换为极坐标系.

coord_polar() 最常见的还是创建饼图. 其由堆叠条形图变化而来.

Facet

除了将类别变量展示为颜色/填充/形状外, 还可以根据分类变量的值将图分为多个小块. 分面提供了这样的功能.

上述例子中, facet_wrap(~ cyl) 根据 cyl 的值将图分为三个小块, 分别展示 cyl = 4, cyl = 6, cyl = 8 的数据.

ggplot 还提供了 facet_grid() 来同时根据两个分类变量进行分面.

没有指定分面时, ggplot 自动添加 facet_null() (不分面). facet 也是 ggplot 图的一个必要组成部分.

facet_*() 中最重要的参数是 scales, 用于控制所有面板中位置刻度是否统一

Theme

theme() 用于设置 ggplot 的整体外观. ggplot 的理念是, 先使用前述的声明式语法 (declarative syntax) 来指定数据该如何展示在图中, 再使用 theme 系统控制图中每个部分如何渲染其外观.

theme() 的参数非常多, 可以控制图中几乎每个部分的外观. 详见其文档.

除了使用 theme() 来单独设置每个部分的外观以外, ggplot 还提供了一些预设的主题函数, 如 theme_bw(), theme_minimal(), theme_classic() 等.

拼图与保存

ggsave()

要将 ggplot2 图片保存到文件, 可以使用 ggsave() 函数

patchwork

有时候, 我们需要将多个不同的 ggplot2 图拼成一个大图, 此时可以使用 patchwork 包. 其基本用法如下

patchwork 继承了 ggplot2 的设计哲学, 可以直接将 ggplot 对象相加来拼图

如果希望其竖向拼接, 可以使用 / 而不是 +:

与 ggplot2 一样, patchwork 拼成的图也可以使用 ggsave() 保存.

plot_layout()

可以添加一个 plot_layout() 来控制如何放置子图.

plot_layout() 的另一个实用参数是 guides:

plot_annotation()

plot_annotation() 可用于控制拼图的标题, 编号等:

Cheatsheet

ggplot2 cheatsheet

练习

练习 1

使用 ggplot2::mpg 完成以下任务:

  • 绘制 displhwy 的散点图
  • drv 映射到颜色
  • 设置点的透明度为 0.7
  • 添加一条线性拟合曲线,不显示置信区间
  • 设置 x 轴刻度为 2 到 7
  • 设置标题、副标题和坐标轴标签
  • 使用 theme_minimal()

练习 2

继续使用 ggplot2::mpg 完成以下任务:

  • 绘制 class 的柱状图
  • drv 映射到填充颜色
  • 使用 position = "fill" 展示各 class 中不同 drv 的比例
  • 将 y 轴标签显示为百分比
  • 翻转坐标轴
  • 将图例放到底部,并设置图例标题
  • 使用 theme_bw()

练习 3

使用 ggplot2::diamonds 完成以下任务:

  • 绘制 carat 的直方图
  • 使用 after_stat(density) 将 y 轴改为密度
  • 叠加一条密度曲线
  • cut 分面
  • 仅显示 carat 在 0 到 3 之间的部分
  • 使用 theme_classic()
  • 设置标题和 x / y 轴标签
Back to top

References

Wickham, Hadley. 2009. “A Layered Grammar of Graphics.” Journal of Computational and Graphical Statistics.
Wilkinson, Leland. 2005. The Grammar of Graphics. 2nd ed. Statistics and Computing. Springer.