这篇推送使用了biscale
工具包绘制了双变量填充地图。近来,小编发现使用常用的绘图工具包也能很便捷的绘制这种地图,而且灵活性更强一些。
1 示例数据
本篇使用的示例数据和上面转载的推送一样,关于数据的介绍可以点击上面的推送链接查看。
library(tidyverse)
data <- socviz::county_data
albersusa::counties_sf(proj = "laea") %>%
mutate(fips = as.character(fips)) %>%
left_join(data, by = c("fips" = "id")) -> usa
- 示例数据来自
socviz
和albersusa
两个工具包,目前它们都只能通过github安装。不会安装的读者可以在公众号后台发送关键词“示例数据”获取。
2 准备工作
灵感来自Nature上的一篇论文:
- Dwyer-Lindgren, L., Cork, M.A., Sligar, A. et al. Mapping HIV prevalence in sub-Saharan Africa between 2000 and 2017. Nature, 570, 189–193 (2019). https://doi.org/10.1038/s41586-019-1200-9
这篇论文绘制的双变量填充地图是这样的:
要想绘制这样的地图,关键是要定义两个方向的渐变颜色,这里直接使用了上述论文所定义的颜色,然后使用sf
工具包的网格生成函数制作了图例,后续只要把这个图例与主图拼接在一起就行了。
library(sf)
# 选择颜色
colors <- list(c(249, 244, 248), c(205, 216, 236),
c(177, 203, 230), c(138, 181, 223),
c(239, 212, 219), c(202, 190, 210),
c(175, 176, 207), c(131, 160, 204),
c(237, 170, 179), c(197, 163, 187),
c(157, 145, 183), c(130, 142, 190),
c(234, 129, 143), c(192, 130, 155),
c(161, 129, 166), c(121, 122, 170))
colors <- sapply(colors, function(x)
do.call('rgb', c(as.list(x), maxColorValue = 255)))
comb <- outer(1:4, 1:4, paste0) %>% c()
coldf <- data.frame(col = colors, comb = comb) %>%
mutate(col = as.character(col))
# 生成网格
pts <- rbind(c(0,0), c(0, 4),c(4,4), c(4,0), c(0,0))
sfc <- st_sfc(st_polygon(list(pts)))
grid <- st_make_grid(sfc, n = c(4,4))
Legend <- st_sf(geom = grid, label = coldf$col)
Legend$col <- coldf$col
# 可视化
plot(st_geometry(Legend), col = Legend$col, border = NA)
通过给矢量对象及其属性变量一个固定的命名,在更换绘图数据或变量时只需在下面代码中相应位置修改即可:
Shp <- usa %>%
mutate(Var01 = hh_income) %>%
mutate(Var02 = travel_time)
- 这里和转载推文一样,使用
hh_income
和travel_time
作为绘图映射变量。
生成示例对象的边界、将属性变量由连续变量转换为分类变量:
Border <- st_union(Shp)
Shp %>%
mutate(A = as.numeric(cut(Var01, include.lowest = T,
breaks = quantile(Var01, na.rm = T,
probs = c(0, 0.25,
0.5, 0.75, 1)))),
B = as.numeric(cut(Var02, include.lowest = T,
breaks = quantile(Var02, na.rm = T,
probs = c(0, 0.25,
0.5, 0.75, 1)))),
comb = paste0(A, B)) %>%
left_join(coldf, by = "comb") -> Shp
- 这里使用的是四分位分类法,如果有需要在
probs
参数中可以修改相应的分位数,或者选用其他分类方法。
3 常用工具包
R语言中常用的绘图系统主要是基础绘图系统graphics
和ggplot2
两种,另外,tmap
是专门绘制地图的工具包。本文分别使用这三个常用工具包来绘制双变量填充地图。
3.1 graphics
绘制方法如下:
# 主图
par(fig = c(0,0.9,0,1), plt = c(0,1,0.05,1), family = "mono")
plot(st_geometry(Shp), col = Shp$col, border = NA)
plot(Border, cex = 1, lwd = 2, add = T)
# 图例
par(fig = c(0.85,1,0.1,0.35), plt = c(0,1,0.1,1))
par(new = T, mgp = c(0,0,-2))
plot(st_geometry(Legend), col = Legend$col, border = NA)
mtext("hh_income", side = 1)
mtext("travel_time", side = 2)
- 使用
graphics
工具包中的函数绘制地图需要加载sf
工具包;- 通过
par
函数的fig
参数控制主图和图例的位置,该参数的元素使用的是相对尺度,即把内边框的长、宽作为1个单位长度,具体可见推文graphics | 基础绘图系统(二)—— 绘图参数及par函数中的布局参数;mtext
函数中需要修改相应的变量或标签名称。
3.2 ggplot2
绘制方法如下:
library(ggplot2)
# 主图
ggplot(Shp) +
geom_sf(aes(fill = I(col)), col = NA) +
theme_void() +
theme(text = element_text(family = "mono"))-> main
# 图例
ggplot(Legend) +
geom_sf(aes(fill = I(col)), col = NA) +
labs(x = "hh_income", y = "travel_time") +
theme_void() +
theme(text = element_text(family = "mono"),
axis.title = element_text(),
axis.title.y = element_text(angle = 90)) -> legend
# 组合
library(cowplot)
ggdraw() +
draw_plot(main, 0, 0, 0.9,1) +
draw_plot(legend, 0.75, 0.05, 0.25, 0.25)
- 拼接
ggplot2
工具包绘制的图形需要借助cowplot
工具包,因此需要对主图和图例分别进行命名;draw_plot
函数同样使用的是相对尺度的坐标方式。
3.3 tmap
绘制方式如下:
library(tmap)
# 主图
tm_shape(Shp) +
tm_fill(col = "col") +
tm_shape(Border) +
tm_borders(lwd = 2) +
tm_compass(position = c(0.75, 0.85), size = 2.5) +
tm_scale_bar(position = c(0.6, 0.01), text.size = 0.8) +
tm_legend(show = F) +
tm_layout(frame = F, fontfamily = "mono") -> main
main
# 图例
tm_shape(Legend) +
tm_fill("col") +
tm_xlab("hh_income") +
tm_ylab("travel_time") +
tm_layout(frame = F, fontfamily = "mono") -> legend
legend
主图
图例
tmap
作为专门的地图绘制工具包,其语法结构类似于ggplot2
,因此对于习惯使用ggplot2
绘图的读者很容易入门和掌握。
目前小编还没发现完美拼接tmap
绘制的地图的方法,tmap_arrage
函数达不到相应的效果:
tmap_arrange(main, legend, ncol = 2,
widths = c(0.75, 0.25))
- 该函数只能将主图和图例拼接成并列的两列,而不能将图例缩小放到右下角;替代方法是分别导出主图和图例,然后使用Adobe Illustrator软件进行手动调整。
4 比较
4.1 优劣比较
这三个工具包各有优劣:
graphics
使用自有参数就可以实现灵活的图形拼接功能,运行速度也较快,但是绘制地图时无法添加指北针和比例尺等地图要素;ggplot2
是比较流行的绘图工具包,其绘制地图的功能可能有限,但它有诸多拓展包,从而可以实现灵活拼接、添加指北针和比例尺等功能,不过这也增加了其学习难度;tmap
作为专业的地图绘制工具包,可以很方便地添加各种地图要素,缺点是它的图形拼接功能比较有限,而且加载该工具包极为耗时。
4.2 颜色参数比较
这三个工具包中的绘图函数都拥有多个与颜色相关的参数,辨析如下:
graphics
中的plot
函数,col
参数不能用于映射,其取值只能是颜色编码,并按照行序依次循环赋予给相应的空间单元;palette
参数用于映射,并结合breaks
和nbreaks
参数使用;border
参数用于给空间单元的边界上色。这里使用的是col
参数并设置border = NA
,以消除内部边界;ggplot2
中的geom_sf
函数,fill
和col
参数均有映射和非映射两种用法,其中前者用于空间单元的填充色,后者用于给边界上色。这里使用fill
的映射用法并设置col = NA
,以消除内部边界;tmap
中的tm_fill
函数,该函数只针对填充色进行设置,col
参数有映射和非映射两种用法,无论是否映射,其取值都需要加引号""
;palette
参数为非映射用法,用于为col
参数的映射提供对应的颜色编码;若col
参数映射的变量本身就是颜色编码时,则会忽略palette
参数;设置边界需要调用tm_border
函数。这里设置col = "col"
,其中col变量本身为颜色编码,不调用tm_border
函数则默认不显示边界。