Global fertility: what’s in the data

Preparation · 2026-10-09 · Candidate grammar layer: geoms and stats

Published

October 9, 2026

NotePreparation notes, not a session writeup

These notes describe a candidate dataset before any session. The group has made no design decisions. The two Rosling-style figures were asked for by the facilitator as a familiar reference point, and the candidate mappings at the end are options for the group to choose from. Drafted by Claude Opus 5.5 from the output of scripts/describe_data.R and the Rdatasets documentation for dslabs::gapminder.

The data is the gapminder table from Rafael Irizarry’s dslabs package, which compiles Gapminder country indicators. It has one row per country per year, giving each country’s total fertility rate (births per woman) alongside life expectancy, infant mortality, population and GDP.

The data is cached in data/gapminder-fertility.csv. To refresh it from the Rdatasets copy:

readr::read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/dslabs/gapminder.csv") |>
  dplyr::select(-rownames) |>
  readr::write_csv("data/gapminder-fertility.csv")

Licence: dslabs is Artistic-2.0, and Gapminder’s data is CC BY 4.0. Cite Gapminder.

The description

This is the verbatim output of the repository’s deterministic describe script, so it is the same whichever agent runs it.

rscript <- file.path(R.home("bin"), "Rscript")
cat(system2(rscript, c("--vanilla", here::here("scripts/describe_data.R"),
                       here::here("data/gapminder-fertility.csv")),
            stdout = TRUE), sep = "\n")
gapminder-fertility.csv: 10,545 rows x 9 cols
Grain: one row per country x year

 variable         type       levels %NA  examples                     hint               
 country          discrete     185   0.0 Albania, Algeria, Angola     x / y as categories
 year             continuous    57   0.0 1960, 1988, 2016             time axis (x)      
 infant_mortality continuous  1761  13.8 1.5, 88.8, 276.9             x / y / size       
 life_expectancy  continuous  3095   0.0 13.2, 60.43, 83.9            x / y / size       
 fertility        continuous   752   1.8 0.84, 4.65, 9.22             x / y / size       
 population       continuous 10352   1.8 31240, 5016000, 1.376e+09    x / y / size       
 gdp              continuous  7562  28.2 40400000, 7.833e+09, 1.17... x / y / size       
 continent        discrete       5   0.0 Europe, Africa, Americas     colour / facet     
 region           discrete      22   0.0 Southern Europe, Northern... x / y as categories

Colour candidates (2-8):   continent (5) 
Facet candidates (4-12):   continent (5) 

Reading it

  • What it is. Country indicators compiled by Gapminder, covering 185 countries from 1960 to 2016.
  • Grain. One row per country per year, 10,545 rows. There’s no reshaping to do: the data is already tidy.
  • Types in grammar terms. fertility, life_expectancy, infant_mortality, population and gdp are continuous. year is temporal and works as a continuous x. continent (5 levels) is the only discrete variable in the range for colour or facets; region (22) and country have too many levels for either.
  • Missingness has a shape. gdp is 28.2% missing. It stops after 2011 and is patchy before 1980. fertility (1.8% missing) and infant_mortality (13.8%) have no values at all for 2016, so in practice the fertility series ends in 2015. A line plot that runs to 2016 will stop short without warning.
  • Range. Fertility runs from 0.84 (Macao, China, 2003) to 9.22 (Yemen, 1983).

Rosling-style reference figures

Anyone who watched Hans Rosling’s talks will recognise these: fertility on x, life expectancy on y, one bubble per country sized by population and coloured by continent. They are here as a shared starting point to react to, not as the group’s choices. The facilitator asked for them in preparation, and the group can keep, change or throw away any slot.

The animated original shows time by moving the bubbles. A static page has to put time somewhere else, and the two figures below make that choice in two different ways.

Code
decades <- seq(1960, 2010, by = 10)

rosling <- gm |>
  filter(!is.na(fertility), !is.na(life_expectancy), !is.na(population))

continent_cols <- c(Africa = "#E69F00", Americas = "#D55E00", Asia = "#009E73",
                    Europe = "#0072B2", Oceania = "#CC79A7") # Okabe-Ito

Time as facets: one panel per decade

Code
rosling |>
  filter(year %in% decades) |>
  arrange(desc(population)) |> # draw big bubbles first so small ones stay visible
  ggplot(aes(x = fertility, y = life_expectancy,
             size = population, colour = continent)) +
  geom_point(alpha = 0.7) +
  facet_wrap(~ year) +
  scale_size_area(max_size = 12, guide = "none") +
  scale_colour_manual(values = continent_cols) +
  scale_x_reverse() +
  labs(x = "Total fertility rate (births per woman, reversed)",
       y = "Life expectancy (years)", colour = NULL,
       title = "Fewer children, longer lives, 1960 to 2010") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")
Figure 1: Each panel is one year at the start of a decade. Read across the panels to see the cloud of countries move from top left to bottom right.

The x axis is reversed, which is a decision to argue about. Reversed, the movement over time runs left to right, and “fewer births” sits on the same side as “longer lives”. Rosling himself plotted fertility the natural way round (high on the right), so the countries moved right to left. Neither is wrong. But the choice changes what readers see as progress.

Two figures from the data anchor the panels. In 1960 the median country had 6.19 births per woman and a life expectancy of 56.3; by 2010 those medians were 2.38 and 73.6.

Time as fading: older years more transparent

Code
latest <- rosling |> filter(year == max(year))

rosling |>
  arrange(country, year) |>
  ggplot(aes(x = fertility, y = life_expectancy, colour = continent)) +
  geom_path(aes(group = country, alpha = year), linewidth = 0.4) +
  geom_point(data = latest, aes(size = population), alpha = 0.8) +
  scale_alpha_continuous(range = c(0.03, 0.6), guide = "none") +
  scale_size_area(max_size = 12, guide = "none") +
  scale_colour_manual(values = continent_cols) +
  scale_x_reverse() +
  labs(x = "Total fertility rate (births per woman, reversed)",
       y = "Life expectancy (years)", colour = NULL,
       title = paste0("Where each country came from, ", min(rosling$year),
                      " to ", max(rosling$year))) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")
Figure 2: Each country’s path from 1960 to 2015, fading into the past. The solid bubbles are the latest year with fertility data.

This version uses alpha for time, so older data is more faded. It keeps the movement in one panel, but the paths overlap heavily, and the faint tails are hard to read for any single country. Note the two layers here: the trails use all 10,358 rows, and the bubbles use a second data frame of only the latest year. That every layer can take its own data is itself a grammar point. It comes back on the cross-silo page.

Looking at the rendered figures shows things the code doesn’t. The few sudden vertical drops in life expectancy are not data errors. They are catastrophes: the large Asian bubble low in the 1960 panel is China (life expectancy 30.53) during the famine that followed the Great Leap Forward, and the lone Americas point low in the 2010 panel is Haiti (32.2) in the year of its earthquake. The trails show the same events as spikes.

Questions these two figures leave for the group:

  • Facets or alpha? Facets give clean snapshots but lose each country’s identity across panels. Alpha keeps identity and loses legibility.
  • Size. Bubble area tracks population, so China and India dominate every panel. Is that the right emphasis for a question about typical countries?
  • The stat. Both figures show raw points. A per-continent median path (a stat_summary layer) would be the geoms-and-stats lesson in one line of code.

What the data can’t say

These are national averages, so variation within a country is invisible, and every country counts equally whatever its population. The 1960s values for many countries are Gapminder’s estimates and interpolations, not registered births. The record also ends before the recent falls in fertility.

Candidate mappings (the group chooses)

Candidates (the group chooses) - gapminder-fertility:
  position x/y (continuous): fertility, life_expectancy, infant_mortality (14% NA),
                             population, gdp (28% NA, ends 2011)
  time:                      year (continuous, 1960-2015 usable for fertility)
  colour / facet (5 levels): continent
  size:                      population (spans 5 orders of magnitude)
  not for colour:            region (22), country (185) - filter or highlight a few

Why the data could suit geoms and stats: if you map year to x and fertility to y, you get 10,358 points, and those points can be drawn as dots, as one line per country, as a smoother per continent, or as a boxplot per decade. Choosing between those drawings is that session’s lesson.