Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Actuarial life-table toolkit

A small, portable AWK toolkit that builds an actuarial life table from mortality rates (q_x), and generates synthetic sample data to try it on. Both single-year-of-age tables and abridged tables (e.g. 5-year age bands) are supported.

Files

  • lifetab.awk — reads a CSV of age,qx pairs and produces a formatted life table (l_x, d_x, q_x, p_x, e_x, a_x_due), with deaths subtotaled per age band and a summary block (life expectancy at birth, total deaths, average and peak q_x, annuity at birth).
  • datagen.awk — generates a synthetic age,qx CSV using a Gompertz-Makeham mortality model plus an infant mortality bump, for ages 0 to 110.
  • life1yr.csv — sample output of the generator above, ready to feed into lifetab.awk.

Real-world data

life1yr.csv is synthetic (see Design notes below), good for trying the toolkit out, but not real mortality experience.

For actual published mortality tables, ISTAT (the Italian national statistics institute) provides single-age tables for the Italian resident population, 1974 onward, as downloadable CSV: pick a year and geography, download the single-age table, and reshape it to the age,qx CSV format lifetab.awk expects.

the ISTAT column is typically a death probability per thousand, so divide by 1000 to get qx in [0, 1].

Usage

# Build a life table from the bundled sample data
awk -f lifetab.awk life1yr.csv

# Customize the starting cohort size (radix) and the age-band width
awk -f lifetab.awk -v radix=1000 -v band=10 life1yr.csv

# Customize the interest rate used to discount the annuity column
awk -f lifetab.awk -v rate=0.02 life1yr.csv

# Regenerate the sample data
awk -f datagen.awk > life1yr.csv

Design notes

  • POSIX-first: no gawk-specific extensions are used anywhere (e.g. /dev/stderr is avoided in favor of the portable | "cat 1>&2" pipe idiom). Both scripts have been checked against mawk and the system default awk.
  • Variable-width intervals: lifetab.awk infers each interval's width from consecutive tabulated ages (age[i+1] - age[i]), so it works with both single-year tables (0, 1, 2, 3, ...) and abridged tables (0, 1, 5, 10, 15, ...). It uses the width-scaled midpoint approximation L_x = n * (l_x + l_{x+n}) / 2 for person-years lived in each interval (except at a one-year-wide age 0, see below). Ages must be strictly increasing. The last tabulated age has no following age to derive a width from, so it reuses the previous interval's width as a documented simplification.
  • Infant mortality correction: at age 0, when its interval is exactly one year wide, L_0 = 0.3*l_0 + 0.7*l_1 is used instead of the midpoint rule, since infant deaths are concentrated early in the year rather than spread uniformly. The 0.3/0.7 split is the standard actuarial "separation factor" a_0 = 0.3, not a value fitted to the input data.
  • Single-pass with buffering: life expectancy e_x depends on a backward cumulative sum of person-years (T_x = sum_{y>=x} L_y), which can't be computed until every row has been read. The script buffers each row into indexed arrays during the main pass and does the backward summation in the END block, using an explicit numeric index to preserve row order (AWK arrays are unordered associative arrays).
  • Annuity column (a_x_due): the actuarial value of a life annuity-due of 1, discounted at -v rate=... (default 0.03), computed with the standard commutation functions D_x = v^x * l_x and N_x = sum_{y>=x} D_y (v = 1/(1+rate)), reusing the same backward-cumulative-sum pattern used for T_x. It pays 1 unit at the start of each tabulated interval, not necessarily every year: on a single-year table this is the standard whole-life annuity-due, but on an abridged table (e.g. 5-year bands) it values a payment made once per interval, since there is no mortality data for the years in between — treat it as a true annual annuity only when every interval width is 1. rate must be greater than -1 (the script rejects anything else). All column headers, including a_x_due, are kept plain ASCII (rather than the traditional actuarial symbol ä_x) since fixed-width printf columns can misalign under multi-byte UTF-8 characters on some awk/terminal combinations.

Known limitations / possible extensions

Premiums: extending a_x_due to net single premiums for endowment or term insurance would require a discounted death-benefit column alongside the existing annuity commutation functions.

About

AWK life-table toolkit

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages