数据可视化方面,我可是用过不少的 R 包,在生产实践上,大量使用的 R 包是 plotly 包,自己出于兴趣又嫌 plotly 包太笨重,遂在本博客网站上,大量使用 echarts4r 包。
- R 语言制作地区分布图及其动画(echarts4r)
- Gapminder:关注差异、理解变化(echarts4r)
- 动画制作与 Plotly Python(plotly)
- 用 Python 语言开发 Shiny 应用(plotly)
- 开发企业级 Shiny 应用的技术栈(plotly)
- 交互式网页图形与 R 语言(plotly 和 echarts4r)
为什么又介绍了一个新的用于数据可视化的 R 语言扩展包?因为在图形种类丰富的前提下, gglite 包又轻量又原生支持图形语法,它站在 Ant G2 的肩膀上。哦,对了!它还扔掉了对 htmlwidgets 的依赖!简直难以置信!我在博客中使用它,不需要每次上传一堆 JS 文件了。写技术博客的痛,谁懂呀?
考虑到实用性,本文用 gglite 包制作最常用的四种数据可视化图形。
1 气泡图
散点图是探索连续型变量之间关系的有效方式,气泡图更加了一个维度。一个城市(邵阳市)的自然增长率与它的城镇化率的关系如下图所示。
# 数据来源:邵阳市统计局发布的 2024 年统计年鉴
# 2023 年邵阳市各地区人口相关数据
shao_yang <- read.table(text = "
地区 自然增长人口 自然增长率 城镇人口 城镇化率
双清 -736 -2.82 28.83 90.07
大祥 -345 -1.09 32.02 88.16
北塔 -15 -0.15 9.94 80.36
新邵 283 0.35 27.36 46.7
邵阳 1691 1.62 34.03 47.63
隆回 1746 1.36 44.04 44.93
洞口 620 0.7 33.01 50.24
绥宁 105 0.28 12.12 42.8
新宁 96 0.15 23.76 47.73
城步 169 0.61 9.85 44.55
武冈 522 0.64 31.97 51.56
邵东 -260 -0.2 58.53 59.01
", header = T, sep = " ")
library(gglite)
g2(data = shao_yang, 自然增长率 ~ 城镇化率, size = ~城镇人口) |>
mark_point(
encode = list(color = "城镇人口", text = "地区"),
tooltip = list(title = "地区"),
style = list(fillOpacity = 0.5)
) |>
scale_size(type = "log", range = c(5, 25)) |>
scale_color(palette = "inferno") |>
mark_line_x(
data = list(list(城镇化率 = 66.16)),
style = list(stroke = "red", lineWidth = 3, lineDash = c(3, 3))
) |>
mark_line_y(
data = list(list(自然增长率 = -1.48)),
style = list(stroke = "red", lineWidth = 3, lineDash = c(3, 3))
) |>
legend_size(FALSE) |>
legend_color(position = "right", title = "城镇人口(万)") |>
mark_text(encode = list(text = "地区")) |>
axis_x(title = "城镇化率(%)") |>
axis_y(title = "自然增长率(‰)") |>
titles(main = "2023 年邵阳市各地区人口自然增长率与城镇化率的关系",
subtitle = "数据源:邵阳市统计局") |>
theme_academy()
根据国家统计局的国民经济和社会发展统计公报,2023 年中国城镇化率为 66.16% ,自然增长率为 -1.48‰,将该数据视为全国平均水平,即图中两条虚线的交叉点,各区县被分割成四个象限。第一象限,区县数量最多,未来发展空间大,发展中地区。第二象限,跑在全国平均水平前面,城镇化率已经很高,发展空间较小,中等发达地区。第三象限,邵阳市下没有区县在这一象限,发展空间很小,欠发达地区。第四象限,发达地区。另,2025 年这两个数据分别为 67.89% 和 -2.41‰。
如图所示,总体上城镇化率越高的地方,自然增长率越低。但是,我们不能说城市化是导致人口数量减少的原因。
- Ant G2 内置的调色板。
- Ant G2 Mark Point 图层。
当数据规模稍大,气泡之间相互重叠时,就不便每个气泡都标注文本,而要借助悬浮提示和笔刷了。根据湖南省统计局发布的《统计年鉴2025》,笔者找到2024 年各区县的常住人口、社零总额和地区生产总值。
# 数据已经整理好
hu_nan <- read.csv(file = "https://xiangyun.rbind.io/data/社会消费品零售总额.csv", header = T)
hu_nan |>
transform(生产总值 = 生产总值 / 10000) |>
g2(社零总额 ~ 生产总值, size = ~常住人口) |>
mark_point(
encode = list(color = "常住人口", text = "区县"),
tooltip = list(title = "区县",
channel = "x", valueFormatter = ".2%"),
style = list(fillOpacity = 0.5)
) |>
scale_size(type = "log", range = c(2, 15)) |>
scale_color(palette = "inferno") |>
legend_size(FALSE) |>
legend_color(position = "right", title = "常住人口(万)") |>
# mark_text(encode = list(text = "区县")) |> # 去掉文本标注
mark_text(data = data.frame(区县 = "邵东市", 社零总额 = 356.67, 生产总值 = 791.5441),
encode = list(x = "生产总值", y = "社零总额", text = "区县")) |>
interact('brushFilter') |> # 笔刷功能
axis_x(title = "生产总值(亿)") |>
axis_y(title = "社零总额(万)") |>
titles(main = "2024 年湖南省各区县社零总额与地区生产总值的关系",
subtitle = "数据源:湖南省统计局") |>
theme_academy()
通过笔刷可以任意圈选区域并放大,气泡之间的距离就拉开了。也可以通过拖动图例上下两端的滑块,根据图例映射的指标筛选数据。
- 如果要对其中某些数据标记,只能另做一份数据。而 ggplot2 包支持在原数据上进行筛选。
g2(data = hu_nan, 固定资产投资增速 ~ 城镇化率, size = ~常住人口) |>
mark_point(
encode = list(color = "常住人口", text = "区县"),
tooltip = list(title = "区县",
channel = "x", valueFormatter = ".2%"),
style = list(fillOpacity = 0.5)
) |>
scale_size(type = "log", range = c(2, 15)) |>
scale_color(palette = "inferno") |>
legend_size(FALSE) |>
legend_color(position = "right", title = "常住人口(万)") |>
mark_text(data = data.frame(区县 = " 邵东市", 固定资产投资增速 = 9.4, 城镇化率 = 59.93),
encode = list(x = "城镇化率", y = "固定资产投资增速", text = "区县")) |>
interact('brushFilter') |> # 笔刷功能
axis_x(title = "城镇化率(%)") |>
axis_y(title = "固定资产投资增速(%)") |>
titles(main = "2024 年湖南省各区县固定资产投资增速与城镇化率的关系",
subtitle = "数据源:湖南省统计局") |>
theme_academy()
从图上看,固定资产投资增速与城镇化率并无明显关联。但是,根据常识,城镇化率低的落后地区往往大搞基础设施投资和建设,因而投资增速比较高,过去城镇化建设的思路就是大搞基建。
# 2005-2023 年邵阳市各区县的出生人口和死亡人口
# 数据已经整理好
shao_yang_pop <- read.csv(file = "https://xiangyun.rbind.io/data/邵阳市人口数据.csv", header = T)
g2(data = shao_yang_pop, 死亡人口 ~ 出生人口, color = ~年份) |>
mark_point(
encode = list(text = "地区"),
tooltip = list(title = "地区"),
style = list(fillOpacity = 0.5)
) |>
scale_color(palette = "inferno") |>
legend_color(position = "right", title = "年份") |>
transform(type = "stackEnter", groupBy = "color") |>
# 入场动画
animate(enter = list(type = 'fadeIn', duration = 2000)) |>
axis_x(title = "出生人口(人)") |>
axis_y(title = "死亡人口(人)") |>
titles(main = "2005-2023 年邵阳市各区县的出生人口和死亡人口",
subtitle = "数据源:邵阳市统计局") |>
theme_academy()
- gglite 包的动画功能仅限于入场动画,即数据可以一组组入场,最后都呈现在一起,不支持转场动画,也就意味着暂不支持汉斯·罗琳那种动画。
如图所示,各区县在过去近20年里,出生人口、死亡人口随时间呈现分层变化。在近些年,各区县死亡人口普遍比出生人口多。
2 折线图
# 数据来源:中国人民银行(央行)货币政策执行季度报告
# 个人住房贷款加权平均利率
lending_rate <- data.frame(
"季度" = c(
"09Q1", "09Q2", "09Q3", "09Q4",
"10Q1", "10Q2", "10Q3", "10Q4",
"11Q1", "11Q2", "11Q3", "11Q4",
"12Q1", "12Q2", "12Q3", "12Q4",
"13Q1", "13Q2", "13Q3", "13Q4",
"14Q1", "14Q2", "14Q3", "14Q4",
"15Q1", "15Q2", "15Q3", "15Q4",
"16Q1", "16Q2", "16Q3", "16Q4",
"17Q1", "17Q2", "17Q3", "17Q4",
"18Q1", "18Q2", "18Q3", "18Q4",
"19Q1", "19Q2", "19Q3", "19Q4",
"20Q1", "20Q2", "20Q3", "20Q4",
"21Q1", "21Q2", "21Q3", "21Q4",
"22Q1", "22Q2", "22Q3", "22Q4",
"23Q1", "23Q2", "23Q3", "23Q4",
"24Q1", "24Q2", "24Q3", "24Q4",
"25Q1", "25Q2"
),
"利率" = c(
4.45, 4.34, 4.38, 4.42, 4.63, 4.95, 5.03, 5.34, 6.17,
6.83, 7.36, 7.62, 7.43, 6.68, 6.20, 6.22, 6.27, 6.29, 6.39,
6.53, 6.70, 6.93, 6.96, 6.25, 6.01, 5.53, 5.02, 4.70, 4.63,
4.55, 4.52, 4.52, 4.55, 4.69, 5.01, 5.26, 5.42, 5.60, 5.72,
5.75, 5.68, 5.53, 5.55, 5.62, 5.60, 5.42, 5.36, 5.34, 5.37,
5.42, 5.54, 5.63, 5.49, 4.62, 4.34, 4.26, 4.14, 4.11, 4.02,
3.97, 3.69, 3.45, 3.31, 3.09, 3.13, 3.06
)
)
g2(data = lending_rate, 利率 ~ 季度) |>
mark_line(
tooltip = list(title = ""),
encode = list(color = "#FC8D62")
) |>
style_mark(lineWidth = 2) |>
mark_point(
tooltip = list(title = ""),
encode = list(color = "#FC8D62")
) |>
axis_x(title = "季度") |>
axis_y(title = "个人住房贷款利率(%)") |>
titles(
main = "2009 - 2025 年个人住房贷款加权平均利率",
subtitle = "数据源:中国人民银行"
) |>
theme_academy()
3 柱形图
3.1 百分比堆积柱形图
# 邵阳市 2016-2023 年第一、二、三产业生产总值
shaoyang_gdp <- data.frame(
"年份" = rep(c(2016:2023), each = 3),
"产业" = rep(c("第一产业", "第二产业", "第三产业"), 8),
"生产总值" = c(
3268118, 5437376, 6597083,
3334662, 5951343, 7628990,
2958165, 6268262, 8600054,
3513128, 5956103, 12055536,
3976168, 6937583, 11483810,
4088812, 7861750, 12664786,
4352147, 8329085, 13310599,
4361137, 8976487, 13976527
)
)
g2(data = shaoyang_gdp, 生产总值 ~ 年份, color = ~产业) |>
mark_interval(tooltip = list(channel = "y", valueFormatter = ".2%")) |>
transform("stackY") |>
transform("normalizeY") |>
scale_color(palette = "set2") |>
axis_x(title = "年份") |>
# 格式化 y 轴
axis_y(title = "产业占比(%)", labelFormatter = ".0%") |>
legend_color(title = "产业", position = "right") |>
titles(main = "邵阳市 2016-2023 年第一、二、三产业生产总值",
subtitle = "数据源:邵阳市统计局") |>
theme_academy()
3.2 堆积柱形图
transform(shaoyang_gdp, 生产总值 = 生产总值 / 10000) |>
g2(生产总值 ~ 年份, color = ~产业) |>
mark_interval(tooltip = list(channel = "y", valueFormatter = ".2f")) |>
transform("stackY") |>
scale_color(palette = "set2") |>
legend_color(title = "产业", position = "right") |>
axis_x(title = "年份") |>
axis_y(title = "生产总值(亿元)") |>
titles(main = "邵阳市 2016-2023 年第一、二、三产业生产总值",
subtitle = "数据源:邵阳市统计局") |>
theme_academy()
4 双轴图
示例数据来源见matplotlib 绘制双轴图。
house_loan <- read.table(
text =
"年份 季度 个人住房 同比增速
2011 Q3 6.4 16.2
2012 Q3 7.2 12.0
2013 Q3 8.7 20.9
2014 Q3 10.2 17.6
2015 Q3 12.4 21.6
2016 Q3 16.8 34.9
2017 Q3 21.1 26.2
2018 Q3 24.97 17.9
2019 Q3 29.05 16.8
2020 Q3 33.59 15.7
2021 Q3 37.4 11.3
2022 Q3 38.9 4.1
2023 Q3 38.4 -1.2
2024 Q3 37.56 -2.3
2025 Q3 37.44 -0.3
", header = T, sep = "\t"
)
# 双轴图:柱状图(个人住房,左轴)+ 线图(同比增速,右轴)
g2(house_loan, x = "年份") |>
mark_interval(encode = list(color = "#8DA0CB", y = "个人住房")) |> # 柱状图
axis_y(position = "left", title = "个人住房贷款余额(万亿)") |>
scale_y(independent = TRUE) |> # 独立 y 轴
mark_line(encode = list(color = "#FC8D62", y = "同比增速")) |> # 线图
style_mark(lineWidth = 2) |>
axis_y(position = "right", title = "同比增速 (%)") |> # 右轴标题
mark_point(
encode = list(color = "#FC8D62", y = "同比增速"),
tooltip = list(title = "")
) |>
titles(
main = "2011-2025 年个人住房贷款余额",
subtitle = "数据源:中国人民银行"
) |>
theme_academy()
5 运行环境
本文代码运行的环境信息如下。
sessionInfo()
## R version 4.5.3 (2026-03-11)
## Platform: aarch64-apple-darwin20
## Running under: macOS Tahoe 26.4.1
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Asia/Shanghai
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] gglite_0.0.31
##
## loaded via a namespace (and not attached):
## [1] digest_0.6.39 R6_2.6.1 bookdown_0.46 fastmap_1.2.0
## [5] xfun_0.57.4 blogdown_1.23 cachem_1.1.0 knitr_1.51
## [9] htmltools_0.5.9 rmarkdown_2.31 lifecycle_1.0.5 cli_3.6.6
## [13] sass_0.4.10 jquerylib_0.1.4 compiler_4.5.3 rstudioapi_0.18.0
## [17] tools_4.5.3 evaluate_1.0.5 bslib_0.10.0 yaml_2.3.12
## [21] otel_0.2.0 jsonlite_2.0.0 rlang_1.2.0