这篇推送使用了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
  • 示例数据来自socvizalbersusa两个工具包,目前它们都只能通过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

这篇论文绘制的双变量填充地图是这样的:



r语言画世界地图 r语言 画地图_编程语言

要想绘制这样的地图,关键是要定义两个方向的渐变颜色,这里直接使用了上述论文所定义的颜色,然后使用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)



r语言画世界地图 r语言 画地图_编程语言_02

通过给矢量对象及其属性变量一个固定的命名,在更换绘图数据或变量时只需在下面代码中相应位置修改即可:

Shp <- usa %>%
  mutate(Var01 = hh_income) %>%
  mutate(Var02 = travel_time)
  • 这里和转载推文一样,使用hh_incometravel_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语言中常用的绘图系统主要是基础绘图系统graphicsggplot2两种,另外,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)



r语言画世界地图 r语言 画地图_数据可视化_03

  • 使用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)



r语言画世界地图 r语言 画地图_可视化_04

  • 拼接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



r语言画世界地图 r语言 画地图_python_05

主图

r语言画世界地图 r语言 画地图_编程语言_06

图例

  • tmap作为专门的地图绘制工具包,其语法结构类似于ggplot2,因此对于习惯使用ggplot2绘图的读者很容易入门和掌握。

目前小编还没发现完美拼接tmap绘制的地图的方法,tmap_arrage函数达不到相应的效果:

tmap_arrange(main, legend, ncol = 2,
             widths = c(0.75, 0.25))



r语言画世界地图 r语言 画地图_r语言画世界地图_07

  • 该函数只能将主图和图例拼接成并列的两列,而不能将图例缩小放到右下角;替代方法是分别导出主图和图例,然后使用Adobe Illustrator软件进行手动调整。

4 比较

4.1 优劣比较

这三个工具包各有优劣:

  • graphics使用自有参数就可以实现灵活的图形拼接功能,运行速度也较快,但是绘制地图时无法添加指北针和比例尺等地图要素;
  • ggplot2是比较流行的绘图工具包,其绘制地图的功能可能有限,但它有诸多拓展包,从而可以实现灵活拼接、添加指北针和比例尺等功能,不过这也增加了其学习难度;
  • tmap作为专业的地图绘制工具包,可以很方便地添加各种地图要素,缺点是它的图形拼接功能比较有限,而且加载该工具包极为耗时。

4.2 颜色参数比较

这三个工具包中的绘图函数都拥有多个与颜色相关的参数,辨析如下:

  • graphics中的plot函数,col参数不能用于映射,其取值只能是颜色编码,并按照行序依次循环赋予给相应的空间单元;palette参数用于映射,并结合breaksnbreaks参数使用;border参数用于给空间单元的边界上色。这里使用的是col参数并设置border = NA,以消除内部边界;
  • ggplot2中的geom_sf函数,fillcol参数均有映射和非映射两种用法,其中前者用于空间单元的填充色,后者用于给边界上色。这里使用fill的映射用法并设置col = NA,以消除内部边界;
  • tmap中的tm_fill函数,该函数只针对填充色进行设置,col参数有映射和非映射两种用法,无论是否映射,其取值都需要加引号""palette参数为非映射用法,用于为col参数的映射提供对应的颜色编码;若col参数映射的变量本身就是颜色编码时,则会忽略palette参数;设置边界需要调用tm_border函数。这里设置col = "col",其中col变量本身为颜色编码,不调用tm_border函数则默认不显示边界。