ggplot2 绘图中关于图例的一些-Tips~

ggplot2 绘图中关于图例的一些-Tips~

之前有个培训班的小伙伴在会员群里问了个关于 ggplot2 添加图例的问题,所以我今天就帮他解决下!

注意下文代码里面的 cnfont 和 enfont 都是我在 Profile 里面设置的,如果没有设置可以参考系列课程「R 数据科学」设置或者去除相关参数。

案例引入

我们还是从案例入手,下面的案例中我使用的 2021 年 5 月 26 日世界各国的新冠肺炎确诊病例数量的数据。首先我们读取并整理这份数据(下载链接(需要翻墙):https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv):

1
pacman::p_load(readxl, tidyverse, ggplot2, lubridate, scales, tidyr, purrr, ggrepel)

读取处理数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 确诊
# 5 月 26 日的数据不完整,所以筛选 26 号之前的数据
read_csv('time_series_covid19_confirmed_global.csv') %>%
gather(5:ncol(.), key = "date", value = "confirmed") %>%
set_names(c("prov", "country", "lat",
"lon", "date", "confirmed")) %>%
mutate(country = case_when(
country == "Taiwan*" ~ "China",
country == "US" ~ "United States",
T ~ country
)) %>%
group_by(country, date) %>%
summarise(confirmed = sum(confirmed)) %>%
ungroup() %>%
distinct() %>%
mutate(date = mdy(date),
confirmed = if_else(is.na(confirmed), 0, confirmed)) %>%
arrange(country, date) %>%
dplyr::filter(date <= ymd("2021-05-25")) -> df

df 是这样的:

1
DT::datatable(df)

2021 年 5 月 25 日世界各国的新冠肺炎总确诊病例数为:

1
2
3
4
5
6
current_total <- subset(df, date == "2021-05-25") %>%
pull(confirmed) %>%
sum()
current_total

#> [1] 167847607

接下来绘制一幅折线图展示各国的增长趋势:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
df %>%
mutate(confirmed = confirmed / 1000000) -> df1
df1 %>%
ggplot(aes(x = date, y = confirmed, color = country),
size = 1) +
geom_line() +
geom_label_repel(data = subset(df1,
date == ymd("2021-05-25")),
aes(label = paste0(country, ": ",
round(confirmed, 2))),
family = cnfont,
max.overlaps = 100,
show.legend = F) +
theme_ipsum(base_family = cnfont) +
theme(legend.position = "none") +
scale_x_date(breaks = date_breaks("100 days"),
labels = date_format("%Y-%m-%d"),
limits = ymd(c("2020-01-19", "2021-05-25"))) +
scale_color_manual(values = rev(rep(RColorBrewer::brewer.pal(n = 9, name = "Paired"), 22))) +
labs(title = paste0("COVID-19 总确诊人数: ", round(current_total / 100000000, 2), " 亿"),
subtitle = "绘制:微信公众号 RStata | 2021-05-25",
caption = "数据来源: John Hopkins University\n<https://github.com/CSSEGISandData/COVID-19>",
x = "", y = "确诊人数(百万)")

下面我们进入今天的正题,为了方便,我仅仅选择截止 5 月 25 日确诊人数最多的是个国家,这里可以用 top_n 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 查找 5 月 25 日确诊人数最多的前 10 个国家
df %>%
dplyr::filter(date == "2021-05-25") %>%
top_n(10, confirmed) %>%
arrange(-confirmed) %>%
pull(country) -> country_list

# 筛选出这些国家的数据并绘图
df1 %>%
dplyr::filter(country %in% country_list) %>%
ggplot(aes(x = date, y = confirmed, color = country),
size = 1) +
geom_line() +
geom_label_repel(data = subset(df1,
date == ymd("2021-05-25") &
country %in% country_list),
aes(label = paste0(country, ": ",
round(confirmed, 2))),
family = cnfont,
max.overlaps = 20,
show.legend = F) +
theme_ipsum(base_family = cnfont) +
scale_x_date(breaks = date_breaks("100 days"),
labels = date_format("%Y-%m-%d"),
limits = ymd(c("2020-01-19", "2021-05-25"))) +
scale_color_manual(values = RColorBrewer::brewer.pal(n = 10, name = "Paired")) +
labs(title = paste0("COVID-19 总确诊人数: ", round(current_total / 100000000, 2), " 亿"),
subtitle = "绘制:微信公众号 RStata | 2021-05-25",
caption = "数据来源: John Hopkins University\n<https://github.com/CSSEGISandData/COVID-19>",
x = "", y = "确诊人数(百万)",
color = "国家")

这个图例其实是由三个映射生成的,因为我把 color = country 放在了 ggplot() 里面,所以这个参数会传递给下面的三个图层,最后这三个映射复合在一起才形成了这样的图例。

下面我们看一下如果我们把 color 映射为 confirmed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
df1 %>%
dplyr::filter(country %in% country_list) %>%
ggplot(aes(x = date, y = confirmed, color = confirmed),
size = 1) +
geom_line() +
geom_label_repel(data = subset(df1,
date == ymd("2021-05-25") &
country %in% country_list),
aes(label = paste0(country, ": ",
round(confirmed, 2))),
family = cnfont,
max.overlaps = 20,
show.legend = F) +
theme_ipsum(base_family = cnfont) +
scale_x_date(breaks = date_breaks("100 days"),
labels = date_format("%Y-%m-%d"),
limits = ymd(c("2020-01-19", "2021-05-25"))) +
scale_color_gradientn(colors = RColorBrewer::brewer.pal(n = 9, name = "Reds")) +
labs(title = paste0("COVID-19 总确诊人数: ", round(current_total / 100000000, 2), " 亿"),
subtitle = "绘制:微信公众号 RStata | 2021-05-25",
caption = "数据来源: John Hopkins University\n<https://github.com/CSSEGISandData/COVID-19>",
x = "", y = "确诊人数(百万)",
color = "国家")

注意到这个时候这个图看起来就不太对了,这是因为我们把 confirmed 映射为 color 的时候 confirmed 变量也会自动被作为分组变量,所以这个时候我们还需要指定分组变量为 country: group = country

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
df1 %>%
dplyr::filter(country %in% country_list) %>%
ggplot(aes(x = date, y = confirmed,
color = confirmed,
group = country),
size = 1) +
geom_line() +
geom_label_repel(data = subset(df1,
date == ymd("2021-05-25") &
country %in% country_list),
aes(label = paste0(country, ": ",
round(confirmed, 2))),
family = cnfont,
max.overlaps = 20,
show.legend = F) +
theme_ipsum(base_family = cnfont) +
scale_x_date(breaks = date_breaks("100 days"),
labels = date_format("%Y-%m-%d"),
limits = ymd(c("2020-01-19", "2021-05-25"))) +
scale_color_gradientn(colors = RColorBrewer::brewer.pal(n = 9, name = "Reds")) +
labs(title = paste0("COVID-19 总确诊人数: ", round(current_total / 100000000, 2), " 亿"),
subtitle = "绘制:微信公众号 RStata | 2021-05-25",
caption = "数据来源: John Hopkins University\n<https://github.com/CSSEGISandData/COVID-19>",
x = "", y = "确诊人数(百万)",
color = "国家") -> p
p

可以看到,这个时候的图例是连续的渐变色柱条(colorbar),我们还可以通过下面的设置把渐变色柱条变成分组着色的柱条(colorsteps):

1
2
p +
guides(color = guide_colorsteps())

这样的比较好看,最近我比较喜欢使用这种图例。guide_colorsteps() 提供了丰富的方法进行图例样式设计,例如图例的高度:

1
2
p +
guides(color = guide_colorsteps(barheight = grid::unit(5, "cm")))

再例如把图例的方向反过来:

1
2
3
p +
guides(color = guide_colorsteps(barheight = grid::unit(5, "cm"),
reverse = TRUE))

更多设置可以查看帮助文档:

1
?guide_colorsteps()

当然如果你想设置 colorbar() 可以查看 colorbar() 的帮助文档:

1
?guide_colorbar()

例如:

1
2
p +
guides(color = guide_colorbar(barheight = grid::unit(5, "cm"), reverse = TRUE))

这位小伙伴遇到的问题

大家注意到上面的图表的图例都是通过指定一个映射生成的,那么有时候我们会遇到下面的情况,我们先构造一个数据框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
df %>%
dplyr::filter(country %in% country_list[1:2]) %>%
spread(key = "country", value = "confirmed") -> df_wide
df_wide

#> # A tibble: 490 x 3
#> date India `United States`
#> <date> <dbl> <dbl>
#> 1 2020-01-22 0 1
#> 2 2020-01-23 0 1
#> 3 2020-01-24 0 2
#> 4 2020-01-25 0 2
#> 5 2020-01-26 0 5
#> 6 2020-01-27 0 5
#> 7 2020-01-28 0 5
#> 8 2020-01-29 0 6
#> 9 2020-01-30 1 6
#> 10 2020-01-31 1 8
#> # … with 480 more rows

这种数据是宽型数据,我们也可以直接用 df_wide 绘图:

1
2
3
ggplot(df_wide, aes(x = date)) +
geom_line(aes(y = `United States`), color = "#E31A1C") +
geom_line(aes(y = India), color = "#18BC9C")

大家注意到这个时候就没有图例了,因为我们是单独把两个序列绘制出来的,当然想要图例的一个方法就是把宽型数据转换成长型数据(像上面的一样),不过其实也可以这样:

1
2
3
4
5
6
7
ggplot(df_wide, aes(x = date)) +
geom_line(aes(y = `United States`, color = "United States")) +
geom_line(aes(y = India, color = "India")) +
scale_color_manual(values = c(
"United States" = "#E31A1C",
"India" = "#18BC9C"
), name = "country")

看,图例是不是出来了!

或者把宽形数据转换成长形数据即可:

1
2
3
4
5
6
7
8
df_wide %>%
gather(`United States`, India, key = "country", value = "confirmed") %>%
ggplot(aes(x = date, y = confirmed, color = country)) +
geom_line() +
scale_color_manual(values = c(
"United States" = "#E31A1C",
"India" = "#18BC9C"
))

这和上面的结果就是一样的了!大家在实际使用中可以根据自己的需要选择方法。

直播信息

为了让大家能更好的理解上面的内容,欢迎各位培训班的小伙伴参加明晚 8 点的直播课:「ggplot2 绘图中关于图例的一些 Tips~」

  1. 直播时间:2021 年 5 月 27 日晚上 8 点;
  2. 直播地址:腾讯会议(需要报名 RStata 培训班参加)
  3. 如何报名 RStata 培训班,详情可以阅读这篇推文了解:推荐一个学习 R 语言、Stata、效率分析与计量经济学的好地方!

更多详情可点击阅读原文进入 RStata 学院了解(从首页的会员卡专区即可查看和购买会员卡)。

更多关于 RStata 培训班的信息可添加微信号 r_stata 咨询:

ggplot2 绘图中关于图例的一些-Tips~

https://tidyfriday.cn/posts/13570/

作者

Painter

发布于

2021-05-25

更新于

2021-05-31

许可协议

评论