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.
lifetab.awk— reads a CSV ofage,qxpairs 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 peakq_x, annuity at birth).datagen.awk— generates a syntheticage,qxCSV 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 intolifetab.awk.
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
qxin[0, 1].
# 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- POSIX-first: no gawk-specific extensions are used anywhere (e.g.
/dev/stderris avoided in favor of the portable| "cat 1>&2"pipe idiom). Both scripts have been checked againstmawkand the system defaultawk. - Variable-width intervals:
lifetab.awkinfers 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 approximationL_x = n * (l_x + l_{x+n}) / 2for 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_1is 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_xdepends 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 theENDblock, 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 functionsD_x = v^x * l_xandN_x = sum_{y>=x} D_y(v = 1/(1+rate)), reusing the same backward-cumulative-sum pattern used forT_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.ratemust be greater than -1 (the script rejects anything else). All column headers, includinga_x_due, are kept plain ASCII (rather than the traditional actuarial symbolä_x) since fixed-widthprintfcolumns can misalign under multi-byte UTF-8 characters on some awk/terminal combinations.
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.