ggplot2是基于grid的绘图工具,它绘制的图像其实是由多个grob(grid graph object)组成的,比如一张点图,它的标题是titleGrob,点图的基本单元包括pointsGrob。
gtable可以个性化修改定制这些grob对象,从而可以对gplot2绘图对象进行更加高阶的定制,比如拼图、图中嵌图等。
很多工具包都可以进行拼图或嵌图, 比如cowplot、patchwork、ggpubr、gridExtra等等,但是gtable是相对底层的进行操纵ggplot2对象的包。cowplot的很多功能实现就依赖于gtable。
最近探索了一下gtable的使用,主要是两个方面:如何进行拼图和嵌图。
使用gtable进行拼图
cowplot拼图示例
cowplot就可以很方便的进行拼图,使用plot_grid函数即可,如下图所示。
如果觉得每张图片太大,还可以使用scale功能对每张图片进行缩放。
对于ncol、nrow参数而言,它们几乎已经是拼图布局的行列定义的标签了,在多个拼图工具中都能见到,比如ggpubr的ggarrange函数,当然这也不奇怪,毕竟ggarrange就是依赖的cowplot包。
还可以定义多行或者多列的比例,使用rel_widths或者rel_heights来指定。
align参数用于对齐多个图的元素,可以对水平、垂直方向进行对齐。
代码语言:javascript复制library(tidyverse)
library(grid)
library(gtable)
library(cowplot)
p1 <- mtcars %>% ggplot(aes(x = mpg, y = disp, color = am))
geom_point()
p2 <- iris %>% ggplot(aes(x = Sepal.Length, y = Sepal.Width))
geom_boxplot(aes(fill = Species))
cowplot::plot_grid(p1, p2, ncol = 2)
gtable进行拼图
如果要使用gtable进行拼图,只需要创建一个1行2列的gtable对象,然后每个位置填入相应的图像即可。
填充图形是使用的gtable_add_grob实现的。
代码语言:javascript复制# 将待拼图转换为gtable对象(grob table)
# 以下两种方式都可以,函数来源于ggplot2
g1 <- ggplotGrob(p1)
g2 <- ggplot_gtable(ggplot_build(p2))
# 创建一个布局为一行两列的gtable对象,用于放置上述的两个对象
# 每一个位置的长宽都是1 null
fig_combined <- gtable(widths = unit(c(1, 1), "null"),
heights = unit(1, "null"))
# 将两张图片分别放入gtable中
# gtable_add_grob可以对gtable对象进行操纵,添加
# t是top,上边界是第一行
# l是left,左边界是第一列
fig_combined <- gtable_add_grob(fig_combined,
grobs = g1,
t = 1,
l = 1)
# l是left,左边界是第二列
fig_combined <- gtable_add_grob(fig_combined,
grobs = g2,
t = 1,
l = 2)
plot(fig_combined)
这个图像其实和cowplot的出图是一样的,就不附图了。
其实只需要简单修改就可以实现不同比例的拼图,比如让左右两图的宽度是2:1,那么只需要设置好新建gtable的宽度为2null和1null即可。如下图所示。
另外更有意思的是,新建gtable的宽度并非要求整数,gtable(widths = unit(c(1, 0.5), "null"), heights = unit(1, "null"))
也是可以的。
# 创建一个一行两列的gtable对象,宽度分别是2null和1null
fig_combined <- gtable(widths = unit(c(2, 1), "null"),
heights = unit(1, "null"))
# 其他照旧
fig_combined <- gtable_add_grob(fig_combined,
grobs = g1,
t = 1,
l = 1)
fig_combined <- gtable_add_grob(fig_combined,
grobs = g2,
t = 1,
l = 2)
plot(fig_combined)
使用gtable进行嵌图
cowplot嵌图示例
cowplot进行嵌图的思路是通过创建新画布并不停叠加图层来实现,由于新图层的位置和大小可以调,也就呈现出了不同的嵌图效果。
比如将第二张图的宽和高设置为以前的30%,并且将其放在绘图区域的右上角(绘图区域(0.6,0.6)是待嵌图的左下角。)
这里的0.6是相当于整个绘图区域而言(下图灰色部分),而不是第一张图的坐标轴显示区域, 其值是相对值,最宽最高代表值为1。
代码语言:javascript复制ggdraw()
draw_plot(p1)
draw_plot(p2, x = 0.6, y = 0.6, width = 0.3, height = 0.3)
cowplot还可以对其图表并进行叠加,思路也是一样的。
代码语言:javascript复制# 使用透明主题,以防止图片被覆盖
p2_new <- p2 cowplot::theme_cowplot()
# 对齐图片
align_grobs <- cowplot::align_plots(p1, p2_new, align = "h")
# 去除坐标轴和图例
align_grobs[[2]] <- cowplot::gtable_remove_grobs(
align_grobs[[2]],
names = c("axis-b", "axis-l",
"xlab-b", "ylab-l", "guide-box") )
# 拼图
ggdraw()
draw_grob(align_grobs[[1]])
draw_grob(align_grobs[[2]])
gtable进行嵌图
gtable也可以进行嵌图,思路和拼图是一样的,先构造一个新gtable布局,然后将图片依次嵌入。
不同于cowplot嵌图,在这里可以指定图片只嵌到坐标轴指示的panel区域。让嵌入的图是主图的panel区域的40%大小。
代码语言:javascript复制# p2添加边框,以用于嵌图显示,并转换为gtable对象:g2_new
g2_new <- ( p2 theme(plot.background = element_rect(fill = NA, colour = "black")) ) %>% cowplot::as_gtable()
# 取出g1的panel区域及panel区域的位置信息(layout)
g1_panel <- gtable::gtable_filter(g1, "panel")
g1_panel_layout <- g1 %>% .$layout %>% filter(name == "panel") %>% c()
# 将g2_new按照比例并到g1的panel
# 新建panel是2*2的绘图方格,比例都是6:4
# g1_panel占据新建的gtable全部方格,而g2_new只在右上方方格
# 于是g2_new就是g1_panel的40%大小
fig_embed <- gtable(widths = unit(c(0.6, 0.4), "null"),
heights = unit(c(0.4, 0.6), "null"))
fig_embed <- gtable_add_grob(fig_embed, g1_panel,
t=1,l=1,b=2,r=2)
fig_embed <- gtable_add_grob(fig_embed, g2_new,
t=1,l=2)
# 将合并好的fig_embed重新覆盖g1的panel区域
fig_embed <- gtable_add_grob(g1, fig_embed,
t = g1_panel_layout$t,
l = g1_panel_layout$l,
b = g1_panel_layout$b,
r = g1_panel_layout$r)
plot(fig_embed)
还可以通过对p2添加空白框,从而完成图形的缩放:
代码语言:javascript复制# 添加空白区域,缩放为90%大小
g2_scale <- gtable_add_padding(g2_new, padding = unit(0.05, "npc"))
# 以下是相同的操作
# 取出g1的panel和panel的位置信息
g1_panel <- gtable::gtable_filter(g1, "panel")
g1_panel_layout <- g1 %>% .$layout %>% filter(name == "panel") %>% c()
# 将g2并到g1的panel
fig_embed <- gtable(widths = unit(c(0.6, 0.4), "null"),
heights = unit(c(0.4, 0.6), "null"))
fig_embed <- gtable_add_grob(fig_embed, g1_panel,
t=1,l=1,b=2,r=2)
fig_embed <- gtable_add_grob(fig_embed, g2_scale,
t=1,l=2)
# 将合并好的fig_embed重新覆盖g1的panel区域
fig_embed <- gtable_add_grob(g1, fig_embed,
t = g1_panel_layout$t,
l = g1_panel_layout$l,
b = g1_panel_layout$b,
r = g1_panel_layout$r)
plot(fig_embed)