https://github.com/thomasp85/patchwork
The goal of
patchwork
is to make it ridiculously simple to combine separate ggplots into the same graphic. As such it tries to solve the same problem asgridExtra::grid.arrange()
andcowplot::plot_grid
but using an API that incites exploration and iteration.
安装加载:
p_load_gh('thomasp85/patchwork')
1、合并对象
两个ggplot2对象,通过'+'可以直接合并在一张图中 (默认左右等面积排布):
p_load(ggplot)
p1 = ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 = ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
p1 + p2
如果未正常加载patchwork包,则会报错:
错误: Don't know how to add p2 to a plot
2、 plot_layout()
自定义排布:
You can use this call to determine the dimensions you want the plots to be arranged in, and the size of the plots.
p1 + p2 + plot_layout(ncol = 1) # 按1列排布
p1 + p2 + plot_layout(ncol = 1, heights = c(3, 1)) # 两图高度比3:1
3、 plot_spacer()
增加图形间距:
p1 + plot_spacer() + p2 # 默认一个对象宽度的空白间距
p1 + plot_spacer() + p2 + plot_layout(nrow = 1, width = c(5, 1, 5)) # 自定义间距
通过上示用法,已经可以很方便的完成很多形式的排布:
mtcars$cyl = as.factor(mtcars$cyl)
p1 = p1 + labs(tag = 'A') # 给每个图添加标签
p2 = p2 + labs(tag = 'B')
p3 = ggplot(mtcars, aes(wt, mpg, color = cyl, shape = cyl)) + geom_point() + geom_smooth(method = lm, se = F, fullrange = T) + theme_classic() + scale_color_brewer(palette = 'Dark2') + theme(legend.position = 'top') + labs(tag = 'C')
# 两种不同的显示效果
p1 + p2 + p3 + plot_layout(nrow = 2, height = c(2, 1)) # p3在左侧
p1 + p2 + plot_spacer() + p3 + plot_layout(ncol = 2, height = c(2, 1)) # p3在右侧
那么,如何调整为如下的排布呢?
对此,patchwork包中也提供了极为简便的操作符!
4、操作符:
https://rdrr.io/github/thomasp85/patchwork/man/plot_arithmetic.html
通过前文,我们知道 +
合并ggplot2对象,结合 plot_layout()
可以灵活的对图形进行排布。
patchwork包中其实有更便捷的方法, |
操作符用于设置水平排布, /
操作符垂直排布。所以对于上示问题的解决办法,至少有如下三种:
# 方法1
(p1 + p2)/p3 + plot_layout(height = c(2, 1))
# 方法2
(p1 | p2)/p3 + plot_layout(height = c(2, 1))
# 方法3
(p1 + p2)-p3 + plot_layout(ncol = 1, height = c(2, 1)) # 此处'-'左边的'()'可省略
几点说明:
1、'/'其实看起来与算术表达式略有相似,'/'右侧的在分母位置,即下方
2、 '()' 或者 '{}' 用于表示嵌套关系
You can make nested plots layout by wrapping part of the plots in parentheses - in these cases the layout is scoped to the different nesting levels.
3、方法3中,操作符 -
(不能当做减法)是将其左右两侧的对象置于同一嵌套水平(平级),即 p1+p2
与 p3
应当做两个对象去处理,故需要明确是按行还是按列排布,如按列排布,则要设置 ncol=1
!
否则会报出 警告,并默认按行排布!
Warning message:In xList[i] <- valuelist="" :="">->
那么,方法3中能否将 -
替换成 +
呢?
-
will behave like+
but put the left and right side in the same nesting level (as opposed to putting the right side into the left sides nesting level).
(p1 + p2)+p3 + plot_layout(ncol = 1, height = c(2, 1))
结果如下:
可见,其结果等同于去除括号后的结果,且由于参数 height
只设置了两个值,所以同时会报出警告:
Warning message:In xList[i] <- valuelist="" :="">->
更多复杂的排布:
p4 = ggplot(diamonds, aes(x = price, colour = cut, fill = cut)) + geom_density(alpha = 0.5) + guides(alpha = FALSE) + theme_bw() + labs(tag = 'D')
((p1 + p2/p3) - p4) + plot_layout(width = c(2, 1))
(((p1 + p2)/p3) | p4) + plot_layout(width = c(2, 1)) # 此处'|'可替换为'-'
此时,我们看到,上示图形中虽然已经完成了排布,但是其风格完全不同:有灰色网格背景、白色网格背景和纯白色背景,也有四方框、半边框和无边框...这归因于单独对于某些图形的调整,那么如果是想统一呢?
很简单,对每个图形都做相同的设置,例如都用纯白背景:
p + theme_bw() + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
这里,p要分别替换为p1、p2、p3、p4,代码里也就会有4段高度一致的脚本...
所幸,patchwork包中也替我们考虑到了这个问题:
In order to reduce code repetition
patchwork
provides two operators for adding ggplot elements (geoms, themes, facets, etc.) to multiple/all plots in an assemble.*
will add the element to all plots in the current nesting level, while&
will recurse into nested cells.
((((p1 + p2)/p3) | p4) + plot_layout(width = c(2, 1))) & (theme_bw() + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
如图,全部统一为纯白背景,但是有个问题,C图的图例放在了图的右侧(之前都是在上部),这是因为 theme_bw()
覆盖了之前的 theme()
设置。提供一种解决方法:
((((p1 + p2)/p3) | p4) + plot_layout(width = c(2, 1))) & (theme(panel.background = element_rect(fill = 'white', colour = 'black', size = 1), panel.grid.major = element_blank(), panel.grid.minor = element_blank()))
最终显示效果如下:
所以, &
可以将设置递归地应用到所有的嵌套水平,即拼图中所有的子图上。类似的还有个 *
操作符,只能将设置应用在当前嵌套水平上!
*
will only affect the plots on the current nesting level, whereas&
will recurse into nested levels
#(p1 + p2 + p4) * theme_bw()
T1 = wrap_elements((p1 + p2 + p4) * theme_bw()) + ggtitle('p1 + p2 + p4') + theme(plot.title = element_text(size = 15, hjust = 0.5, color = 'red'))
#(p1 + p2 - p4) * theme_bw()
T2 = wrap_elements((p1 + p2 - p4) * theme_bw()) + ggtitle('p1 + p2 - p4') + theme(plot.title = element_text(size = 15, hjust = 0.5, color = 'red'))
# 拼图组合
T1 - T2 + plot_layout(ncol = 1)
如图所示,对于 p1+p2+p4
, p1
、 p2
、 p4
三者属于同一嵌套水平,所以 *
操作符将 theme_bw()
应用在了每个图中。而对于 p1+p2-p4
, p1+p2
与 p4
则属两个平级的嵌套层,所以 p1+p2
的图面积与 p4
的一样,而 *
只将 theme_bw() 应用在了当前嵌套水平的 p4
上,p1
和 p2
算是子一级水平!
联系客服