说到拼图,那必须得好好学习patchwork
包,这个包是Thomas大佬的作品,一经推出就火了,迅速取代了R中其他的拼图包。
大佬很强,除了这个包,还有很多好用的包都是他开发的,比如gganimate/ggraph/tidygraph/ggforce
等,是不是也有一些你常用的包呢。
如果你仔细扒一扒,就会发现,我们常用的包,都是出自那几个人之手。。。
本期目录:
- 安装
- 拼图!
- 简单拼图
- 嵌套拼图
- 和非`ggplot2`对象拼图
- 堆叠和成组
- 快速拼很多图
- 修改子图形
- 修改全部子图形
- 控制整体布局
- 增加空白占位图形
- 控制行列数
- 行列的精细控制
- Fixed aspect plots
- 插入图形
- 控制图例
- 标题、副标题和说明文字
- 给子图添加序号
- 修改整个图形的外观
安装
代码语言:javascript复制# 2选1
install.packages('patchwork')
# install.packages("devtools")
devtools::install_github("thomasp85/patchwork")
拼图!
代码语言:javascript复制library(patchwork)
首先创建一些基本图形用于演示。
代码语言:javascript复制library(ggplot2)
p1 <- ggplot(mtcars)
geom_point(aes(mpg, disp))
ggtitle('Plot 1')
p2 <- ggplot(mtcars)
geom_boxplot(aes(gear, disp, group = gear))
ggtitle('Plot 2')
p3 <- ggplot(mtcars)
geom_point(aes(hp, wt, colour = mpg))
ggtitle('Plot 3')
p4 <- ggplot(mtcars)
geom_bar(aes(gear))
facet_wrap(~cyl)
ggtitle('Plot 4')
简单拼图
使用起来真的是简单,直接用
连起来就行了。
p1 p2
plot of chunk unnamed-chunk-4
嵌套拼图
默认会在图形左边添加图形,所以如果你先把2个图拼一起,在和第3个图拼,就会像下面这样:
代码语言:javascript复制patch <- p1 p2
p3 patch
plot of chunk unnamed-chunk-5
但是呢,也是可以调节的,简单换个顺序就不一样了:
代码语言:javascript复制patch p3
plot of chunk unnamed-chunk-6
这种情况下还可以用-
,又是不一样的效果,处处都是细节!
patch - p3
plot of chunk unnamed-chunk-7
和非ggplot2
对象拼图
和表格拼:
代码语言:javascript复制p1 gridExtra::tableGrob(mtcars[1:10, c('mpg', 'disp')])
plot of chunk unnamed-chunk-8
和base plot
拼图:
p1 ~plot(mtcars$mpg, mtcars$disp, main = 'Plot 2')
plot of chunk unnamed-chunk-9
但是有个问题,拼出来对不齐,这时候就需要其他函数辅助对齐了:
代码语言:javascript复制old_par <- par(mar = c(0, 2, 0, 0), bg = NA)
p1 wrap_elements(panel = ~plot(mtcars$mpg, mtcars$disp), clip = FALSE)
plot of chunk unnamed-chunk-10
代码语言:javascript复制par(old_par)
文字大小还是不太一样,还可以继续调整:
代码语言:javascript复制old_par <- par(mar = c(0, 0, 0, 0), mgp = c(1, 0.25, 0),
bg = NA, cex.axis = 0.75, las = 1, tcl = -0.25)
p1
wrap_elements(panel = ~plot(mtcars$mpg, mtcars$disp), clip = FALSE)
ggtitle('Plot 2')
theme(plot.margin = margin(5.5, 5.5, 5.5, 35))
plot of chunk unnamed-chunk-11
代码语言:javascript复制par(old_par)
和文本内容也可以拼:
代码语言:javascript复制p1 grid::textGrob('Some really important text')
plot of chunk unnamed-chunk-12
但是呢你要注意,如果你写反了,就拼不出来了:
代码语言:javascript复制grid::textGrob('Text on left side') p1
## NULL
如果你非要反着写,那要借助wrap_elements()
才能显示出来:
wrap_elements(grid::textGrob('Text on left side')) p1
plot of chunk unnamed-chunk-14
堆叠和成组
|
左右拼/
上下拼
p1 / p2
plot of chunk unnamed-chunk-15
代码语言:javascript复制p1 | p2
plot of chunk unnamed-chunk-16
代码语言:javascript复制p1 / (p2 | p3)
plot of chunk unnamed-chunk-17
快速拼很多图
如果你有多个图形组成的列表,再使用 可能就不太方便了,可以直接使用warap_plots()
函数:
wrap_plots(p1,p2,p3,p4)
plot of chunk unnamed-chunk-18
修改子图形
默认操作都会传给最后一个图形。
代码语言:javascript复制p1 p2 geom_jitter(aes(gear, disp))
plot of chunk unnamed-chunk-19
可以通过修改子集的方式达到精准控制某个图形细节:
代码语言:javascript复制patchwork <- p1 p2
patchwork[[1]] <- patchwork[[1]] theme_minimal() # 修改第1个
patchwork
plot of chunk unnamed-chunk-20
修改全部子图形
&
:将主题修改应用到所有子图形*
:将主题修改应用到当前嵌套水平的子图形
patchwork <- p3 / (p1 | p2)
patchwork & theme_minimal()
plot of chunk unnamed-chunk-21
代码语言:javascript复制patchwork * theme_minimal()
plot of chunk unnamed-chunk-22
控制整体布局
还是先建立一些基本图形用于演示。
代码语言:javascript复制library(ggplot2)
p1 <- ggplot(mtcars)
geom_point(aes(mpg, disp))
ggtitle('Plot 1')
p2 <- ggplot(mtcars)
geom_boxplot(aes(gear, disp, group = gear))
ggtitle('Plot 2')
p3 <- ggplot(mtcars)
geom_point(aes(hp, wt, colour = mpg))
ggtitle('Plot 3')
p4 <- ggplot(mtcars)
geom_bar(aes(gear))
facet_wrap(~cyl)
ggtitle('Plot 4')
增加空白占位图形
顾名思义,增加几个空白图形,纯属占位子用的。
代码语言:javascript复制p1 plot_spacer() p2 plot_spacer() p3 plot_spacer()
plot of chunk unnamed-chunk-24
控制行列数
使用plot_layout()
控制行列数:
p1 p2 p3 p4
plot_layout(ncol = 3)
plot of chunk unnamed-chunk-25
还可以控制不同行列的高度和宽度:
代码语言:javascript复制p1 p2 p3 p4
plot_layout(widths = c(2, 1),
heights = c(3, 2)
)
plot of chunk unnamed-chunk-26
行列的精细控制
使用自定义布局达到对行列数的精细控制。
代码语言:javascript复制# 3行6列,4个图形,#表示占位符
layout <- "
##BBBB
AACCDD
##CCDD
"
p1 p2 p3 p4
plot_layout(design = layout)
plot of chunk unnamed-chunk-27
合理使用布局,可以达到叠加的效果:
代码语言:javascript复制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)
plot of chunk unnamed-chunk-28
wrap_plots()
也是可以用布局的:
layout <- '
A#B
#C#
D#E
'
wrap_plots(D = p1, C = p2, B = p3, design = layout) # 指定字母代表的图形
plot of chunk unnamed-chunk-29
Fixed aspect plots
有些图形是固定好坐标轴比例的,这时候的拼图操作是不会影响原本比例的:
代码语言:javascript复制p_fixed <- ggplot(mtcars)
geom_point(aes(hp, disp))
ggtitle('Plot F')
coord_fixed()
p_fixed p1 p2 p3
plot of chunk unnamed-chunk-30
代码语言:javascript复制p_fixed p1 p2 p3 plot_layout(widths = 1) # 比例不变
plot of chunk unnamed-chunk-31
插入图形
把一张图插在另一张图上,使用insert_element()
函数。
p1 inset_element(p2, left = 0.6, bottom = 0.6, right = 1, top = 1)
plot of chunk unnamed-chunk-32
代码语言:javascript复制p1 inset_element(p2, left = 0, bottom = 0.6, right = 0.4, top = 1, align_to = 'full')
plot of chunk unnamed-chunk-33
边距都是可以设置的:
代码语言:javascript复制p1 inset_element(
p2,
left = 0.5,
bottom = 0.5,
right = unit(1, 'npc') - unit(1, 'cm'),
top = unit(1, 'npc') - unit(1, 'cm')
)
plot of chunk unnamed-chunk-34
控制图例
对于多张图图例都一样时,可以直接用一个图例:
代码语言:javascript复制p1 p2 p3 p4
plot_layout(guides = 'collect')
plot of chunk unnamed-chunk-35
合理使用括号改变组图顺序,达到把单个图例放到整张图右边的效果:
代码语言:javascript复制# 默认就是auto
((p2 / p3 plot_layout(guides = 'auto')) | p1) plot_layout(guides = 'collect')
plot of chunk unnamed-chunk-36
代码语言:javascript复制# 改成keep
((p2 / p3 plot_layout(guides = 'keep')) | p1) plot_layout(guides = 'collect')
plot of chunk unnamed-chunk-37
可以自动移除重复图例,比如正常的拼图会像下面这样,mpg
这个图例出现了2次:
p1a <- ggplot(mtcars)
geom_point(aes(mpg, disp, colour = mpg, size = wt))
ggtitle('Plot 1a')
p1a | (p2 / p3)
plot of chunk unnamed-chunk-38
使用guides = 'collect'
后,重复图例被去掉了:
(p1a | (p2 / p3)) plot_layout(guides = 'collect')
plot of chunk unnamed-chunk-39
还可以把图例单独放到一个子图中:
代码语言:javascript复制p1 p2 p3 guide_area()
plot_layout(guides = 'collect')
plot of chunk unnamed-chunk-40
标题、副标题和说明文字
默认图形用于演示。
代码语言:javascript复制library(ggplot2)
p1 <- ggplot(mtcars)
geom_point(aes(mpg, disp))
ggtitle('Plot 1')
p2 <- ggplot(mtcars)
geom_boxplot(aes(gear, disp, group = gear))
ggtitle('Plot 2')
p3 <- ggplot(mtcars)
geom_point(aes(hp, wt, colour = mpg))
ggtitle('Plot 3')
p4 <- ggplot(mtcars)
geom_bar(aes(gear))
facet_wrap(~cyl)
ggtitle('Plot 4')
使用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'
)
plot of chunk unnamed-chunk-42
给子图添加序号
用大写的A就是大写字母,用小写的a就是小写字母:
代码语言:javascript复制patchwork plot_annotation(tag_levels = 'A')
plot of chunk unnamed-chunk-43
当然外观是可以修改的:
代码语言:javascript复制patchwork
plot_annotation(tag_levels = 'A') &
theme(plot.tag = element_text(size = 8))
plot of chunk unnamed-chunk-44
还可以在子图内部使用不同的序号:
代码语言:javascript复制patchwork[[1]] <- patchwork[[1]] plot_layout(tag_level = 'new')
patchwork plot_annotation(tag_levels = c('A', '1'))
plot of chunk unnamed-chunk-45
在序号前添加前缀:
代码语言:javascript复制patchwork plot_annotation(tag_levels = c('A', '1'), tag_prefix = 'Fig. ',
tag_sep = '.', tag_suffix = ':')
plot of chunk unnamed-chunk-46
修改序号外观:
代码语言:javascript复制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))
plot of chunk unnamed-chunk-47
使用自定义列表的形式为不同的子图安排不同的序号:
代码语言:javascript复制patchwork
plot_annotation(tag_levels = list(c('#', '&'), '1'))
plot of chunk unnamed-chunk-48
修改整个图形的外观
代码语言:javascript复制patchwork
plot_annotation(title = 'The surprising truth about mtcars') &
theme(text = element_text('mono'))
plot of chunk unnamed-chunk-49
代码语言:javascript复制patchwork
plot_annotation(title = 'The surprising truth about mtcars',
theme = theme(plot.title = element_text(size = 18))) &
theme(text = element_text('mono'))
plot of chunk unnamed-chunk-50