使用 R 语言绘制三变量填充中国地图

在之前「使用 R 语言绘制历年中国各省市区县地图(小地图版本 + 长版)」课程的基础上,我们今天再来学习下如何绘制双变量填充地图,也同样包含两种版本:

今天我们将会进一步讲解如何使用 R 语言制作三变量填充地图,同样也包含两种版本:

实际上 R 语言中有绘制三元图的包,不过我觉得今天讲解的这个方法会更容易出图,还是非常值得使用。一些使用 R 语言绘制三变量填充地图的教程都是分别绘制三元图和地图,然后再拼在一起,不如今天讲解的这种方法高效。后面我们再讲解拼图方法。

小地图版本

由于三变量填充地图的图例制作较为费事,所以我只提供了 10 阶的。另外制作过程也较为复杂,我就不再讲解了。

在制作地图之前我们需要简单了解下这种三元图如何阅读:

为了给对应的格子生成特定的颜色,我设计了一个“三变量填充地图颜色生成器”:https://observablehq.com/d/ac2462c27dac3e35

该生成器生成的颜色顺序就是按照图中的 1~55:

三元图看起来很绕,但是抛去具体的值,直接看颜色还是很直观的,例如上图中的蓝色就表示这组数据的 B 含量“满满”,A 和 C 都较少。

所以这个图例的使用方法很简单,下面我们结合具体数据来看下。

各城市人口年龄结构

首先读取之前课程中给大家分享的中国地图数据(经过编辑后的):

library(tidyverse)
library(sf)
library(ggspatial)
library(ggnewscale)

# 中国地图通常使用这样的坐标系
mycrs <- "+proj=aea +lat_0=0 +lon_0=105 +lat_1=25 +lat_2=47 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"

# 读取小地图版本的中国城市地图
read_sf("chinacity2020mini/chinacity2020mini.shp") %>%
filter(!is.na(省代码)) -> citymap

# 线条
read_sf("chinacity2020mini/chinacity2020mini_line.shp") %>%
filter(class %in% c("九段线", "海岸线", "小地图框格")) %>%
select(class) -> citylinemap

然后读取和处理个城市人口年龄结构数据(2020 年第七次人口普查结果):

haven::read_dta("七普各市人口数据_各年龄组人口比重、有老年人口的户数、户口登记地在外乡镇街道人口.dta") %>%
filter(!is.na(各年龄组人口占总人口比重_0_14岁人口比重_百分比)) %>%
select(市, 市代码,
年龄0_14岁 = 各年龄组人口占总人口比重_0_14岁人口比重_百分比,
年龄15_64岁 = 各年龄组人口占总人口比重_15_64岁人口比重_百分比,
年龄65岁及以上 = 各年龄组人口占总人口比重_65岁及以上人口比重_百分比) -> mydata

读取把图例的 shp 文件:

read_sf("triscale10/triscale10.shp") -> triscale10

图例的标签位置数据:

readxl::read_xlsx("triscale10/triscale10_label.xlsx") %>%
mutate(cname = c("15~64岁", "0~14岁", "65 岁及以上")) -> trilabel

添加颜色。这些颜色就是使用上面提到的颜色生成器生成的:

triscale10 %>%
mutate(fill = c("#0040ff", "#1f29ea", "#1260cd", "#3c18d3", "#3249b9", "#237aa4", "#590dbc", "#5038a5", "#446493", "#339183", "#7406a5", "#6e2c91", "#645381", "#577b74", "#45a467", "#8f0290", "#8b247f", "#844671", "#7a6965", "#6c8e5b", "#5bb450", "#aa017d", "#a81e6d", "#a43b61", "#9d5b58", "#947b4f", "#879e46", "#77c23c", "#c6006d", "#c6195f", "#c53354", "#c14e4c", "#bc6b45", "#b38a3e", "#a8ac35", "#9ad029", "#e20161", "#e51554", "#e62b4b", "#e64344", "#e45c3d", "#e17837", "#db972f", "#d2b824", "#c6de16", "#ff0059", "#ff114d", "#ff2445", "#ff383f", "#ff4e39", "#ff6733", "#ff832c", "#ffa222", "#ffc514", "#feec00")) -> triscale10

对三个变量进行分组

# 注意 BAC 的对应
mydata %>%
mutate(年龄15_64岁 = cut(年龄15_64岁, breaks = seq(0, 100, by = 10), labels = 1:10),
年龄0_14岁 = cut(年龄0_14岁, breaks = seq(0, 100, by = 10), labels = 1:10),
年龄65岁及以上 = cut(年龄65岁及以上, breaks = seq(0, 100, by = 10), labels = 1:10)) %>%
mutate_at(vars(contains("岁")), as.numeric) %>%
mutate(年龄65岁及以上 = 12 - 年龄0_14岁 - 年龄15_64岁) -> mydata2

匹配颜色:

mydata2 %>%
mutate(groupclass = paste(年龄15_64岁, 年龄0_14岁, 年龄65岁及以上, sep = "-")) %>%
left_join(triscale10) %>%
select(-(ID:geometry)) -> mydata2

匹配地理信息:

citymap %>%
left_join(mydata2) %>%
mutate(fill = if_else(is.na(fill), "gray", fill)) -> citymap2

绘制地图

然后就可以绘图了:

这里的 cnfont 是在 Profile 里面配置的字体,可以学习系列课程「R 语言数据科学」的第一次课配置。

ggplot(citymap2) +
geom_sf(aes(fill = I(fill)), color = "gray", linewidth = 0.01) +
geom_sf(data = citylinemap,
aes(color = class, linewidth = class),
show.legend = F) +
scale_color_manual(
values = c("九段线" = "#A29AC4",
"海岸线" = "#0055AA",
"小地图框格" = "black")
) +
scale_linewidth_manual(
values = c("九段线" = 0.6,
"海岸线" = 0.3,
"小地图框格" = 0.3)
) +
geom_sf(data = triscale10, aes(fill = I(fill)),
linewidth = 0.01) +
geom_text(data = trilabel, aes(X, Y, label = cname),
family = cnfont, size = 3) +
annotation_scale(location = "bl",
width_hint = 0.3,
text_family = cnfont) +
labs(title = "第七次人口普查各城市分年龄组人口比重",
subtitle = "数据处理&绘制:微信公众号 RStata",
caption = "数据来源:2020年分县市人口统计年鉴") +
theme(axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.background = element_rect(fill = "white", color = "white")) +
annotation_north_arrow(
location = "tr",
which_north = "false",
pad_y = unit(0.1, "cm"),
style = north_arrow_fancy_orienteering(
text_family = cnfont
)
) -> p1

ggsave("pic_mini.png", width = 10, height = 8.5, device = png)

可以看到地图上蓝色的城市表示这些城市的劳动力人口比重较高(15~64岁),偏黄色的则表示少年人口比重较老年人口比重高,偏红色则相反。

长版

附件中也提供了长版地图的绘制数据和代码,绘制方法类似,这里就不再赘述了:

点击这里跳转到 RStata 短书平台获取附件:使用 R 语言绘制三变量填充中国地图

评论