generated from jtr13/cctemplate
-
Notifications
You must be signed in to change notification settings - Fork 139
/
animate_your_plots.Rmd
155 lines (116 loc) · 7.62 KB
/
animate_your_plots.Rmd
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# How to animate your plots
Thomas Holvoet
```{r, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
```
```{r}
library(ggplot2)
library(gganimate)
library(dplyr)
library(tidyverse)
library(wpp2019)
library(maps)
```
ggplot is amazing to create numerous visualizations of your data, ranging from histograms and scatter plots to parallel coordinate plots, mosaic plots, and more. However, visualizing time dependencies in a static plot is difficult. You could use one of the axes for time, but this limits the power of your visualization as the axes are your most valuable resource. By creating an animated plot, you gain an additional axis. This allows you to visualize the change over time of the relationship between the other variables, or gives you the real estate to inspect the dynamics of an additional variable. In this tutorial, I will show two ways in which you could animate your plots in R.
## gganimate
The gganimate library provides an intuitive way to create animated plots. You simply need to specify the time axis as `p + transition_time(time)`.
I start with a first example. It is no secret that the world population is rapidly rising. You could create a simple time series plot of the total population over time. However, another important insight is the age distribution of the population. The dynamics of this distribution over time are quite difficult to capture in a static plot. Animating this distribution over time yields some interesting insights. The world's population shows a steady increase in all age categories. You can also see that the number of newborns (0-4) does not increase at a constant rate, but that it rather goes in waves. Moreover, we can see these newborns age into older age groups as child mortality continues to fall.
```{r}
# Prepare the data: get the population per year, country, and age group
data("pop")
pop_by_age <- rbind(popF, popM)
# Some of the country names need to be changed (see later on)
pop_by_age$name <- recode(pop_by_age$name,
"United States of America" = "USA",
"United Kingdom" = "UK",
"Viet Nam" = "Vietnam",
"Bolivia (Plurinational State of)" = "Bolivia",
"Brunei Darussalam" = "Brunei",
"Iran (Islamic Republic of)" = "Iran",
"China, Hong Kong SAR" = "Hong Kong",
"China, Macao SAR" = "China",
"China, Taiwan Province of China" = "Taiwan",
"Dem. Republic of the Congo" = "Democratic Republic of the Congo",
"Dem. People's Rep. of Korea" = "North Korea",
"Lao People's Dem. Republic" = "Laos",
"Venezuela (Bolivarian Republic of)" = "Venezuela",
"United Republic of Tanzania" = "Tanzania",
"Trinidad and Tobago" = "Trinidad",
"Syrian Arab Republic" = "Syria",
"State of Palestine" = "Palestine",
"Russian Federation" = "Russia",
"Republic of Moldova" = "Moldova",
"Cote d'Ivoire" = "Ivory Coast",
"Czechia" = "Czech Republic",
"Congo" = "Republic of Congo"
)
pop_by_age <- pop_by_age %>%
pivot_longer(cols = 4:18, names_to = "Year") %>%
select(age, Year, name, value) %>%
group_by(age, Year, name) %>%
summarise(value = sum(value)) %>%
ungroup() %>%
mutate(age = fct_relevel(age, "5-9", after = 1)) %>%
mutate(age = fct_relevel(age, "100+", after = Inf)) %>%
mutate(Year = strtoi(Year))
# This function will plot the distribution over time for a certain area
plot_dynamic_distribution <- function(area) {
pop_by_age %>%
filter(name == area) %>%
ggplot(aes(x = age, y = 1000 * value)) +
geom_bar(stat = 'identity', fill = "cornflowerblue") +
transition_time(Year) +
labs(title = paste(area, " Population in {frame_time}"), y = "Population", x = "Age") +
theme(axis.text.x = element_text(angle = 90))
}
```
```{r}
plot_dynamic_distribution("World")
```
For the total population, the distribution does stay more or less the same over the years. Japan is contending with an aging population. This can be very clearly observed in the dynamic age distribution plot below. It starts off similar to the world population, but the younger population starts decreasing significantly as the weight shifts towards the elderly.
```{r}
plot_dynamic_distribution("Japan")
```
## Using the `animate.hook` Hook
Maps are another great example where an additional time axis could come in handy. Generally, plots on maps are quite limiting. When color-coding the areas, only one variable can be plotted at a time. By creating an animation of the map the evolution over time of this variable can also be visualized.
The `gganimate` package does not allow for evolving the color over time. We can take a more flexible approach than `gganimate` to create an animated plot of the map. We first create multiple plots corresponding to different points in time. These plots are printed to the output of the R-markdown code block (do not forget actually printing them). We specify `{r, animation.hook='gifski', interval=0.3}` in the code block. This will combine all outputted plots into one GIF!
As an example, we will look at the fraction of 50+ population per country. We already saw that for Japan, the weight of the population shifted towards the elderly. What does this evolution look like on a world scale? We can see that population aging is a new phenomenon that mainly affects developed countries.
```{r}
# Prepare the data: calculate the fraction of 50+ population per country, per year
pop_by_old <- pop_by_age
pop_by_old$old = as.numeric(pop_by_old$age) >= 11
pop_by_old <- pop_by_old %>%
group_by(Year, name) %>%
mutate(total = sum(value)) %>%
ungroup()
pop_by_old <- pop_by_old %>%
group_by(Year, old, name) %>%
summarise(frac = sum(value) / total) %>% ungroup() %>%
unique %>%
filter(old)
```
```{r, animation.hook='gifski', interval=0.3}
world_map <- map_data("world")
world_map <- subset(world_map, region != "Antarctica")
# Iterate over the time axis to print multiple plots, these will be combined by the animation.hook
for (y in unique(pop_by_old$Year)) {
print(pop_by_old %>%
filter(Year == y) %>%
filter(name %in% unique(world_map$region)) %>%
select(Year, name, frac) %>%
ggplot() +
geom_map(
dat = world_map, map = world_map, aes(map_id = region),
fill = "white", color = "#7f7f7f"
) +
geom_map(map = world_map, aes(map_id = name, fill = frac)) +
scale_fill_gradient(low = "#fff7bc", high = "#cc4c02", name = "Fraction", limits = c(0, max(pop_by_old$frac))) +
expand_limits(x = world_map$long, y = world_map$lat) +
labs(title = paste("Fraction of 50+ Population in ", y)))
}
```
Creating animated plots is a very valuable skill in your data visualization toolkit. Using `gganimate` one can very easily animate `ggplot` plots. However, it does not work for all plot types. By using the `animation.hook` approach you can animate virtually any plot you create. Because let's be honest, animated plots are cool :)
__Used Resources__
- On `gganimate` https://www.datanovia.com/en/blog/gganimate-how-to-create-plots-with-beautiful-animation-in-r/
- For creating maps https://stackoverflow.com/questions/61838700/query-on-how-to-make-world-heat-map-using-ggplot-in-r
- On the `animation.hook` https://bookdown.org/yihui/rmarkdown-cookbook/animation.html