suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(jsonlite))
suppressPackageStartupMessages(library(ggtext))
suppressPackageStartupMessages(library(gapminder))
suppressPackageStartupMessages(library(ggh4x))
Images as Facet Labels in ggplot2
In this post, I will show you how to replace the facet labels in ggplot2 with images. The images I’ll use in this post are country flags. Let’s start with loading the required packages:
Data
I am going to get the images of flags of countries that are in the gapminder
dataset. The flag images are available in this Github repo. First, {jsonlite} will help in getting a json of country codes and names:
<- jsonlite::read_json("https://raw.githubusercontent.com/hampusborgos/country-flags/main/countries.json") |>
country_code data.frame() |>
pivot_longer(cols = everything(), names_to = "abb", values_to = "country") |>
mutate(abb = tolower(abb),
abb = stringr::str_remove(abb, "\\.$"))
head(country_code)
# A tibble: 6 × 2
abb country
<chr> <chr>
1 ad Andorra
2 ae United Arab Emirates
3 af Afghanistan
4 ag Antigua and Barbuda
5 ai Anguilla
6 al Albania
Next, I join the country_code
with gapminder
:
<- gapminder |>
gm_joined left_join(country_code, by = "country") |>
drop_na(abb)
head(gm_joined)
# A tibble: 6 × 7
country continent year lifeExp pop gdpPercap abb
<chr> <fct> <int> <dbl> <int> <dbl> <chr>
1 Afghanistan Asia 1952 28.8 8425333 779. af
2 Afghanistan Asia 1957 30.3 9240934 821. af
3 Afghanistan Asia 1962 32.0 10267083 853. af
4 Afghanistan Asia 1967 34.0 11537966 836. af
5 Afghanistan Asia 1972 36.1 13079460 740. af
6 Afghanistan Asia 1977 38.4 14880372 786. af
I know that many countries are dropped as not all countries are stored with the exact same string in both dataframes. But I’m not going to worry about that in th is post :)
Since each country flag is stored as a png in the png250px
folder on the repo, I create a url based on the country abbreviation as a new column. This will be helpful in downloading the flag images in the next step.
<- gm_joined |>
gm_joined mutate(url = paste0("https://github.com/hampusborgos/country-flags/blob/main/png250px/", abb, ".png?raw=true"))
<- gm_joined |>
country_flag_urls distinct(country, url) |>
mutate(country = gsub(" ", "", country))
<- country_flag_urls$url
urls names(urls) <- country_flag_urls$country
head(country_flag_urls)
# A tibble: 6 × 2
country url
<chr> <chr>
1 Afghanistan https://github.com/hampusborgos/country-flags/blob/main/png250px/…
2 Albania https://github.com/hampusborgos/country-flags/blob/main/png250px/…
3 Algeria https://github.com/hampusborgos/country-flags/blob/main/png250px/…
4 Angola https://github.com/hampusborgos/country-flags/blob/main/png250px/…
5 Argentina https://github.com/hampusborgos/country-flags/blob/main/png250px/…
6 Australia https://github.com/hampusborgos/country-flags/blob/main/png250px/…
Now that I have the url to each country flag, I am ready to download the images to a folder named flags
:
download.file(urls, paste0("flags/", names(urls), ".png"), mode="wb")
Theme for flags
The trick is to use ggtext::element_markdown()
for facet strips to replace the facet labels with images. Therefore, I first generate the markdown for reading images:
<- paste0("<img src=", list.files("flags/", full.names = TRUE), " width='100'/>")
flag_markdown
names(flag_markdown) <- names(urls)
head(flag_markdown)
Afghanistan
"<img src=flags/Afghanistan.png width='100'/>"
Albania
"<img src=flags/Albania.png width='100'/>"
Algeria
"<img src=flags/Algeria.png width='100'/>"
Angola
"<img src=flags/Angola.png width='100'/>"
Argentina
"<img src=flags/Argentina.png width='100'/>"
Australia
"<img src=flags/Australia.png width='100'/>"
Now each country name and corresponding markdown for its flag image is available in flag_markdown
. I also add the continents to this vector so that the plot contains information about the continents as well as countries:
<- as.character(unique(gm_joined$continent))
continents names(continents) <- continents
<- c(flag_markdown, continents) flag_markdown
Next, I create a theme that will use ggtext::element_markdown()
to replace the strip text with image:
<- function(base_size = 20,
theme_flag title_size = 20,
...){# CUSTOM THEME:
::theme_minimal(base_size = base_size) +
ggplot2::theme(
ggplot2# title
plot.title = element_text(size = title_size),
plot.title.position = "plot",
#strip
strip.text.x = element_markdown(size = 30),
...
) }
Flags!
To demonstrate the use of ggtext::element_markdown()
, I’m going to plot life expectancy over GDP per capita in different countries. I also utilize the ggh4x::facet_nested_wrap()
function to label both the continents and countries:
|>
gm_joined filter(country %in% country_flag_urls$country) |>
ggplot(mapping = aes(gdpPercap, lifeExp)) +
geom_smooth() +
labs(x = "GDP", y = "Life Expectancy") +
facet_nested_wrap(~ continent + country, nest_line = TRUE, labeller = as_labeller(flag_markdown),
scales = "free",
ncol = 5) +
theme_flag()