This graphic depicts the rankings of the top 10 most populous countries based on their Total Fertility Rate (TFR) from the 1950s to 2021. It showcases the dynamic shifts in fertility rates among these nations over seven decades.
A4 Size Viz
Our World in Data
Public Health
Author
Aditya Dahiya
Published
July 13, 2024
The data used in this graphic is sourced from the United Nations’ World Population Prospects(2022) and processed by Our World in Data. It encompasses the Total Fertility Rate (TFR) for the top 10 most populous countries from the 1950s to 2021. TFR is the average number of children a woman is expected to have during her lifetime. The graphic employs a bump chart to illustrate changes in fertility rate rankings over seven decades, highlighting significant trends such as the consistently high fertility rates in Pakistan, Bangladesh, and Nigeria, and the dramatic decline in China due to the “One Child Policy.” This visualization underscores the dynamic nature of fertility rates among the world’s most populous nations. (Inspired from David Sjoberg’s example).
How I made this graphic?
Getting the data
Code
# Data Import and Wrangling Toolslibrary(tidyverse) # All things tidylibrary(owidR) # Get data from Our World in R# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text supportlibrary(showtext) # Display fonts in ggplot2library(colorspace) # To lighten and darken colourslibrary(patchwork) # Combining plots# ggbump package for Bump-Charts and Sigmoid lines# install.packages("ggbump")library(ggbump) # For bump charts search1 <- owidR::owid_search("fertility")df1 <-owid("children-per-woman")popdf <-owid("population-with-un-projections")
Visualization Parameters
Code
# Font for titlesfont_add_google("News Cycle",family ="title_font") # Font for the captionfont_add_google("Saira Extra Condensed",family ="caption_font") # Font for plot textfont_add_google("Roboto Condensed",family ="body_font") showtext_auto()# Colour Palettemypal <-rev(paletteer::paletteer_d("LaCroixColoR::Lime")[c(1,2,4:6)])# Background Colourbg_col <-"grey95"text_col <-"grey10"text_hil <-"grey25"# Base Text Sizebts <-80plot_title <-"Fertility Rate Rankings among most populous Nations"plot_subtitle <-"The rankings of most populous countries based on their Total Fertility Rate (TFR) from the 1950 to 2021. TFR is the average number of children a woman is expected to have during her lifetime. Notice the consistently high fertility rates in Pakistan, Bangladesh, and Nigeria, and the significant decline in China's fertility due to its One Child Policy. The number in the coloured bands shows the TFR. Below the flags are country's name and population."data_annotation <-"About the Data: This data on fertility rates, sourced from the United Nations' World Population Prospects (2022) and processed by Our World in Data, spans from 1950 to 2021. It reflects the average number of live births per woman, with age-specific rates, providing insights into global fertility trends across decades."# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""xtwitter_username <-"@adityadahiyaias"social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username} </span>")social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")plot_caption <-paste0("**Data:** United Nations & Our World in Data | ","**Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)
Data Wrangling
Code
# A clean tibble for the fertility levels in each yeardf2 <- df1 |>as_tibble() |> janitor::clean_names() |>rename(fertility = fertility_rate_sex_all_age_all_variant_estimates) |>filter(!is.na(code)) |>filter(entity !="World") |>select(-countries_continents) |>filter(!str_detect(code, "OWID"))# A clean tibble of populations for each coutnry in each yearpopdf1 <- popdf |>as_tibble() |> janitor::clean_names() |>filter(!is.na(code)) |>filter(entity !="World") |>mutate(population =ifelse(is.na(population_sex_all_age_all_variant_estimates), population_sex_all_age_all_variant_medium, population_sex_all_age_all_variant_estimates ) ) |>select(-c(population_sex_all_age_all_variant_estimates, population_sex_all_age_all_variant_medium))# Tibble for names of continents and ISO-2 country codes# to be used for flagsdf_continents <- rnaturalearth::ne_countries() |>as_tibble() |> janitor::clean_names() |>select(iso_a3, iso_a2, continent) |>rename(code = iso_a3) |>mutate(country =str_to_lower(iso_a2), .keep ="unused")x_axis_labels <-c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s", "2010s", "2021")# A tibble to use for final computation and country selectiondf3 <- df2 |>left_join(popdf1) |>left_join(df_continents) |>drop_na() |>filter(year >=1951& year <=2020) |>mutate(year =cut( year, breaks =seq(1950, 2020, 10), labels =1:7 ) ) |>group_by(continent, entity, country, year) |>summarise(fertility =weighted.mean(fertility, w = population, na.rm = T),population =mean(population, na.rm = T) ) |>mutate(year =as.numeric(year)) |>ungroup() |>bind_rows( df2 |>left_join(popdf1) |>left_join(df_continents) |>drop_na() |>filter(year ==2021) |>mutate(year =8) ) |>select(-code)# Countries to displaysel_cons <- df3 |>group_by(year) |>slice_max(population, n =10) |>pull(entity) |>unique()# Levels of countries for colour scaleslevels_sel_cons <- df3 |>filter(entity %in% sel_cons) |>filter(year ==1) |>arrange(desc(fertility)) |>pull(entity)# Tibble for final plotting with ggplot2df4 <- df3 |>filter(entity %in% sel_cons) |>group_by(year) |>arrange(desc(fertility)) |>mutate(top_rank =row_number()) |>ungroup() |>mutate(entity =fct(entity, levels = levels_sel_cons))# Adding dummy data to extend bump lines along x-axisdf4 <- df4 |>bind_rows(df4 |>filter(year ==1) |>mutate(year =0.5)) |>bind_rows(df4 |>filter(year ==8) |>mutate(year =8.5))
Visualization: Credits to {ggbump} by (Sjoberg 2020)