我们知道ggplot2
工具包有很多方便的拼图拓展包,如cowplot
、patchwork
等,而本篇就来介绍在使用R语言的基础绘图系统如何进行拼图。需要明确的是,基础绘图系统的拼图功能不需要借助任何其他的工具包,而且它是通过参数而非函数来实现这一功能的。
基础绘图系统拼图主要有两种方法:
第一种方法比较常见且易于操作,是在par
函数中对mfrow
或者mfcol
参数进行设置,这种方式实际上实现的是分子图的功能,各子图之间相互独立;
第二种方法需要同时对par
函数的fig
、plt
和new
三个参数进行设置来实现,使用比较灵活但要经过多次试验才能得到较佳的效果,它主要用来实现组图功能,即将几个相互联系的图形通过合理布局形成一个整体。
本篇主要介绍第二种方法,不过在此之前需要先通过第一种方法来介绍下基础绘图系统中的四种边框概念。
在推文graphics | 基础绘图系统(二)—— 绘图参数及par函数中有如下示意图:
上图中红色字体对应的就是四种边框,在只有单个图形的图片中:
外边框(outer margin)与图片边界相重合,是不能变化位置的;
内边框(inner margin)的位置决定了图片的页面距大小;
图形边框(figure margin)和绘图区边框(plot side)之间用于放置坐标轴刻度、标签、图形标题等要素;
绘图区边框内用于绘制几何图形。
实际上,这种情况下,inner margin和figure margin根本没有区分的必要。在默认状况下,outer、inner、figure margin三种边框是重合的。
但在有子图的情况下,inner margin和figure margin的区别就体现出来了,前者是整幅图的内边框,而后者相当于各子图的外边框。
box
函数可以在绘图时显示各类边框,该函数在推文graphics | 基础绘图系统(五)——plot函数功能再探和低级绘图函数中有所介绍。
par(mfrow = c(2,2))
plot(1:5)
par("fig")
plot(1:5)
par("fig")
plot(1:5)
par("fig")
plot(1:5)
par("fig")
box("fig", col = "red")
box("inner", col = "blue")
红色边框为子图的figure margin,蓝色边框为整副图的inner margin(默认与outer margin重合)。
# 四次par("fig)的输出结果
## [1] 0.0 0.5 0.5 1.0
## [1] 0.5 1.0 0.5 1.0
## [1] 0.0 0.5 0.0 0.5
## [1] 0.5 1.0 0.0 0.5
从输出结果可以看出,子图的位置可以通过
fig
参数控制。
了解以上边框概念后,我们就可以通过手动设置fig
参数来进行组图了。在图形的基础上在叠加新图形也有两种途径:
一是使用低级绘图函数,详见推文graphics | 基础绘图系统(五)——plot函数功能再探和低级绘图函数;
二是在绘制新图形前将par
函数的参数new
设置为TRUE,且每绘制一个新图形就需要设置一次。
学习本推文接下来的内容前,请务必先阅读以下推文尤其是“布局参数”部分,以了解各参数的含义:
graphics | 基础绘图系统(二)—— 绘图参数及par函数(可直接点击)
绘图案例来自Nature Communications期刊上一篇论文:
Burke, M., González, F., Baylis, P. et al. Higher temperatures increase suicide rates in the United States and Mexico. Nature Clim Change 8, 723–729 (2018). https://doi.org/10.1038/s41558-018-0222-x
原图如下:
该图由一幅地图和两幅直方图组成。在作者提供的原代码中只是单独绘制了这三幅图形,并没有提供组图的代码。本文尝试对其进行组装。
原代码对数据的处理过程比较复杂,这里将最终的处理结果作为示例数据(需要数据的读者请在公众号后台发送关键词“示例数据”获取):
library(tidyverse)
library(sf)
load("38-2.usa.rdata")
load("38-3.mex.rdata")
load("38-4.colhist1.rdata")
load("38-5.colhist2.rdata")
usa_siggy <- filter(usa, siggy == T)
mex_siggy <- filter(mex, siggy == T)
论文作者绘制地图的代码的简化版:
plot(st_geometry(usa), col = usa$color,
lwd = 0.025, border = NA,
xlim = c(-125, -66), ylim = c(17, 47))
plot(st_geometry(mex), col = mex$color,
lwd = 0.025, border = NA, add = T)
plot(st_geometry(usa_siggy), add = T, lwd = 1.5)
plot(st_geometry(mex_siggy), add = T, lwd = 1.5)
关于使用
plot
函数绘制地图的方法,本号已有两篇相关推文,这里不再赘述:
sf | 使用plot函数绘制地图;
技巧 | 如何使用R语言的常用工具包绘制双变量填充地图。
论文作者绘制两幅直方图的代码的简化版:
# 直方图1
hist(usa$plotValue, breaks = seq(-4.5, 7, 0.5),
col = colhist1, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-3, 3, 1),
at = seq(-3, 3, 1) - 0.25)
# 直方图2
hist(mex$plotValue, breaks = c(-15, seq(-4.5, 7, 0.5),15),
col = colhist2, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-10, 10, 1),
at =seq(-10, 10, 1) - 0.25)
hist
函数绘制直方图的方法见以下推文:
graphics | 基础绘图系统(四)——柱状图、直方图、扇形图、箱形图和函数图象。
首先根据各子图的位置和大小对fig
参数进行粗略的设置,从第二幅子图开始还需要设置new
参数。
为了更好观察各子图之间的位置关系,可以使用box
函数显示出子图的plot和figure边框,这里对不同子图的边框赋予不同的颜色加以区分。
此外,fig
参数的设置还依赖于导出图片的尺寸,因此图片尺寸需要提前确定。导出图片及其尺寸确定的方法见以下推文:
grDevices | 基础绘图系统(六)——如何导出高清图片
关键设置见下列代码中的行注释:
jpeg("38-6.jpeg", width = 10, height = 6,
units = "in", res = 600)
# 地图
par(fig = c(0.05, 0.8, 0.05, 0.95)) # 设置fig参数
plot(st_geometry(usa), col = usa$color,
lwd = 0.025, border = NA,
xlim = c(-125, -66), ylim = c(17, 47))
plot(st_geometry(mex), col = mex$color,
lwd = 0.025, border = NA, add = T)
plot(st_geometry(usa_siggy), add = T, lwd = 1.5)
plot(st_geometry(mex_siggy), add = T, lwd = 1.5)
box(col = "red") # 添加plot边框
box("fig", col = "red") # 添加fig边框
# 直方图1
par(fig = c(0.7, 0.95, 0.5, 0.95),
new = T) # 设置fig和new参数
hist(usa$plotValue, breaks = seq(-4.5, 7, 0.5),
col = colhist1, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-3, 3, 1),
at = seq(-3, 3, 1) - 0.25)
box(col = "green") # 添加plot边框
box("fig", col = "green")
# 直方图2
par(fig = c(0.5, 0.95, 0.05, 0.5),
new = T) # 设置fig和new参数
hist(mex$plotValue, breaks = c(-15, seq(-4.5, 7, 0.5),15),
col = colhist2, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-10, 10, 1),
at =seq(-10, 10, 1) - 0.25)
box(col = "blue") # 添加plot边框
box("fig", col = "blue") # 添加fig边框
box("outer") # 添加外边框
dev.off()
从上面的草图中可以看出,地图和直方图都未能充分利用各自的图形区域,因此需要对plt
参数进行设置。
地图的figure和plot边框之间没有其他要素,因此可以使二者重合,即设置plt = c(0, 1, 0, 1)
;
直方图的figure和plot边框底部间隙需要放置刻度标签,因此需要留出空间,设置plt = c(0, 1, 0.2, 0.8)
;
直方图位置可以适当往左和中间靠近,需要对相应的fig
参数作略微调整。
关键设置见下列代码中的行注释:
jpeg("38-7.jpeg", width = 10, height = 6,
units = "in", res = 600)
# 地图
par(fig = c(0.05, 0.8, 0.05, 0.95),
plt = c(0, 1, 0, 1)) # 设置plt参数
plot(st_geometry(usa), col = usa$color,
lwd = 0.025, border = NA,
xlim = c(-125, -66), ylim = c(17, 47))
plot(st_geometry(mex), col = mex$color,
lwd = 0.025, border = NA, add = T)
plot(st_geometry(usa_siggy), add = T, lwd = 1.5)
plot(st_geometry(mex_siggy), add = T, lwd = 1.5)
box(col = "red")
box("fig", col = "red")
# 直方图1
par(fig = c(0.6, 0.95, 0.5, 0.9), new = T,
plt = c(0, 1, 0.2, 0.8)) # 调整fig参数和设置plt参数
hist(usa$plotValue, breaks = seq(-4.5, 7, 0.5),
col = colhist1, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-3, 3, 1),
at = seq(-3, 3, 1) - 0.25)
box(col = "green")
box("fig", col = "green")
# 直方图2
par(fig = c(0.55, 0.95, 0.1, 0.5), new = T,
plt = c(0, 1, 0.2, 0.8)) # 调整fig参数和设置plt参数
hist(mex$plotValue, breaks = c(-15, seq(-4.5, 7, 0.5),15),
col = colhist2, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-10, 10, 1),
at =seq(-10, 10, 1) - 0.25)
box(col = "blue")
box("fig", col = "blue")
box("outer")
dev.off()
上图的布局已经基本合理,再做以下调整:
地图左侧的空隙过大,可将其fig
参数的第一个元素由0.05调整为0;
对照作者原图在直方图附近添加相应的文本内容,使用函数为mtext
,具体方法见推文graphics | 基础绘图系统(三)——添加文本标注、坐标轴线和图例;
去掉各子图的边框。
关键设置见下列代码中的行注释:
jpeg("38-8.jpeg", width = 10, height = 6,
units = "in", res = 600)
# 地图
par(fig = c(0, 0.8, 0.05, 0.95),
plt = c(0, 1, 0, 1)) # fig的第一个元素改为0
plot(st_geometry(usa), col = usa$color,
lwd = 0.025, border = NA,
xlim = c(-125, -66), ylim = c(17, 47))
plot(st_geometry(mex), col = mex$color,
lwd = 0.025, border = NA, add = T)
plot(st_geometry(usa_siggy), add = T, lwd = 1.5)
plot(st_geometry(mex_siggy), add = T, lwd = 1.5)
# 直方图1
par(fig = c(0.6, 0.95, 0.5, 0.9), new = T,
plt = c(0, 1, 0.2, 0.8))
hist(usa$plotValue, breaks = seq(-4.5, 7, 0.5),
col = colhist1, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-3, 3, 1),
at = seq(-3, 3, 1) - 0.25)
mtext("United States", side = 4, line = -5, las = 1) # 添加文本
mtext("Percentage change in suicide \n rate per C",
side = 1, line = 3, las = 1) # 添加文本
# 直方图2
par(fig = c(0.55, 0.95, 0.1, 0.5), new = T,
plt = c(0, 1, 0.2, 0.8))
hist(mex$plotValue, breaks = c(-15, seq(-4.5, 7, 0.5),15),
col = colhist2, axes = F, xlim = c(-10, 10),
xlab = "", ylab = "", main = "")
axis(1, tick = F, line = -0.5,
labels = seq(-10, 10, 1),
at =seq(-10, 10, 1) - 0.25)
mtext("Mexico", side = 4, line = -4, las = 1) # 添加文本
mtext("Percentage change in suicide rate per C",
side = 1, line = 2, las = 1) # 添加文本
box("outer")
dev.off()
至此基本复原了论文中的原图。
本号至今已经有多篇集中介绍基础绘图系统的基本语法的推文了,详细可点击话题标签“制表与可视化专辑”查看。本篇是在这些推文基础上的技巧应用。
读者可以根据需要先学习下相关的历史推文,或者直接练习一下本篇的案例。示例数据请在公众号后台发送关键词“示例数据”获取。
往期推荐阅读:
《数据处理通识》专辑-base | 使用apply族函数进行向量化运算
《制表与可视化》专辑-ggplot2 | ggplot2作图语法入门
《数学模型》专辑-car | 线性回归(三)——残差分析和异常点检验
《地理计算与分析》专辑-spdep | 如何在R语言中计算空间自相关指数