Tables

Loading Tables

There are a variety of ways to load your own tables, reference online tables, or load bundled tables:

Bundled mort.SOA.org Tables

Comes with some tables built in via mort.SOA.org and by using you agree to their terms.

Note

Many mortality tables from mort.SOA.org have been tested to work, however not all mortality tables have been tested. Additionally, mort.SOA.org provides non-mortality rate tables which may not be properly parsed by this package. Try it and look at a few of the resulting values. If you encounter any issues, please file an issue.

Sample of some of the included table sets:

2017 Loaded CSO
2015 VBT
2001 VBT
2001 CSO
1980 CSO
1980 CET

Click here to see the full list of tables included.

If you would like more tables added by default, please open a GitHub issue with the request.

Other mort.SOA.org Tables

Given a table id (for example 60029) you can request the table directly from the SOA's mortality table service. Remember that not all tables have been tested, though the standard source format should mean compatibility with MortalityTables.jl.

aus_life_table_female = get_SOA_table(60029)
aus_life_table_female[0]  # returns the attained age 0 rate of 0.10139

You can combine it with the bundled tables too:

tables = MortalityTables.tables()

get_SOA_table!(tables,60029) # this modifies `tables` by adding the new table

t = tables["Australian Life Tables 1891-1900 Female"]
t[0]  # returns the attained age 0 rate of 0.10139

Load custom set of tables

Download the .xml aka the (XTbML format) version of the table from mort.SOA.org and place it in a directory of your choosing. Then call MortalityTables.tables(path_to_your_dir).

From CSV

If you have a CSV file that is from mort.SOA.org, or follows the same structure, then you can load and parse the table into a MortalityTable like so:

using CSV
using MortalityTables

path = "path/to/table.csv"
file = CSV.File(path,header=false) # SOA's CSV files have no true header
table = MortalityTable(file)

From XTbML

If you have a file using the XTbML format:

using MortalityTables
path = "path/to/table.xml"
table = MortalityTables.readXTbML(path)

Custom Tables

Say you have an ultimate vector and select matrix, and you want to leverage the MortalityTables package.

Here's an example, where we first construct the UltimateMortality and then combine it with the select rates to get a SelectMortality table.

using MortalityTables

# represents attained ages of 15 through 100
ult_vec = [0.005, 0.008, ...,0.805,1.00]
ult = UltimateMortality(ult_vec,start_age = 15)

We can now use this the ultimate rates all by itself:

q(ult,15,1) # 0.005

And join with the select rates, which for our example will start at age 0:

# attained age going down the column, duration across
select_matrix = [ 0.001 0.002 ... 0.010;
                  0.002 0.003 ... 0.012;
                  ...
                ]
sel_start_age = 0
sel = SelectMortality(select_matrix,ult,start_age = 0)

sel[0][0] #issue age 0, attained age 0 rate of  0.001
sel[0][100] #issue age 0, attained age 100 rate of  1.0

Lastly, to take the SelectMortality and UltimateMortality we just created, we can combine them into one stored object, along with a TableMetaData:

my_table = MortalityTable(
              s1,
              u1,
              metadata=TableMetaData(name="My Table", comments="Rates for Product XYZ")
              )
MortalityTables.tableFunction
table(id)
table(name)

Given the id or name of a mort.SOA.org table, grab it and return it as a MortalityTable.

!!! Remember that not all tables have been tested to work.

source
MortalityTables.get_SOA_tableFunction
get_SOA_table(id)
get_SOA_table(table_name)

Given the id or name of a mort.SOA.org table, grab it and return it as a MortalityTable.

!!! Remember that not all tables have been tested to work.

source

Table Constructors

Use these to build your own MortalityTables.jl-compatible table:

MortalityTables.MortalityTableType
MortalityTable(ultimate)
MortalityTable(select, ultimate)
MortalityTable(select, ultimate; metadata::MetaData)

Constructs a container object which can hold either: - ultimate-only rates (an UltimateTable) - select and ultimate rates (a SelectUltimateTable)

Also pass a keyword argument metadata=MetaData(...) to store relevant information (source, notes, etc) about the table itself.

Examples

# first construct the underlying data
ult = UltimateMortality([x / 100 for x in 0:100]); # first ma
matrix = rand(10,50); # represents random mortality rates with a select period of 10 years
sel = SelectMortality(matrix,ult,start_age=0);

table = MortalityTable(sel,ult)

# can now get rates, indexed by attained age:

table.select[10] # the vector of rates for a risk issued select at age 10 

table.ultimate[99] # 0.99
source
MortalityTables.SelectMortalityFunction
SelectMortality(select, ultimate; start_age=0)

Given a matrix rates, where the first row represents the select rates for a risk, will create a an OffsetArray that is indexed by issue age, containing a vector of rate indexed by attained age. The ultimate mortality vector is used for rates in the post-select period.

Give the optional keyword argument to start the indexing at an age other than zero.

Examples

julia> ult = UltimateMortality([x / 100 for x in 0:100]);

julia> matrix = rand(50,10); # represents random(!) mortality rates with a select period of 10 years

julia> sel = SelectMortality(matrix,ult,start_age=0);

julia> sel[0] # the mortality vector for a select life with issue age 0
 0.12858960119349439
 0.1172480189376135
 0.8237661916705163
 ⋮
 0.98
 0.99
 1.0

julia> sel[0][95] # the mortality rate for a life age 95, that was issued at age 0
0.95
source
MortalityTables.UltimateMortalityFunction
UltimateMortality(vector; start_age=0)

Given a vector of rates, returns an OffsetArray that is indexed by attained age.

Give the optional keyword argument to start the indexing at an age other than zero.

Examples

julia> m = UltimateMortality([0.1,0.3,0.6,1]);

julia> m[0]
0.1

julia> m = UltimateMortality([0.1,0.3,0.6,1], start_age = 18);

julia> m[18]
0.1
source
MortalityTables.mortality_vectorFunction
mortality_vector(vec; start_age=0)

A convenience constructor to create an OffsetArray which has the array indexed by attained age rather than always starting from 1. The package and JuliaActuary ecosystem assume that the rates are indexed by attained age and this allows transformation of tables without a direct dependency on OffsetArrays.jl.

Equivalent to doing:

using OffsetArrays
OffsetArray(vec,start_age-1)
source

Table Attributes

Basic metadata about the table (automatically populated for some tables).

MortalityTables.TableMetaDataType
TableMetaData(kwargs...)

Has the following fields, which default to nothing if not specified with a keyword:

  • name - a name for the table
  • id - if a mort.SOA.org sourced table, will be the identifying table ID
  • provider - Where the rates came from
  • reference - Source for more info on table
  • content_type
  • description
  • comments
  • source_path

When you call a MortalityTable interactively, it will nicely print this summary infomration.

Example content from mort.SOA.org:

  • Table Identity: 1076
  • Provider Domain: actuary.org
  • Provider Name: American Academy of Actuaries
  • Table Reference: Tillinghast, “American Council of Life Insurers: Preferred Version of 2001 CSO Mortality Tables”, ...
  • Content Type: CSO/CET
  • Table Name: 2001 CSO Super Preferred Select and Ultimate - Male Nonsmoker, ANB
  • Table Description: 2001 Commissioners Standard Ordinary (CSO) Super Preferred Select and Ultimate Table – Male Nonsmoker. Basis: Age Nearest Birthday. Minimum Select Age: 0. Maximum Select Age: 99. Minimum Ultimate Age: 16. Maximum Ultimate Age: 120
  • Comments: Study Data: A preferred version of the 2001 Commissioners Standard Ordinary (CSO) table ...

And the source_path would be: https://mort.soa.org/ViewTable.aspx?&TableIdentity=1076

Example usage:

julia-repl> TableMetaData(name="My Table Name")
TableMetaData("My Table Name", nothing, nothing, nothing, nothing, nothing, nothing, nothing)
source

Find the final age for which a table defines a rate.

MortalityTables.omegaFunction
omega(x)
ω(x)

Returns the last index of the given vector. For mortality vectors this means the last attained age for which a rate is defined.

Note that omega can vary depending on the issue age for a select table, and that a select omega may differ from the table's ultimate omega.

ω is aliased to omega, but un-exported. To use, do using MortalityTables: ω when importing or call MortalityTables.ω()

Examples

julia> qs = UltimateMortality([0.1,0.3,0.6,1]);
julia> omega(qs)
3

julia> qs = UltimateMortality([0.1,0.3,0.6,1],start_age=10);
julia> omega(qs)
13
source

Rates, Survival and Decrement

To access the rates, simply index by the attained age. Example:

julia> vbt2001 = tables["2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB"]
MortalityTable (Insured Lives Mortality):
   Name:
       2001 VBT Residual Standard Select and Ultimate - Male Nonsmoker, ANB
   Fields:
       (:select, :ultimate, :metadata)
   Provider:
       Society of Actuaries
   mort.SOA.org ID:
       1118
   mort.SOA.org link:
       https://mort.soa.org/ViewTable.aspx?&TableIdentity=1118
   Description:
       2001 Valuation Basic Table (VBT) Residual Standard Select and Ultimate Table -  Male Nonsmoker.
       Basis: Age Nearest Birthday. 
       Minimum Select Age: 0. 
       Maximum Select Age: 99. 
       Minimum Ultimate Age: 25. 
       Maximum Ultimate Age: 120

The package revolves around easy-to-access vectors which are indexed by attained age:

julia> vbt2001.select[35]          # vector of rates for issue age 35
 0.00036
 0.00048
 ⋮
 0.94729
 1.0
 
julia> vbt2001.select[35][35]      # issue age 35, attained age 35
 0.00036
 
julia> vbt2001.select[35][50:end] # issue age 35, attained age 50 through end of table
0.00316
0.00345
 ⋮
0.94729
1.0

julia> vbt2001.ultimate[95]        # ultimate vectors only need to be called with the attained age
 0.24298

Docstrings

MortalityTables.survivalFunction
survival(mortality_vector,to_age)
survival(mortality_vector,from_age,to_age)

Returns the survival through attained age to_age. The start of the calculation is either the start of the vector, or attainedage `fromage.fromageandtoage` need to be Integers. Add a DeathDistribution as the last argument to handle floating point and non-whole ages:

survival(mortality_vector,to_age,::DeathDistribution)
survival(mortality_vector,from_age,to_age,::DeathDistribution)

If given a negative to_age, it will return 1.0. Aside from simplifying the code, this makes sense as for something to exist in order to decrement in the first place, it must have existed and survived to the point of being able to be decremented.

Examples

julia> qs = UltimateMortality([0.1,0.3,0.6,1]);
    
julia> survival(qs,0)
1.0
julia> survival(qs,1)
0.9

julia> survival(qs,1,1)
1.0
julia> survival(qs,1,2)
0.7

julia> survival(qs,0.5,Uniform())
0.95
source
MortalityTables.decrementFunction
decrement(mortality_vector,to_age)
decrement(mortality_vector,from_age,to_age)

Returns the cumulative decrement through attained age to_age. The start of the calculation is either the start of the vector, or attainedage `fromage.fromageandtoage` need to be Integers. Add a DeathDistribution as the last argument to handle floating point and non-whole ages:

decrement(mortality_vector,to_age,::DeathDistribution)
decrement(mortality_vector,from_age,to_age,::DeathDistribution)

Examples

julia> qs = UltimateMortality([0.1,0.3,0.6,1]);
    
julia> decrement(qs,0)
0.0
julia> decrement(qs,1)
0.1

julia> decrement(qs,1,1)
0.0
julia> decrement(qs,1,2)
0.3

julia> decrement(qs,0.5,Uniform())
0.05
source

Life Expectancy

Calculate curtate or complete life expectancy.

Docstrings

MortalityTables.life_expectancyFunction
life_expectancy(table,age)
life_expectancy(table,age,DeathDistribution)

Calcuate the remaining life expectancy. Assumes curtate life expectancy for tables if not Parametric or DeathDistribution given.

The life_expectancy of the last age defined in the table is set to be 0.0, even if the table does not end with a rate of 1.0.

source

Fractional Year Assumptions

When evaluating survival over partial years when you are given full year mortality rates, you must make an assumption over how those deaths are distributed throughout the year. Three assumptions are provided as options and are based on formulas from the 2016 Experience Study Calculations paper from the SOA, specifically pages 40-44.

The three assumptions are:

  • Uniform() which assumes an increasing force of mortality throughout the year.
  • Constant() which assumes a level force of mortality throughout the year.
  • Balducci() which assumes a decreasing force of mortality over the year. It seems [to

be for making it easier](https://www.soa.org/globalassets/assets/library/research/actuarial-research-clearing-house/1978-89/1988/arch-1/arch88v17.pdf) to calculate successive months by hand rather than any theoretical basis.

MortalityTables.DeathDistributionType
DeathDistribution

An abstract type used to form an assumption of how deaths occur throughout a year. See Balducci(), Uniform(), and Constant() for concrete assumption types.

source