---
title: "Demographic analysis with the lifecontingencies package"
author: "Giorgio Alfredo Spedicato"
date: today
format: html
bibliography: lifecontingenciesBiblio.bib
vignette: >
  %\VignetteEngine{quarto::html}
  %\VignetteIndexEntry{Demographic analysis with the lifecontingencies package}
  %\VignetteEncoding{UTF-8}
---

```{r}
#| include: false
knitr::opts_chunk$set(
  warning = FALSE,
  message = FALSE,
  collapse = TRUE,
  comment = "#>"
)
library(lifecontingencies)
```

# Scope

This vignette collects the *demographic* functions of the
**lifecontingencies** package: the quantities that describe the mortality of a
cohort through a life table, independently of any interest rate. It complements
the introductory vignette (which focuses on the financial and actuarial
functions) and the multiple-decrement vignette.

All of the examples below run on life tables that can be reconstructed
exactly, so the numbers can be checked against the standard actuarial and
demographic literature [@bowers1997actuarial; @dickson2009actuarial; @keyfitz2005applied].

# Building and inspecting a life table

A `lifetable` object stores a vector of ages `x` and the survivorship column
`lx`. The illustrative Society of Actuaries table bundled with the package is
an `actuarialtable` (a `lifetable` that additionally carries an interest
rate), so every demographic function below works on it unchanged.

```{r build}
data(soa08Act)
getOmega(soa08Act)          # last attainable age
soa08Act@lx[1:5]            # survivorship at the first ages
```

Printing a `lifetable` tabulates the one-year survival probability together
with the person-years `Lx`, the total time lived `Tx` and the expectation of
life `ex`. The `print` method accepts optional arguments (discussed below) and
returns the displayed `data.frame` invisibly, so it can be captured for
further use:

```{r printdf}
lt <- new("lifetable", x = 0:5, lx = c(1000, 990, 975, 940, 880, 790),
          name = "toy")
df <- print(lt)
```

# Core demographic functions

The basic one-year and `t`-year quantities are computed directly from the
`lx` column.

```{r core}
pxt(soa08Act, x = 65, t = 1)     # 1-year survival probability
qxt(soa08Act, x = 65, t = 5)     # 5-year death probability
dxt(soa08Act, x = 65, t = 10)    # deaths between 65 and 75
Lxt(soa08Act, x = 65, t = 1)     # person-years lived in [65, 66)
mxt(soa08Act, x = 65, t = 1)     # central mortality rate
Tx(soa08Act, x = 65)             # total years lived from age 65
```

The central mortality rate and the death probability are linked by the usual
conversions:

```{r mx}
m <- mxt(soa08Act, 65, 1)
c(mx2qx(m), qxt(soa08Act, 65, 1))
```

# Expectation of life: curtate and complete

`exn` returns the expectation of future lifetime. With `type = "curtate"`
(the default) it is the expected number of *complete* future years,
$e_x = \sum_{k\ge 1}{}_k p_x$; with `type = "complete"` it is the expected
*exact* future lifetime, $\mathring e_x = T_x/l_x$, evaluated under the uniform
distribution of deaths (UDD) within each year of age. Under UDD the two are
related by $\mathring e_x \approx e_x + 0.5$:

```{r exn}
curtate  <- exn(soa08Act, x = 0, type = "curtate")
complete <- exn(soa08Act, x = 0, type = "complete")
c(curtate = curtate, complete = complete, difference = complete - curtate)
```

Both forms accept a temporary horizon `n`; the temporary complete expectation
is $\mathring e_{x:\overline{n}|} = {}_n L_x / l_x$:

```{r exntemp}
exn(soa08Act, x = 50, n = 20, type = "curtate")
exn(soa08Act, x = 50, n = 20, type = "complete")
```

As a published check, Finan's *Exam MLC* study manual [@finanMLC]
(Example 23.24) uses the small extract below and obtains $e_{80}=2.3$:

```{r finan}
t80 <- new("lifetable", x = 80:86,
           lx = c(250, 217, 161, 107, 62, 28, 0), name = "Finan 23.24")
exn(t80, 80)
```

# Within-year deaths and the open last interval

`Tx` and `exn` (complete branch) expose two assumptions that matter when a
life table is to be matched against an officially published one:

* `fxt` — the fraction of the final year of age lived by those who die within
  it, so that $L_x = l_x - fxt\, d_x$. The default `0.5` is the UDD
  assumption.
* `axOmega` — the mean number of years lived in the last, *open-ended* age
  interval by those still alive at the last tabulated age $\omega$, so that
  $L_\omega = axOmega \cdot l_\omega$. The default `1 - fxt` reproduces the
  historical closed-interval behaviour of the package (the last age
  contributes $0.5\, l_\omega$ when `fxt = 0.5`).

Official period life tables (for example the U.S. NCHS tables) leave the last
interval open and close it with $L_\omega = l_\omega / m_\omega$, i.e.
`axOmega = 1 / mOmega`. The excerpt below reproduces the published closure of
the last age of the NCHS 2019 total table ($l_{100}=2090$, $m_{100}$ implied by
$L_{100}=4696$):

```{r openinterval}
tail2 <- new("lifetable", x = c(99, 100), lx = c(3024, 2090), name = "NCHS tail")
mOmega <- 2090 / 4696                 # published central rate at the open age
c(default_closed = Tx(tail2, 100),
  open_interval  = Tx(tail2, 100, axOmega = 1 / mOmega),
  published_L100 = 4696)
```

With the defaults the results are identical to previous versions of the
package, so existing code is unaffected.

# Dispersion of the future lifetime

`varxn` and `sdxn` are the second-moment companions of `exn`: the variance and
standard deviation of the future lifetime. For the curtate lifetime the
variance is computed exactly from the table; for the complete lifetime it uses
the UDD relation $\mathrm{Var}(T_x) = \mathrm{Var}(K_x) + 1/12$.

```{r var}
varxn(soa08Act, x = 65, type = "Kx")        # Var(K_65)
sdxn(soa08Act,  x = 65, type = "Kx")        # sd(K_65)
varxn(soa08Act, x = 65, type = "complete")  # Var(T_65) = Var(K_65) + 1/12
```

On the Finan extract the variance of the curtate lifetime is $2.394$:

```{r varfinan}
varxn(t80, 80, type = "Kx")
```

A temporary horizon is available for the curtate lifetime:

```{r vartemp}
varxn(soa08Act, x = 40, n = 20, type = "Kx")
```

# Distribution of the age at death

The age at death implied by a life table has a full distribution, not only a
mean. The package exposes it through the familiar **R** generics `median` and
`quantile`, plus the Lexis modal age at death `modalAge`.

```{r dist}
median(soa08Act)                                   # median age at death
quantile(soa08Act, probs = c(0.1, 0.25, 0.75, 0.9))
modalAge(soa08Act, startAge = 10)                  # adult modal age at death
modalAge(soa08Act, startAge = 10, interpolate = TRUE)
```

Both `median` and `quantile` can be conditioned on survival to a given age via
the `age` argument, returning the distribution of the age at death given that
the life has reached `age`:

```{r distcond}
median(soa08Act, age = 65)
quantile(soa08Act, probs = c(0.25, 0.75), age = 65)
```

Because these are the standard generics, they behave as users expect: the
median is exactly the 50% quantile, and on a de Moivre table ($l_x = \omega-x$)
the quantiles are exact.

```{r demoivre}
dm <- new("lifetable", x = 0:100, lx = 100 - (0:100), name = "de Moivre")
c(median = median(dm), q25 = unname(quantile(dm, 0.25)),
  q75 = unname(quantile(dm, 0.75)))
```

# An enriched life-table printout

Finally, the `print` method of a `lifetable` forwards the `exType`, `fxt` and
`axOmega` arguments to the table it builds, so the complete expectation (and
any non-default within-year or open-interval assumption) can be shown
directly. (An `actuarialtable` such as `soa08Act` prints its commutation
functions instead, so we coerce it to a plain `lifetable` here.)

```{r printopts}
ltSoa <- new("lifetable", x = soa08Act@x, lx = soa08Act@lx, name = "SOA 2008")
head(print(ltSoa, exType = "complete"))
```

# References
