Life expectancy and death rates
A chance comment on Twitter about historical increases in life expectancy (in the USA) with fairly constant death rates got me wondering about the exact calculations of these things so I did some Googling. Warning on what follows - I am strictly an amateur in demographics, and was deliberately working things out from basics as a learning exercise, so there are possibly better ways to do some of the things below.
According to Wikipedia which is always good at this sort of thing, life expectancy can be calculated two ways:
by observing a cohort until they are all dead, and taking the average age. Obviously this only works with historical data. This is referred to as “cohort life expectancy at birth”.
by taking a set of death rates by age bracket, and calculating the average age of death of a hypothetical cohort of people who move through life with those death rates. This is called “period life expectancy at birth” and is the method used for reporting by national statistics offices.
By convention, period life expectancy applies today’s death rates to an infant born today. Hence, there’s no estimation of those death rates changing; we assume that by the time this infant is 60 years old, the death rate of 60 year olds in 2078 will be the same as now. This has some rather obvious flaws, but it’s also easy to see why that approach is convenient, particularly for standardising data across countries. It means that the reported life expectancies aren’t really the best estimates of how long a baby born today will live conditional on standard expectations about civilization continuing at all, because we actually expect death rates to continue to decline (although some days I wonder). But it does mean that “life expectancy” is very clearly defined and with minimum discretion needed in the calculation. So long as we remember that period life expectancy is really a summary statistic of today’s death rates by age, not really how long people are expected to live but a sort of hypothetical life length, we are ok.
Life expectancy can go up while crude death rates are also going up (or vice versa) because of changing age composition in a population. All the age-specific death rates (and hence any age-adjusted death rate) might be going down, but if more and more people are in the higher-rate age brackets the overall crude death rate might be increasing. We can see this happening for Japan in the image below:
Here’s the R code that grabbed the data for that image and built it. ggplot2 afficianados might be interested in how I’ve used geom_blank to increase the scales a bit beyond the defaults, which were too narrow and had my text banging against the axes (a common problem). It’s a bit of a hack but it works nicely:
library(frs) # if necessary, install with devtools::install_github(&quot;ellisp/frs-r-package/pkg&quot;)
library(tidyverse)
library(scales)
library(WDI)
# Use WDIsearch to find the indicators we want
# View(WDIsearch(&quot;life&quot;))
death_rate %
# take out NAs which confuse the geom_text call later:
filter(!is.na(SP.DYN.LE00.IN)) %__%
# order country by life expectancy so graphic facets make some sense:
mutate(country = fct_reorder(country, SP.DYN.LE00.IN)) %__%
ggplot(aes(x = SP.DYN.CDRT.IN, y = SP.DYN.LE00.IN, colour = country)) +
geom_path() +
# add labels of the year at max and min points
geom_text(aes(label = ifelse(year %in% range(year), year, &quot;&quot;)), colour = &quot;grey30&quot;, size = 3) +
facet_wrap(~country, scales = &quot;free&quot;) +
# these next two geom_blank calls are a hack to make sure enough space is given for the labels which
# otherwise crash into the axes, and can't be easily controlled with scale limits:
geom_blank(aes(x = SP.DYN.CDRT.IN * 1.1, y = SP.DYN.LE00.IN * 1.05)) +
geom_blank(aes(x = SP.DYN.CDRT.IN * 0.9, y = SP.DYN.LE00.IN * 0.95)) +
theme(legend.position = &quot;none&quot;) +
labs(x = &quot;Crude death rate (per 1,000 people)&quot;,
y = &quot;Life Expectancy&quot;,
caption = &quot;Source: World Bank World Development Indicators&quot;) +
ggtitle(&quot;Differing relationship of crude death rate and life expectancy&quot;,
&quot;Countries with aging populations (eg Georgia, Japan, in the below) can experience increases in both simultaneously.&quot;)Calculating life expectancy
To be sure I understood how it worked, I had a go at estimating some life expectancies myself. I started with some French death rates per age group from 2015, because these were the most convenient to hand. Death rates are reported as deaths per 1,000 people per year. Here’s how they look when you convert them into the probability of surviving the year at any particular age:
Here’s how that was drawn. The french_death_rates_2015 object comes from the frs package where I store miscellaneous things to help out with this blog.
french_death_rates_2015 %__%
gather(sex, value, -age) %__%
ggplot(aes(x = age, y = (1000 - value) / 1000, colour = sex)) +
geom_line() +
labs(x = &quot;Age&quot;, y = &quot;Probability of surviving to the next year&quot;, colour = &quot;&quot;,
caption = &quot;https://www.ined.fr/en/everything_about_population/data/france/deaths-causes-mortality/mortality-rates-sex-age/&quot;) +
coord_cartesian(ylim = c(0.8, 1), xlim = c(0, 100)) +
ggtitle(&quot;French death rates in 2015&quot;) +
theme(legend.position = c(0.6, 0.6))To actually convert these probabilities into a life expectancy, we need to estimate the proportion of our hypothetical population that will die at each age. There’s lots of different ways you might do this but the one that came to mind to me was:
Create interpolated death rates for each integer age (because typically death rates are given for a bracket of ages, not every single age)
Estimate the proportion still alive at any age, starting with 1 at birth and 0 at some arbitrary end point (I chose 150 which seems reasonable). This is the cumulative product of the yearly survival rates, which are of course 1 - death rate (where death rate has been converted to a probability rather than a factor out of 1,000).
Estimate the difference between those proportions for each year, which gives you the proportion of the total population that died in that year.
Take the average of all of our ages, weighted by the proportion of the population that died each year.
Now that I write it up this seems a bit more involved than I would have thought. It’s possible there’s a simpler way. Anyway, here’s the function that implements my approach
#' @param age vector of ages in years
#' @param rate death rate in deaths per 1000 people alive at the given age. Must be the same length as age.
#' @param data optionally, a data frame or matrix where the first column is used as age and the second column as rate
#' @examples
#' life_expectancy(data = french_death_rates_2015[ , c(&quot;age&quot;, &quot;female&quot;)])
#' life_expectancy(age = french_death_rates_2015$age, rate = french_death_rates_2015$male)
life_expectancy