写在前面
本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github,欢迎大家参与贡献,详细信息见:
Books-zh-cn 项目介绍:
Books-zh-cn:开源免费的中文书籍社区
r4ds-zh-cn Github 地址:
https://github.com/Books-zh-cn/r4ds-zh-cn
r4ds-zh-cn 网站地址:
https://books-zh-cn.github.io/r4ds-zh-cn/
目录
- 
11.4 Scales 
- 
11.5 主题 
- 
11.6 布局 
- 
11.7 总结 
11.4 Scales
11.4.4 替换 scale
您不仅可以对细节进行一些调整,还可以完全替换 scale。 您大多有可能要切换两种类型的 scales:连续位置 scales 和颜色 scales。 幸运的是,相同的原则适用于所有其他美学,因此,一旦掌握了位置和颜色,您就可以快速拿起其他 scale 替代品。
绘制变量的转换非常有用。 例如,如果我们 log 转换它们,则更容易看到 carat 和 price 之间的确切关系:
# Left
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_bin2d()
# Right
ggplot(diamonds, aes(x = log10(carat), y = log10(price))) +
  geom_bin2d()
但是,这种转换的缺点是轴现在用转换值标记,因此很难解释图。 与其在美学映射中进行转换,我们可以用 scale 来进行。 这在视觉上是相同的,除了轴以原始数据 scale 标记。
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_bin2d() + 
  scale_x_log10() + 
  scale_y_log10()
经常自定义的另一个 scale 是 color。 默认的分类 scale 挑选围绕色轮均匀间隔的颜色。 有用的替代方法是手工调整的 ColorBrewer scales,可以为具有常见的色盲类型的人提供更好的工作。 下面的两个图看起来相似,但是红色和绿色的阴影有足够的差异,即使有红绿色盲的人也可以区分右边的点1。
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv))
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  scale_color_brewer(palette = "Set1")
不要忘记改善可访问性的简单技术。 如果只有几种颜色,则可以添加冗余形状映射。 这也将有助于确保您的绘图在黑白中可以解释。
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv, shape = drv)) +
  scale_color_brewer(palette = "Set1")
ColorBrewer scales 在 https://colorbrewer2.org/ 上在线记录,并通过 Erich Neuwirth 的 RColorBrewer 软件包在 R 中提供。Figure 11.1 显示了所有调色板的完整列表。 连续(顶部)和离散(底部)调色板特别有用,如果您的分类值被排序或具有"中间"值。 这通常会出现在如果您使用 cut() 将连续变量变成一个分类变量。

Figure 11.1: 所有 ColorBrewer scales
当您在值和颜色之间具有预定义的映射时,请使用 scale_color_manual()。 例如,如果我们将 presidential party 映射给 color,我们希望将红色的标准映射用于 Republicans 和蓝色用于 Democrats。 分配这些颜色的一种方法是使用 hex 颜色代码:
presidential |>
  mutate(id = 33 + row_number()) |>
  ggplot(aes(x = start, y = id, color = party)) +
  geom_point() +
  geom_segment(aes(xend = end, yend = id)) +
  scale_color_manual(values = c(Republican = "#E81B23", Democratic = "#00AEF3"))
对于连续颜色,您可以使用内置 scale_color_gradient() 或 scale_fill_gradient()。 如果您有一个 diverging scale,则可以使用 scale_color_gradient2()。 这使您可以给出例如正值和负值不同的颜色。 如果您想区分均值以上或以下的点时,这也很有用。
另一个选择是使用 viridis color scales。 设计师 Nathaniel Smith 和 Stéfan van der Walt 精心量身定制的连续配色方案,这些配色方案对各种形式的色盲以及颜色和黑色和白色的感知均匀的人都可以感知。 这些 scales 可作为连续(continuous,c),离散(discrete,d)和 ggplot2 中的 binned(b)调色板提供。
df <- tibble(
  x = rnorm(10000),
  y = rnorm(10000)
)
ggplot(df, aes(x, y)) +
  geom_hex() +
  coord_fixed() +
  labs(title = "Default, continuous", x = NULL, y = NULL)
ggplot(df, aes(x, y)) +
  geom_hex() +
  coord_fixed() +
  scale_fill_viridis_c() +
  labs(title = "Viridis, continuous", x = NULL, y = NULL)
ggplot(df, aes(x, y)) +
  geom_hex() +
  coord_fixed() +
  scale_fill_viridis_b() +
  labs(title = "Viridis, binned", x = NULL, y = NULL)
请注意,所有颜色 scales 都有两个类型:scale_color_*() 和 scale_fill_*() 分别用于 color 和 fill 美学(英式和英式拼写都可以使用 color scales)。
11.4.5 放大
有三种控制图限制(limits)的方法:
- 
调整绘制哪些数据。 
- 
在每个 scale 中设置 limits。 
- 
在 coord_cartesian()中设置xlim和ylim。
我们将在一系列绘图中演示这些选项。 左侧的绘图显示了发动机尺寸和燃油效率之间的关系,并通过驱动器类型进行着色。 右侧的图显示了相同的变量,但是用子集绘制的数据。 子集数据影响了 x 和 y scales 以及平滑曲线。
# Left
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  geom_smooth()
# Right
mpg |>
  filter(displ >= 5 & displ <= 6 & hwy >= 10 & hwy <= 25) |>
  ggplot(aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  geom_smooth()
让我们将它们与下面的两个图进行比较,其中左侧的绘图设置了单个 scales 上的 limits,右侧的绘图在 coord_cartesian() 中设置它们。 我们可以看到,降低 limits 等效于子集。 因此,要放大图块的区域,通常最好使用 coord_cartesian()。
# Left
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  geom_smooth() +
  scale_x_continuous(limits = c(5, 6)) +
  scale_y_continuous(limits = c(10, 25))
# Right
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv)) +
  geom_smooth() +
  coord_cartesian(xlim = c(5, 6), ylim = c(10, 25))
另一方面,如果要扩大 limits,例如,以匹配不同图的尺度,则通常在单个 scales 上设置 limits 通常更有用。 例如,如果我们提取两类汽车并分别绘制它们,则很难比较绘图,因为所有三个 scales(x-axis,y-axis,color aesthetic)都有不同的范围。
suv <- mpg |> filter(class == "suv")
compact <- mpg |> filter(class == "compact")
# Left
ggplot(suv, aes(x = displ, y = hwy, color = drv)) +
  geom_point()
# Right
ggplot(compact, aes(x = displ, y = hwy, color = drv)) +
  geom_point()
克服此问题的一种方法是在多个图上共享 scales,并使用完整数据的 limits 训练 scales。
x_scale <- scale_x_continuous(limits = range(mpg$displ))
y_scale <- scale_y_continuous(limits = range(mpg$hwy))
col_scale <- scale_color_discrete(limits = unique(mpg$drv))
# Left
ggplot(suv, aes(x = displ, y = hwy, color = drv)) +
  geom_point() +
  x_scale +
  y_scale +
  col_scale
# Right
ggplot(compact, aes(x = displ, y = hwy, color = drv)) +
  geom_point() +
  x_scale +
  y_scale +
  col_scale
在这种特殊情况下,您可以简单地使用 faceting,但是此技术更普遍地有用,例如,如果您想在报告的多个页面上传播 绘图。
11.4.6 练习
- 
为什么以下代码不覆盖默认 scale? df <- tibble( x = rnorm(10000), y = rnorm(10000) ) ggplot(df, aes(x, y)) + geom_hex() + scale_color_gradient(low = "white", high = "red") + coord_fixed()
- 
每个 scale 的第一个参数是什么? 与 labs()相比如何?
- 
通过以下方式更改 presidential terms 的显示 a. 结合自定义 colors 和 x 轴 breaks 这两个变量。 b. 改善 y 轴的显示。 c. 将每个 term 标记为总统的名字。 d. 添加内容丰富的绘图标签。 e. 替换 breaks 为每 4 年(这比看起来更棘手!)。 
- 
首先,创建以下图。 然后,使用 override.aes修改代码,以使图例更容易看到。ggplot(diamonds, aes(x = carat, y = price)) + geom_point(aes(color = cut), alpha = 1/20)
11.5 主题
最后,您可以用主题自定义绘图的非数据元素:
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  theme_bw()
ggplot2 包括 Figure 11.2 中所示的八个主题,theme_gray() 作为默认值。2 诸如 Jeffrey Arnold 的 ggthemes (https://jrnold.github.io/ggthemes) 之类的附加软件包中包含了更多主题。 您也可以创建自己的主题,如果您要匹配特定的公司或期刊样式。

Figure 11.2: ggplot2 内置的八个主题
也可以控制每个主题的各个组件,例如用于 y 轴的字体的大小和颜色。 我们已经看到了 legend.position 控制图例的绘制位置。 图例中还有许多其他方面可以通过 theme() 自定义。 例如,在下面的绘图中,我们改变了图例的方向,并在其周围放置了一个黑色边框。 请注意,通过 element_*() 函数,自定义图例框和主题的图标题元素。 这些函数指定了非数据组件的样式,例如,标题文本在 element_text() 的 face 参数中被加粗,而图例边框颜色 element_rect() 的 color 参数中定义。 控制 title 和 caption 位置的主题元素分别为 plot.title.position 和 plot.caption.position。 在以下图中,这些设置为 "plot",以指示这些元素与整个绘图区域对齐,而不是绘图面板(默认值)。 其他一些有用的 theme() 组件用于更改 title 和 caption 文本格式的位置。
ggplot(mpg, aes(x = displ, y = hwy, color = drv)) +
  geom_point() +
  labs(
    title = "Larger engine sizes tend to have lower fuel economy",
    caption = "Source: https://fueleconomy.gov."
  ) +
  theme(
    legend.position = c(0.6, 0.7),
    legend.direction = "horizontal",
    legend.box.background = element_rect(color = "black"),
    plot.title = element_text(face = "bold"),
    plot.title.position = "plot",
    plot.caption.position = "plot",
    plot.caption = element_text(hjust = 0)
  )
#> Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2
#> 3.5.0.
#> ℹ Please use the `legend.position.inside` argument of `theme()` instead.
有关所有 theme() 组件的概述,请参见 ?theme。ggplot2 book 也是有关主题的全部详细信息的好地方。
11.5.1 练习
- 
选择 ggthemes 软件包提供的主题,并将其应用于您制作的最后一个绘图。 
- 
使绘图的轴标签为蓝色和粗体。 
11.6 布局
到目前为止,我们讨论了如何创建和修改单个图。 如果您想以某种方式布置多个图怎么办? patchwork 软件包允许您将单独的图组合到同一图形中。 我们在本章的早期加载了此软件包。
要彼此放置两个绘图,您只需将它们互相添加。 请注意,您首先需要创建图并将其保存为对象(在下面的示例中,它们称为 p1 和 p2)。 然后,你放置它们彼此相邻通过 +。
p1 <- ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point() + 
  labs(title = "Plot 1")
p2 <- ggplot(mpg, aes(x = drv, y = hwy)) + 
  geom_boxplot() + 
  labs(title = "Plot 2")
p1 + p2
重要的是要注意,在上面的代码块中,我们没有使用 patchwork 软件包中的新函数。 相反,软件包向 + 运算符添加了新功能。
您还可以使用 patchwork 创建复杂的绘图布局。 在下面,| 将 p1 和 p3 彼此相邻,/ 将 p2 移至下一行。
p3 <- ggplot(mpg, aes(x = cty, y = hwy)) + 
  geom_point() + 
  labs(title = "Plot 3")
(p1 | p3) / p2
此外,patchwork 允许您从多个绘图中收集图例为一个通用图例,并自定义图例的位置以及图的尺寸,并在图中添加一个通用 title,subtitle,caption 等。 下面我们创建5个图。 我们已经关闭了 box plots 和 scatterplot 上的图例,并通过 & theme(legend.position = "top") 收集了 density plots 的图例绘制在顶部。 请注意,这里的使用 & 运算符而不是通常的 +。 这是因为我们正在修改 patchwork 图的主题,而不是单个 ggplots。 图例放在 guide_area() 内部的顶部。 最后,我们还定制了 patchwork 的各个组件的高度 -- guide 的高度为 1,box plots 为 3,density plots 为 2,faceted scatterplot 为 4。 Patchwork 将您分配给绘图的区域,并使用此刻度分配了您的绘图区域,并将组件放置相应地放置。
p1 <- ggplot(mpg, aes(x = drv, y = cty, color = drv)) + 
  geom_boxplot(show.legend = FALSE) + 
  labs(title = "Plot 1")
p2 <- ggplot(mpg, aes(x = drv, y = hwy, color = drv)) + 
  geom_boxplot(show.legend = FALSE) + 
  labs(title = "Plot 2")
p3 <- ggplot(mpg, aes(x = cty, color = drv, fill = drv)) + 
  geom_density(alpha = 0.5) + 
  labs(title = "Plot 3")
p4 <- ggplot(mpg, aes(x = hwy, color = drv, fill = drv)) + 
  geom_density(alpha = 0.5) + 
  labs(title = "Plot 4")
p5 <- ggplot(mpg, aes(x = cty, y = hwy, color = drv)) + 
  geom_point(show.legend = FALSE) + 
  facet_wrap(~drv) +
  labs(title = "Plot 5")
(guide_area() / (p1 + p2) / (p3 + p4) / p5) +
  plot_annotation(
    title = "City and highway mileage for cars with different drive trains",
    caption = "Source: https://fueleconomy.gov."
  ) +
  plot_layout(
    guides = "collect",
    heights = c(1, 3, 2, 4)
    ) &
  theme(legend.position = "top")
如果您想了解有关将多个图与 patchwork 相结合和布局的更多信息,我们建议您在软件包网站上查看指南:https://patchwork.data-imaginist.com。
11.6.1 练习
- 
如果您在以下绘图布局中省略括号,会发生什么。 您能解释一下为什么会发生这种情况吗? p1 <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point() + labs(title = "Plot 1") p2 <- ggplot(mpg, aes(x = drv, y = hwy)) + geom_boxplot() + labs(title = "Plot 2") p3 <- ggplot(mpg, aes(x = cty, y = hwy)) + geom_point() + labs(title = "Plot 3") (p1 | p2) / p3
- 
使用上一个练习中的三个图,重新创建以下拼布。  
11.7 总结
在本章中,您已经了解了添加图标签,例如 title,subtitle,caption 以及修改默认轴标签,使用注释将信息文本添加到图中或突出显示特定的数据点,自定义轴 scales 以及更改图的主题。 您还了解了使用简单和复杂的绘图布局在单个图中组合多个图。
到目前为止,您已经了解了如何制作多种不同类型的图以及如何使用各种技术自定义它们,但我们对 ggplot2 的介绍仅停留在表面。 如果您想对 ggplot2 有全面的了解,我们建议阅读 ggplot2: Elegant Graphics for Data Analysis。 其他有用的资源是 Winston Chang 的 R Graphics Cookbook 和 Claus Wilke 的 Fundamentals of Data Visualization。
- 
您可以使用 SimDaltonism 之类的工具模拟色盲来测试这些图像。 
- 
许多人想知道为什么默认主题具有灰色背景。 这是一个故意的选择,因为它可以将数据传递到同时使网格线可见。 白色网格线是可见的(这很重要,因为它们可以大大帮助位置判断),但是它们的视觉影响很小,我们可以轻松地将它们调出。 灰色背景使绘图与文本具有类似的印刷色彩,从而确保图形与文档的流程符合文档的流程,而不会以明亮的白色背景跳出。 最后,灰色背景创建了一个连续的颜色字段,可确保图被视为单个视觉实体。 
--------------- 本章结束 ---------------
本期翻译贡献:
- @TigerZ生信宝库
注:本文已开启快捷转载,欢迎大家转载,只需标明文章出处即可。