The {geofacet} package allows to « arrange a sequence of plots of data for different geographical entities into a grid that strives to preserve some of the original geographical orientation of the entities ».
Like the previous post, it’s interesting if you view each entity as a unit and don’t care for its real size or weight, and don’t want to spend too much time manually finding the best grid.
We will again use the sameCOVID-19 dataset. We manually add the overseas départements once we have found the right grid (by trying different seeds) and adjust Corsica position.
COVID-19 deceased in hospital, by département, for 100 000 inhab.
Hexagon tessellation using the great {geogrid} package.
The départements are the second level of administrative government in France. They neither have the same area nor the same population and this heterogeneity provides a few challenges for a fair and accurate map representation (see the post on smoothing).
However if we are just interested in the départements as units, we can use a regular grid for visualization. Since France is often called the hexagon, we could even use an hexagon tiling (a fractal map !)…
Creating the grid and conserving minimal topological relations and the general shape can be time consuming, but thanks to Geogrid it’s quite easy. The geogrid dev page provides nice examples. We will reuse our code of the COVID19 animation. The resulting GIS file is provided below.
The global shape and relations are well rendered. Deformations are quite important for the small départements around Paris, but the map remains legible.
From the official data by Santé Publique France, we spatially smooth the decease (produced by SPF at the département scale) and normalize by a similarly smoothed population grid. For that we use the {btb} package.
After a classic plot of the Keeling curve (see our former post) used on Wikipedia, we can explore another data visualization. The CO₂ atmospheric concentration, the main cause of the climate warming, is following a seasonal cycle so it could be interesting (or ironic ?) to use a polar plot.
Config and data
We only keep two translations for brevity here…
# Required packages
library(tidyverse)
library(scales)
library(lubridate)
# Translations ------------------------------------------------------------
language <- list(
en_US = list(
locale_lc_time = "en_US.UTF-8",
title = bquote("Monthly mean"~CO[2]~"concentration"),
caption = paste("Data : P. Tans, NOAA/ESRL (www.esrl.noaa.gov/gmd/ccgg/trends/)\nand R. Keeling, Scripps Institution of Oceanography (scrippsco2.ucsd.edu/). Accessed", Sys.Date()),
x = "Year",
y = bquote(CO[2]~"fraction in dry air ("*mu*"mol/mol)"),
x2 = "Month",
y2 = bquote(atop(CO[2]~"fraction in dry air ("*mu*"mol/mol)", "Departure from yearly average")),
title2 = "Seasonal variation"
),
fr_FR = list(
locale_lc_time = "fr_FR.UTF-8",
title = bquote("Moyenne mensuelle de la concentration de"~CO[2]),
caption = paste("données : P. Tans, NOAA/ESRL (www.esrl.noaa.gov/gmd/ccgg/trends/)\net R. Keeling, Scripps Institution of Oceanography (scrippsco2.ucsd.edu/). Accédé le", Sys.Date()),
x = "année",
y = bquote("fraction de"~CO[2]~"dans l'air sec ("*mu*"mol/mol)"),
x2 = "mois",
y2 = bquote(atop("fraction de"~CO[2]~"dans l'air sec ("*mu*"mol/mol)", "en écart à la moyenne annuelle")),
title2 = "Variation saisonnière"
))
# Data --------------------------------------------------------------------
# https://www.esrl.noaa.gov/gmd/ccgg/trends/
co2ml <- read_delim("ftp://aftp.cmdl.noaa.gov/products/trends/co2/co2_mm_mlo.txt",
delim = " ",
locale = locale(decimal_mark = "."),
na = c("-99.99", "-1"),
col_types = "iiddddi",
col_names = c("year", "month", "decimal", "co2", "co2_interpol", "co2_trend", "days"),
comment = "#",
trim_ws = TRUE) %>%
group_by(year) %>%
mutate(year_mean = mean(co2_interpol, na.rm = TRUE),
delta = co2_interpol - year_mean,
vdate = ymd(paste0("2015-", month, "-01"))) %>%
ungroup()
Create the plot for each language and save
We use a virtual date to keep the data in the same January-December interval and we add a partial dataframe to smooth the Dec./Jan. transition and build the spiral.
# Polar plot
for (l in names(language)) {
message(l)
current <- language[[l]]
# format the date in local names
Sys.setlocale("LC_TIME", current$locale_lc_time)
p3 <- co2ml %>%
filter(vdate == "2015-01-01") %>%
mutate(vdate = ymd("2015-12-31"),
year = year -1) %>%
bind_rows(co2ml) %>%
ggplot(aes(vdate, co2_interpol, group = year, color = year)) +
geom_line(size = 1.2) +
scale_x_date(breaks = pretty_breaks(12), labels = date_format("%b")) +
scale_color_viridis_c() +
labs(subtitle = current$title,
x = "",
y = current$y,
color = current$x,
title = paste("Mauna Loa", min(co2ml$year), "-", max(co2ml$year)),
caption = current$caption) +
coord_polar() +
theme_bw() +
theme(axis.title.y = element_text(hjust = .85),
panel.grid.major.y = element_blank(),
panel.grid.minor.x = element_blank(),
panel.border = element_blank(),
plot.caption = element_text(size = 7))
ggsave(p3, file = paste("co2_mauna_loa_polar", l, Sys.Date(), "wp.svg", sep = "_"), width = 20, height = 20, units = "cm", device = svg)
}
60 years of CO₂ increase
Does this new year look good ? Will the spiral cross its path soon ?
The use case is to create the same plot in different languages. I used this technique for Wikipediaplots.
We are going to build a list containing all translations, we will then loop over each language, generating and saving the plot.
# Mauna Loa atmospheric CO2 change
# multi language plot for Wikipedia
# Required packages
library(tidyverse)
library(gridExtra)
library(scales)
library(lubridate)
library(Hmisc)
# Translations ------------------------------------------------------------
language <- list(
en_US = list(
locale_lc_time = "en_US.UTF-8",
title = expression(paste("Monthly mean ", CO[2], " concentration ")),
caption = paste("Data : R. F. Keeling, S. J. Walker, S. C. Piper and A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Accessed ", Sys.Date()),
x = "Year",
y = expression(paste(CO[2], " fraction in dry air (", mu, "mol/mol)")),
x2 = "Month",
y2 = expression(atop(paste(CO[2], " fraction in dry air (", mu, "mol/mol)"), "Departure from yearly average")),
title2 = "Seasonal variation"
),
fr_FR = list(
locale_lc_time = "fr_FR.UTF-8",
title = expression(paste("Moyenne mensuelle de la concentration de ", CO[2])),
caption = paste("données : R. F. Keeling, S. J. Walker, S. C. Piper et A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Accédé le", Sys.Date()),
x = "année",
y = expression(paste("fraction de ", CO[2], " dans l'air sec (", mu, "mol/mol)")),
x2 = "mois",
y2 = expression(atop(paste("fraction de ", CO[2], " dans l'air sec (", mu, "mol/mol)"), "en écart à la moyenne annuelle")),
title2 = "Variation saisonnière"
),
de_DE = list(
locale_lc_time = "de_DE.UTF-8",
title = expression(paste("Monatliche durchschnittliche ", CO[2], "-Konzentration")),
caption = paste("Datei : R. F. Keeling, S. J. Walker, S. C. Piper und A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Zugänglich am", Sys.Date()),
x = "Jahr",
y = expression(paste(CO[2], "-Anteil in trockener Luft (", mu, "mol/mol)")),
x2 = "Monate",
y2 = expression(atop(paste(CO[2], "-Anteil in trockener Luft (", mu, "mol/mol)"), "Abweichung vom Jahresmittel")),
title2 = "Monatliche Variation"
),
es_ES = list(
locale_lc_time = "es_ES.UTF-8",
title = expression(paste("Media mensual de la concentración de ", CO[2])),
caption = paste("dato : R. F. Keeling, S. J. Walker, S. C. Piper y A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Visitada", Sys.Date()),
x = "Año",
y = expression(paste("Fraccion de ", CO[2], " en aire secco (", mu, "mol/mol)")),
x2 = "Mes",
y2 = expression(atop(paste("Fraccion de ", CO[2], " en aire secco (", mu, "mol/mol)"), "Desviación de la media anual")),
title2 = "Variación mensual"
),
cs_CZ = list(
locale_lc_time = "cs_CZ.UTF-8",
title = expression(paste("Průměrné měsíční koncentrace oxidu uhličitého")),
caption = paste("data : R. F. Keeling, S. J. Walker, S. C. Piper a A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Přístupné", Sys.Date()),
x = "rok",
y = expression(paste("koncentrace ", CO[2], " v suchém vzduchu (", mu, "mol/mol)")),
x2 = "měsíc",
y2 = expression(atop(paste("koncentrace ", CO[2], " v suchém vzduchu (", mu, "mol/mol)"), "odchylka od ročního průměru")),
title2 = "Měsíční změna (průměrná roční odchylka)"
),
nn_NO = list(
locale_lc_time = "nn_NO.UTF-8",
title = expression(paste("Gjennomsnittlig månedlig ", CO[2], "-konsentrasjon")),
caption = paste("data : R. F. Keeling, S. J. Walker, S. C. Piper og A. F. Bollenbacher\nScripps CO2 Program (http://scrippsco2.ucsd.edu). Vist", Sys.Date()),
x = "År",
y = expression(paste(CO[2],"-andel i tørr luft (", mu, "mol/mol)")),
x2 = "Måned",
y2 = expression(atop(paste(CO[2],"-andel i tørr luft (", mu, "mol/mol)"),
"Avvik fra årlig gjennomsnitt")),
title2 = "Årlig variasjon"
)
)
# Data --------------------------------------------------------------------
# http://scrippsco2.ucsd.edu/data/atmospheric_co2/primary_mlo_co2_record
# used during US gov shutdown
co2ml <- read_csv("http://scrippsco2.ucsd.edu/assets/data/atmospheric/stations/in_situ_co2/monthly/monthly_in_situ_co2_mlo.csv",
col_names = c("year", "month", "xls_date", "decimal",
"co2", "co2_seas_adj", "fit", "fit_seas_adj",
"co2_filled", "co2_filled_seas_adj"),
col_types = "iiiddddddd",
skip = 57,
na = "-99.99",
comment = "\"") %>%
group_by(year) %>%
mutate(year_mean = mean(co2_filled, na.rm = TRUE),
delta = co2_filled - year_mean,
vdate = ymd(paste0("2015-", month, "-01")))
# Generate the plot for each language -------------------------------------
for (l in names(language)) {
message(l)
current <- language[[l]]
# format the date in local names
Sys.setlocale("LC_TIME", current$locale_lc_time)
# main plot
p1 <- ggplot(co2ml, aes(decimal, co2_filled)) +
geom_line(color = "pink") +
geom_point(color = "red", size = 0.6) +
stat_smooth(span = 0.1) +
scale_x_continuous(breaks = pretty_breaks()) +
scale_y_continuous(breaks = pretty_breaks(4), minor_breaks = pretty_breaks(8)) +
labs(
x = current$x,
y = current$y,
title = current$title,
subtitle = paste("Mauna Loa", min(co2ml$year), "-", max(co2ml$year)),
caption = current$caption) +
theme_bw() +
theme(plot.caption = element_text(size = 7))
# inset plot
p2 <- ggplot(co2ml, aes(vdate, delta)) +
geom_hline(yintercept = 0) +
stat_smooth(span = 0.4, se = FALSE) +
stat_summary(fun.data = "mean_cl_boot", colour = "red", size = 0.3) +
scale_x_date(breaks = pretty_breaks(4), minor_breaks = pretty_breaks(12), labels = date_format("%b")) +
labs(
x = current$x2,
y = current$y2,
title = current$title2) +
theme_bw()
# merge the plots and export in SVG
p1 + annotation_custom(grob = ggplotGrob(p2), xmin = 1957, xmax = 1991, ymin = 361, ymax = 412)
ggsave(file = paste("co2_mauna_loa", l, Sys.Date(), "wp.svg", sep = "_"), width = 20, height = 20, units = "cm", device = svg)
}