前言

上一节讲的是如何使用 cowplot 包来对图形进行排列对齐,今天要讲的是,如何使用 patchwork 包来排列图形

patchwork 包主要针对的是 ggplot2 图形,也可以是其他图像系统绘制的图形。

patchwork 以一种简单的方式对图形进行排列和组合,不论多复杂的组合图形,都能确保图形之间正确对齐

安装 patchwork

install.packages("patchwork")

导入相关模块

library(ggplot2) library(patchwork)

示例

我们主要使用如下图形进行说明

p1 <- ggplot(mtcars) geom_point(aes(mpg, disp), colour = "#7fc97f") ggtitle('Plot 1') p2 <- ggplot(mtcars) geom_boxplot(aes(gear, disp, fill = factor(gear)), show.legend = FALSE) ggtitle('Plot 2') p3 <- ggplot(mtcars) geom_point(aes(hp, wt, colour = mpg)) scale_colour_gradientn( colours = c("#66c2a5", "#fc8d62", "#8da0cb")) ggtitle('Plot 3') p4 <- ggplot(mtcars) geom_bar(aes(gear, fill = factor(gear)), show.legend = FALSE) facet_wrap(~cyl) ggtitle('Plot 4')

1. 组合图形1.1 添加图形

patchwork 使用 来连接两个图形

p1 p2

r语言数据可视化实例(R数据可视化)(1)

拼凑多个图

patch <- p1 p2 p3 patch

r语言数据可视化实例(R数据可视化)(2)

1.1.1 添加非 ggplot 图形

有时候,你可能想要添加其他类型的图片,例如,grid 系统的图形对象

p1 grid::textGrob('Some really important text')

r语言数据可视化实例(R数据可视化)(3)

或者,gridExtra 的 tableGrob

p1 gridExtra::tableGrob(mtcars[1:10, c('mpg', 'disp')])

r语言数据可视化实例(R数据可视化)(4)

此外,对于 base 绘图系统,可以通过单边公式的方式来添加

p1 ~plot(mtcars$mpg, mtcars$disp, main = 'Plot 2', col = if_else(mtcars$disp > 250, "red", "green"))

r语言数据可视化实例(R数据可视化)(5)

我们可以看到,两幅图并没有对齐,要将 ggplot 图形和非 ggplot 图形对齐,需要使用 par() 函数来进行调整

非 ggplot 图形可以使用 wrap_elements() 函数来添加,可以进行更加灵活的控制,例如

old_par <- par(mar = c(0, 2, 0, 0), bg = NA) p1 wrap_elements(panel = ~plot(mtcars$mpg, mtcars$disp), clip = FALSE) par(old_par)

如果你想将非 ggplot 图放在最前面,例如

> grid::textGrob('Text on left side') p1 NULL

返回的是 NULL,这时,也需要使用 wrap_elements() 函数

wrap_elements(grid::textGrob('Text on left side')) p1

r语言数据可视化实例(R数据可视化)(6)

总的来说,对齐方式比 cowplot 更加复杂和繁琐

1.1.2 堆叠和包装

运算符只能简单地对图形进行组合,并不能提供任何布局信息,图片是以堆叠还是并列的方式排列。

因此,patchwork 提供了两个操作符:|:图形并列放置,即按行/:图形竖直堆叠,即按列

例如

p1 | p2

r语言数据可视化实例(R数据可视化)(7)

p1 / p2

r语言数据可视化实例(R数据可视化)(8)

这三个运算符的运算顺序与数学上一致,/ 和 | 比 优先级更高,最好的方式是使用小括号来区分组合优先级,例如

p1 / (p2 | p3)

r语言数据可视化实例(R数据可视化)(9)

1.1.3 组合函数

当我们需要处理绘图函数列表时,使用 来添加图形会显得很笨拙,wrap_plots() 允许传入一个绘图列表,或者每个绘图以参数的形式分开传递

wrap_plots(p1, p2, p3, p4) # 或者 wrap_plots(list(p1, p2, p3, p4))

r语言数据可视化实例(R数据可视化)(10)

1.2 左侧嵌套

上面的运算符都是将图形添加到左侧,例如

patch <- p1 p2 p3 patch

r语言数据可视化实例(R数据可视化)(11)

我们改变添加的顺序,图形会看起来不太一样

patch p3

r语言数据可视化实例(R数据可视化)(12)

这两种方式有什么不一样呢?因为 是按顺序逐个添加的, 的右侧图形需要与前面连接的图形串内的图形处于同一个嵌套级别。

对于 patch p3,相当于是 p1 p2 p3,而对于 p3 patch,patch 与 p3 处于同一嵌套级别

patchwork 还提供了一个 - 操作符来处理这种情况,其作为连接符而不是减号,两边的图形处于同一嵌套级别

patch - p3

r语言数据可视化实例(R数据可视化)(13)

或者使用 wrap_plots(),所有输入参数都处于同一级别

wrap_plots(patch, p3)

1.3 修改图形

在我们创建一个 patchwork 时,会返回最后一个添加的图形对象,我们可以继续添加 ggplot 图形对象

p1 p2 geom_jitter(aes(gear, disp))

r语言数据可视化实例(R数据可视化)(14)

如果想要修改其他图形,可以使用双中括号加索引的方式访问

patchwork <- p1 p2 patchwork[[1]] <- patchwork[[1]] theme_minimal() patchwork

r语言数据可视化实例(R数据可视化)(15)

修改全部

有时,我们可能想对所有图形进行统一的修改,例如修改主题,patchwork 提供了两个操作符

patchwork <- p3 / (p1 | p2) patchwork & theme_minimal()

r语言数据可视化实例(R数据可视化)(16)

patchwork * theme_minimal()

r语言数据可视化实例(R数据可视化)(17)

2. 控制布局

虽然使用 、|、/ 操作符可以创建复杂的图形,但是还缺少一些更灵活的控制,下面我们介绍如何使用 plot_layout() 函数来进行更多的控制

2.1 添加空白占位

plot_spacer() 函数可以添加一个空白的区域,大小与同一嵌套级别的图形一样

p1 plot_spacer() p2 plot_spacer() p3 plot_spacer()

r语言数据可视化实例(R数据可视化)(18)

不同的嵌套级别的占位大小不同

(p1 plot_spacer() p2) / (plot_spacer() p3 plot_spacer())

r语言数据可视化实例(R数据可视化)(19)

2.2 网格布局

如果没有给出任何布局信息,会尽可能将图形按照正方形网格进行排列,如果无法排列,则会使用启发式的方法自动调整。

我们可以使用 plot_layout() 来控制行列数量,每个网格具有相同的大小

p1 p2 p3 p4 plot_layout(ncol = 3)

r语言数据可视化实例(R数据可视化)(20)

使用 widths 可以控制相对宽度比

p1 p2 p3 p4 plot_layout(widths = c(2, 1))

r语言数据可视化实例(R数据可视化)(21)

或者使用绝对大小,使用 heights 设置第一行高度为 5cm,第二行为剩下的区域

p1 p2 p3 p4 plot_layout(widths = c(2, 1), heights = unit(c(5, 1), c('cm', 'null')))

r语言数据可视化实例(R数据可视化)(22)

2.3 非网格布局

对于非网格布局,你可能会想到使用嵌套的方式,但是这样容易让不同嵌套级别的图形很难再对齐。另一种方式是,通过设计自定义布局来排列图形。

有两种自定义布局的方式,最简单的就是使用文本表示,例如

layout <- " ##BBBB AACCDD ##CCDD " p1 p2 p3 p4 plot_layout(design = layout)

r语言数据可视化实例(R数据可视化)(23)

用 # 来表示空白区域,A-D 会根据添加的顺序自动对应到图形,也可以使用数字的方式

layout <- " ##2222 113344 ##3344 "

一种更具编程性的方法是使用 area() 函数来构建布局

layout <- c( area(t = 2, l = 1, b = 5, r = 4), area(t = 1, l = 3, b = 3, r = 5) ) p1 p2 plot_layout(design = layout)

r语言数据可视化实例(R数据可视化)(24)

t、l、b、r 分别指定了图形所占的上、左、下、右的网格,例如

layout <- c( area(1, 1), area(1, 3, 3), area(3, 1, 3, 2) ) plot(layout)

r语言数据可视化实例(R数据可视化)(25)

也可以使用 wrap_plots() 函数来绘制,如果使用文本表示的布局,可以传递命名图形的方式

layout <- ' A#B #C# D#E ' wrap_plots(D = p1, C = p2, B = p3, design = layout)

r语言数据可视化实例(R数据可视化)(26)

2.4 固定纵横比图

当我们对具有固定纵横比的图图形(如 coord_fixed()、coord_polar() 和 coord_sf() 创建的图形)进行组合时,由于 widths 和 heights 参数值默认设置为 NA,自动调整图形的大小看起来会比较奇怪

p_fixed <- ggplot(mtcars) geom_point(aes(hp, disp)) ggtitle('Plot F') coord_fixed() p_fixed p1 p2 p3

r语言数据可视化实例(R数据可视化)(27)

我们可以为 widths 设置值

p_fixed p1 p2 p3 plot_layout(widths = 1)

r语言数据可视化实例(R数据可视化)(28)

虽然其他图片对齐得很好,但是固定纵横比的图片,为了维持大小比例,在某一方向上不再对齐了

2.5 嵌入图形

前面的例子中,我们使用 area() 函数在网格布局中将一个图形嵌入到另一个图形中,还可以使用 inset_element() 函数将一个图形或图形对象嵌入到前一个图形中,你可以将它放在前一个图形区域的任何位置。

例如

p1 inset_element(p2, left = 0.6, bottom = 0.6, right = 1, top = 1)

r语言数据可视化实例(R数据可视化)(29)

p1 inset_element(p2, left = 0, bottom = 0.6, right = 0.4, top = 1, align_to = 'full')

r语言数据可视化实例(R数据可视化)(30)

默认定位使用的数值的单位为 npc,我们可以调整 1cm 的位置

p1 inset_element( p2, left = 0.5, bottom = 0.5, right = unit(1, 'npc') - unit(1, 'cm'), top = unit(1, 'npc') - unit(1, 'cm') )

r语言数据可视化实例(R数据可视化)(31)

2.6 控制图例

通常,每幅图及其图例都是一个整体,我们可以使用 guides 参数来控制图例的显示方式,可选的值为

例如

p1 p2 p3 p4 plot_layout(guides = 'collect')

r语言数据可视化实例(R数据可视化)(32)

((p2 / p3 plot_layout(guides = 'keep')) | p1) plot_layout(guides = 'collect')

r语言数据可视化实例(R数据可视化)(33)

对于存在重复图例的组合图形,我们可能想要删除其中某一个,例如,对于如下图形

p1a <- ggplot(mtcars) geom_point(aes(mpg, disp, colour = mpg, size = wt)) scale_colour_gradientn(colours = c("#66c2a5", "#fc8d62", "#8da0cb")) ggtitle('Plot 1a') p1a | (p2 / p3)

r语言数据可视化实例(R数据可视化)(34)

设置 guides = 'collect'

(p1a | (p2 / p3)) plot_layout(guides = 'collect')

r语言数据可视化实例(R数据可视化)(35)

我们还可以使用 guide_area() 来添加图例区域,其表现方式基本与 plot_spacer() 一样,如果没有设置 collect 形式,则与 plot_spacer() 一样,如果设置了,则会将所有图例绘制在该区域

p1 p2 p3 guide_area() plot_layout(guides = 'collect')

r语言数据可视化实例(R数据可视化)(36)

3. 添加注释和样式

在组合完图形之后,通常也需要添加一些注释信息,像标题或其他文本注释。patchword 提供了 plot_annotation() 函数用于添加注释

patchwork <- (p1 p2) / p3 patchwork plot_annotation( title = 'The surprising truth about mtcars', subtitle = 'These 3 plots will reveal yet-untold secrets about our beloved data-set', caption = 'Disclaimer: None of these plots are insightful' )

r语言数据可视化实例(R数据可视化)(37)

tag_level 参数用于控制标签的格式,格式包括:

patchwork plot_annotation(tag_levels = 'A')

r语言数据可视化实例(R数据可视化)(38)

可以使用 theme() 函数来设置标签的样式

patchwork plot_annotation(tag_levels = 'A') & theme(plot.tag = element_text(size = 8))

r语言数据可视化实例(R数据可视化)(39)

如果组合图形是嵌套布局,则会递归地添加图形标签,可以设置多个标签样式

patchwork[[1]] <- patchwork[[1]] plot_layout(tag_level = 'new') patchwork plot_annotation(tag_levels = c('A', '1'))

r语言数据可视化实例(R数据可视化)(40)

还可以设置标签的分隔符、前缀和后缀

patchwork plot_annotation( tag_levels = c('A', '1'), tag_prefix = 'Fig. ', tag_sep = '.', tag_suffix = ':' )

r语言数据可视化实例(R数据可视化)(41)

patchwork plot_annotation( tag_levels = c('A', '1'), tag_prefix = 'Fig. ', tag_sep = '.', tag_suffix = ':' ) & theme(plot.tag.position = c(0, 1), plot.tag = element_text(size = 8, hjust = 0, vjust = 0) )

r语言数据可视化实例(R数据可视化)(42)

可以为标签设置序列,如果设置的序列不够,后面的图形标签会设置为空

patchwork plot_annotation(tag_levels = list(c('#', '%'), '1'))

r语言数据可视化实例(R数据可视化)(43)

其他样式都可以使用 theme() 函数来设置,比如,文本字体

patchwork plot_annotation(title = 'The surprising truth about mtcars', theme = theme(plot.title = element_text(size = 18))) & theme(text = element_text('mono'))

r语言数据可视化实例(R数据可视化)(44)

,