library(tidyverse)
library(glue)
library(scales)
library(showtext)
library(ggtext)
library(shadowtext)
library(maps)
font_add_google("Ubuntu", "Ubuntu", regular.wt = 400, bold.wt = 700)
showtext_auto()
showtext_opts(dpi = 300)
About the Data
This week we are exploring measles and rubella cases across the world. This data was downloaded from the World Health Organisation Provisional monthly measles and rubella data on 2025-06-12.
Please note that all data contained within is provisional. The number of cases of measles and rubella officially reported by a WHO Member State is only available by July of each year (through the joint WHO UNICEF annual data collection exercise). If any numbers from this provisional data are quoted, they should be properly sourced with a date (i.e. “provisional data based on monthly data reported to WHO (Geneva) as of June 2025”). For official data from 1980, please visit our website: https://immunizationdata.who.int/global/wiise-detail-page/measles-reported-cases-and-incidence
1 Initializing
1.1 Load libraries
1.2 Set theme
<- "#323955"
cool_gray0 <- "#5a6695"
cool_gray1 <- "#7e89bb"
cool_gray2 <- "#a4aee2"
cool_gray3 <- "#cbd5ff"
cool_gray4 <- "#e7efff"
cool_gray5
theme_set(
theme_minimal() +
theme(
# axis.line.x.bottom = element_line(color = '#474747', linewidth = .3),
# axis.ticks.x= element_line(color = '#474747', linewidth = .3),
# axis.line.y.left = element_line(color = '#474747', linewidth = .3),
# axis.ticks.y= element_line(color = '#474747', linewidth = .3),
# # panel.grid = element_line(linewidth = .3, color = 'grey90'),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks.length = unit(-0.15, "cm"),
plot.background = element_blank(),
plot.title.position = "plot",
plot.title = element_text(family = "Ubuntu", size = 18, face = 'bold'),
plot.caption = element_text(
size = 8,
color = cool_gray4,
margin = margin(20, 0, 0, 0),
hjust = 0
),plot.subtitle = element_text(
size = 9,
lineheight = 1.15,
margin = margin(5, 0, 15, 0)
),axis.title.x = element_markdown(
family = "Ubuntu",
hjust = .5,
size = 8,
color = cool_gray1
),axis.title.y = element_markdown(
family = "Ubuntu",
hjust = .5,
size = 8,
color = cool_gray1
),axis.text = element_text(
family = "Ubuntu",
hjust = .5,
size = 8,
color = cool_gray1
),legend.position = "top",
text = element_text(family = "Ubuntu", color = cool_gray1),
plot.margin = margin(25, 25, 25, 25)
) )
1.3 Load this week’s data
<- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-06-24/cases_month.csv')
cases_month <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-06-24/cases_year.csv') cases_year
2 Data analysis
How many country and years?
<- cases_year |> distinct(year) |> nrow()
n_years <- cases_year |> distinct(country) |> nrow()
n_countries <- cases_year |> distinct(year, country) |> nrow()
n_countries_year
print(glue('There are {n_countries} countries in {n_years} years.'))
There are 194 countries in 14 years.
print(glue('There are {n_countries_year} countries in {n_years} years.'))
There are 2382 countries in 14 years.
print(glue('It was expected {n_countries} * {n_years} ({comma(n_countries * n_years)}) records and found {comma(n_countries_year)} ({percent(n_countries_year / (n_countries * n_years))}).'))
It was expected 194 * 14 (2,716) records and found 2,382 (88%).
Seasonality
|>
cases_month filter(region == 'EUR') |>
group_by(year, month) |>
summarize(
mean_melases = mean(measles_total, na.rm = TRUE),
sd = sd(measles_total, na.rm = TRUE)
|>
) ggplot(aes(x = month, y = mean_melases, color = year)) +
geom_point() +
geom_line(aes(group = year))
Cases by year
|>
cases_year group_by(year) |>
summarize(
sum_cases = sum(measles_total, na.rm = TRUE),
|>
) ggplot(aes(x = year, y = sum_cases)) +
geom_point(color = cool_gray0) +
geom_text(aes(label = year)) +
geom_line(color = cool_gray0)
Global Trends
<- map_data("world") |>
world left_join(read_csv('region2iso3.csv'), by = 'region')
<-
world_plot_data |>
world left_join(
|>
cases_year select(iso3, measles_total, year),
by = 'iso3'
)ggplot() +
geom_polygon(data = world_plot_data, aes(x = long, y = lat, group = group, fill = measles_total)) +
coord_fixed(1.2) +
facet_wrap(~year)
What happend in Madagascar in 2019?
|>
cases_year filter(year == 2019) |>
mutate(is_madagascar = country == 'Madagascar') |>
group_by(is_madagascar) |>
summarise(sum_measles = sum(measles_total))
# A tibble: 2 × 2
is_madagascar sum_measles
<lgl> <dbl>
1 FALSE 328110
2 TRUE 213291
3 Transform Data for Plotting
<-
cases_data |>
cases_year filter(year == 2019) |>
mutate(is_madagascar = country == 'Madagascar') |>
mutate(measles_by_1m = measles_total / total_population * 10^6) |>
select(iso3, country, is_madagascar, measles_total, measles_by_1m) |>
arrange(-measles_by_1m)
<-
map_data |> left_join(cases_data, by = 'iso3') world
4 Time to plot!
4.1 Before
|>
map_data ggplot() +
geom_polygon(aes(x = long, y = lat, group = group, fill = measles_total)) +
coord_fixed(1.2) +
theme_grey()
# Circular Plot
<-
data2plot |>
cases_data group_by(is_madagascar) |>
summarise(total = sum(measles_total))
<-
p_circular |>
data2plot ggplot(aes(y = total)) +
geom_col(aes(x = 1, fill = is_madagascar)) +
annotate(
geom = 'text',
x = -1,
y = 0,
label = percent(
|> filter(is_madagascar) |> pull(total) / sum(data2plot$total)
data2plot
),family = 'Ubuntu',
size = 3,
color = cool_gray1,
vjust = .5,
fontface = 'bold'
+
) coord_radial(theta = 'y', expand = FALSE) +
scale_fill_manual(values = c(`TRUE` = "#F01B5B", `FALSE` = cool_gray5)) +
theme(
# Remove axis text
axis.text = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
legend.position = 'bottom',
plot.margin = margin(0, 0, 0, 0)
+
) guides(fill = 'none')
# Top bar plots
<-
p_bars |>
cases_data slice_max(measles_by_1m, n = 5) |>
ggplot(aes(x = measles_by_1m, y = reorder(country, measles_by_1m))) +
geom_segment(
aes(x = 0, xend = measles_by_1m, color = is_madagascar),
lineend = 'round',
linewidth = 2,
show.legend = FALSE
+
) scale_x_continuous(label = comma, breaks = c(0, 3500, 7000)) +
scale_color_manual(values = c(`TRUE` = "#F01B5B", `FALSE` = cool_gray1)) +
labs(x = NULL, y = NULL, title = "Total measles cases (per million)") +
theme(plot.title = element_text(size = 8), plot.margin = margin(0, 0, 0, 0))
|>
map_data ggplot() +
geom_polygon(data = subset(map_data, is_madagascar), aes(
x = long,
y = lat,
color = '#FFD8E4', fill = NA, linewidth = 6) +
), geom_polygon(aes(
x = long,
y = lat,
group = group,
fill = measles_total / 1000000
+
)) coord_fixed(1.2) +
scale_fill_gradient(
low = '#F5F8FF',
high = cool_gray1,
na.value = 'grey98',
breaks = pretty_breaks(n = 4)
+
) theme(
plot.title = element_text(size = 14),
legend.title.position = 'top',
legend.position = c(0, 0),
legend.justification = c(0, 0),
legend.direction = 'horizontal',
# Remove axis text
axis.text = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
+
) annotation_custom(
xmin = 60,
xmax = 110,
ymin = 0,
ymax = -80,
grob = ggplotGrob(p_circular)
+
) annotation_custom(
xmin = 55,
xmax = 190,
ymin = 25,
ymax = 80,
grob = ggplotGrob(p_bars)
+
) annotate(
geom = 'text',
x = 65,
y = 0,
label = str_wrap(
"In that year almost 40% of all measles cases worldwide were registered in Madagascar",
width = 30
),hjust = 0,
family = 'Ubuntu',
size = 3,
color = cool_gray1
+
) labs(
title = "In 2019, a huge measles outbreak hit Madagascar",
fill = "Registered measles cases (M)",
y = NULL,
caption = str_wrap(
"NOTE This visualization offers a preliminary look at the data and may not capture the full complexity of the underlying reality. SOURCE #Tidytuesday 2025-06-17 GITHUB barreiro-r",
width = 110,
|>
) str_replace_all("@", "\n")
+
) guides(
fill = guide_colorbar(
barwidth = 13,
barheight = .3,
title.position = 'top'
) )