How has the relation between Life Expectancy and GDP per Capita has evolved - demonstrated using a Preston Curve
{gganimate}
Our World in Data
Public Health
Author
Aditya Dahiya
Published
September 2, 2024
The Preston Curve, developed by Samuel H. Preston in 1975, illustrates the relationship between life expectancy and income per capita across countries. The curve shows that life expectancy tends to rise with higher national income, but the relationship is non-linear. At lower income levels, even small increases in income can lead to significant gains in life expectancy due to improved access to healthcare, sanitation, and disease control. However, at higher income levels, the life expectancy gains flatten, suggesting that other factors like healthcare innovations and policies play a bigger role in further improving health outcomes. The curve highlights that while wealth is important, it is not the only driver of health improvements. Read more about it here and here.
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 support for ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # To lighten and darken colourslibrary(gganimate) # For animation across years# search1 <- owid_search("life expectancy")# search1 |> as_tibble()rawdf1 <-owid("life-expectancy-vs-gdp-per-capita")joindf1 <- wbstats::wb_countries() |>select(iso3c, income_level) |>rename(code = iso3c) |>filter(!(income_level %in%c(NA, "Not classified"))) |>mutate(income_level =fct( income_level,levels =c("High income", "Upper middle income","Lower middle income","Low income", "Aggregates" ) ) )
Visualization Parameters
Code
# Font for titles & body textfont_add_google("Roboto",family ="body_font") # Font for the captionfont_add_google("Saira Extra Condensed",family ="caption_font") showtext_auto()plot_caption <-paste0("Data: Our World in Data | ","Code: @aditya-dahiya (GitHub)")
Exploratory Data Analysis and Data Wrangling
Code
df <- rawdf1 |>as_tibble() |>filter(year >=1950& year <=2023) |>filter(str_length(code) ==3) |> janitor::clean_names()# Checking the missingness in data# df |> # visdat::vis_miss()# Remove the countries missing too much info to avoid a jagged animationremove_countries1 <- df |>group_by(entity, code) |>summarise(life_expectancy_at_birth =mean(is.na(life_expectancy_at_birth)),gdp_per_capita =mean(is.na(gdp_per_capita)) ) |>filter(gdp_per_capita >0.8) |>pull(code)remove_countries2 <- df |>group_by(entity, code) |>summarise(life_expectancy_at_birth =mean(is.na(life_expectancy_at_birth)),gdp_per_capita =mean(is.na(gdp_per_capita)) ) |>filter(life_expectancy_at_birth >0.8) |>pull(code)remove_countries <-unique(c(remove_countries1, remove_countries2))rm(remove_countries1, remove_countries2)# Creating the dataframe used to plot# Removing some aberrations and outliersplotdf <- df |>filter(!(code %in% remove_countries)) |>select(-countries_continents) |>group_by(year) |>arrange(desc(gdp_per_capita)) |>slice(-1:-5) |>ungroup() |>filter(year <=2020) |>left_join(joindf1)# Recheck for missingness# plotdf |> # visdat::vis_miss()