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.
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.readXTbML
— FunctionreadXTbML(path)
Loads the XtbML (the SOA XML data format for mortality tables) stored at the given path and returns a MortalityTable
.
MortalityTables.table
— Functiontable(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.
MortalityTables.get_SOA_table
— Functionget_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.
Table Constructors
Use these to build your own MortalityTables.jl
-compatible table:
MortalityTables.MortalityTable
— TypeMortalityTable(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
MortalityTables.SelectMortality
— FunctionSelectMortality(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
MortalityTables.UltimateMortality
— FunctionUltimateMortality(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
MortalityTables.mortality_vector
— Functionmortality_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)
Table Attributes
Basic metadata about the table (automatically populated for some tables).
MortalityTables.TableMetaData
— TypeTableMetaData(kwargs...)
Has the following fields, which default to nothing
if not specified with a keyword:
name
- a name for the tableid
- if a mort.SOA.org sourced table, will be the identifying table IDprovider
- Where the rates came fromreference
- Source for more info on tablecontent_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)
Find the final age for which a table defines a rate.
MortalityTables.omega
— Functionomega(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
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.survival
— Functionsurvival(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.
fromageand
toage` 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
MortalityTables.decrement
— Functiondecrement(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.
fromageand
toage` 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
Life Expectancy
Calculate curtate or complete life expectancy.
Docstrings
MortalityTables.life_expectancy
— Functionlife_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
.
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.DeathDistribution
— TypeDeathDistribution
An abstract type used to form an assumption of how deaths occur throughout a year. See Balducci()
, Uniform()
, and Constant()
for concrete assumption types.
MortalityTables.Balducci
— TypeBalducci()
A DeathDistribution
type that assumes a decreasing force of mortality over the year.
MortalityTables.Uniform
— TypeUniform()
A DeathDistribution
type that assumes an increasing force of mortality over the year.
MortalityTables.Constant
— TypeConstant()
A DeathDistribution
type that assumes a constant force of mortality over the year.