Using the ValuationTables Package

Reinhold Kainhofer, reinhold@kainhofer.com

2016-09-03

The ValuationTables package provides the valuationTable base class and some derived classes to handle different types of valuation life tables, mainly used for life insurance. Additionally it provides a plot function to compare multiple life tables either directly using the absolute mortalities in log-linear plots or using relative mortalities as percentages of a given reference table.

Types of Life Tables

Provided types of valuation tables are:

Loading the ValuationTables package

library("ValuationTables")

Provided Data Sets

The package provides several real-life life tables published by census bureaus and actuarial associations around the world. You can use the function valuationTables.list to list all available datasets (if no argument is given) or all datasets that match the given pattern (wildcard character is *). You can then use valuationTables.load to load either one single data set or all datasets that match the pattern (if wildcard=TRUE is given).

# list all available data sets
valuationTables.list()
#>  [1] "Austria_Annuities"           "Austria_Annuities_AVOe1996R"
#>  [3] "Austria_Annuities_AVOe2005R" "Austria_Annuities_EROMF"    
#>  [5] "Austria_Annuities_RR67"      "Austria_Census"             
#>  [7] "Germany_Annuities"           "Germany_Annuities_DAV1994R" 
#>  [9] "Germany_Annuities_DAV2004R"  "Germany_Endowments"         
#> [11] "Germany_Endowments_DAV1994T" "Germany_Endowments_DAV2008T"
#> [13] "USA_Annuities"               "USA_Annuities_1971IAM"      
#> [15] "USA_Annuities_1983a"         "USA_Annuities_1994GAR"      
#> [17] "USA_Annuities_2012IAM"       "USA_Annuities_Annuity2000"

# list all datasets for Austria
valuationTables.list("Austria_*")
#> [1] "Austria_Annuities"           "Austria_Annuities_AVOe1996R"
#> [3] "Austria_Annuities_AVOe2005R" "Austria_Annuities_EROMF"    
#> [5] "Austria_Annuities_RR67"      "Austria_Census"

# Load the German annuity table DAV 2004-R
valuationTables.load("Germany_Annuities_DAV2004R")
#> Loading valuation life table data set 'Germany_Annuities_DAV2004R'

# Load all Austrian data sets
valuationTables.load("Austria_*", wildcard=TRUE)
#> Loading valuation life table data set 'Austria_Annuities'
#> Loading valuation life table data set 'Austria_Annuities_AVOe1996R'
#> Loading valuation life table data set 'Austria_Annuities_AVOe2005R'
#> Loading valuation life table data set 'Austria_Annuities_EROMF'
#> Loading valuation life table data set 'Austria_Annuities_RR67'
#> Loading valuation life table data set 'Austria_Annuities_AVOe1996R'
#> Loading valuation life table data set 'Austria_Annuities_AVOe2005R'
#> Loading valuation life table data set 'Austria_Annuities_EROMF'
#> Loading valuation life table data set 'Austria_Annuities_RR67'
#> Loading valuation life table data set 'Austria_Census'

In the next few sections we will always use some of the provided life tables for demonstration purposes.

Working with life table objects

Plotting life tables

The package provides two functions to plot lifetables:

Both functionalities are also combined into the S3 plot function for the valuationTable class, so you can usually just call plot on the valuation tables. If the reference argument is given, plotValuationTableComparisons is used, otherwise plotValuationTables is called.

# Log-linear plot comparing some Austrian census tables
plot(mort.AT.census.1951.male, mort.AT.census.1991.male, 
     mort.AT.census.2001.male, mort.AT.census.2011.male, 
     legend.position=c(1,0))


# Relative death probabilities in percentage of the latest census
plot(mort.AT.census.1951.male, mort.AT.census.1991.male, 
     mort.AT.census.2001.male, 
     reference = mort.AT.census.2011.male, legend.position=c(1,0.75), ylim=c(0,4))
#> Warning in normalize_deathProbabilities(cbind(x = ages(t), y =
#> deathProbabilities(t, : Reference mortality table does not contain ages
#> 101102103104105106107108109110111112 required for normalization. These ages
#> will not be normalized!

For cohort life tables, the plot functions also take either the YOB or the Period parameter to plot either the cohort death probabilities for the given birth year or the period death probabilities for the given observation year.

# Comparison of two Austrian annuity tables for birth year 1977
plot(AVÖ1996R.male, AVOe2005R.male, YOB=1977, title="Comparison for YOB=1977")


# Comparison of two Austrian annuity tables for observation year 2020
plot(AVÖ1996R.male, AVOe2005R.male, Period=2020, title="Comparison for observation year 2020")

Obtaining period and cohort death probabilities

To obtain death probabilities from all the different types of tables, there are two functions:

valuationTables.load("Austria_Annuities")
# Get the cohort death probabilities for Austrian Annuitants born in 1977:
qx.coh1977 = deathProbabilities(AVOe2005R.male, YOB=1977)

# Get the period death probabilities for Austrian Annuitants observed in the year 2020:
qx.per2020 = periodDeathProbabilities(AVOe2005R.male, Period=2020)

These functions return the death probabilities as a simple, numeric R vector.

There are two similar functions that return the death probabilities as a period life table object that can be used with all other functions provided by this package:

# Get the cohort death probabilities for Austrian Annuitants born in 1977 as a valuationTable.period object:
table.coh1977 = getCohortTable(AVOe2005R.male, YOB=1977)

# Get the period death probabilities for Austrian Annuitants observed in the year 2020:
table.per2020 = getPeriodTable(AVOe2005R.male, Period=2020)

# Compare those two in a plot:
plot(table.coh1977, table.per2020, title="Comparison of cohort 1977 with Period 2020", legend.position=c(1,0))

Not surprisingly, at 43 years the two death probabilities cross, because in 2020 the person born 1977 is 43 years old, so the \(q_x\) refer to the same person. However, for younger ages, the period 2020 probabilities are lower, because the mortality improvement for those younger ages has much less time in the cohort 1977 table. For ages above 43 the cohort table describes the mortality further into the future than 2020, so there is more improvement and thus lower death probabilities for the cohort life table.

Other data extraction functions from life tables

function description
ages(table) Returns the vector of ages, for which the life table can provide death probabilities
getOmega(table) Returns the maximum age, for which the life table can provide dath probabilities
ageShift(table, YOB) Returns the age shift for the given year of birth
baseTable(table) Returns the base table, from which the table projects (for cohort tables)
baseYear(table) Returns the year of the base table
lifetable(table, YOB) Returns the cohort death probabilities as a lifetable object for use with the lifecontingencies package

Creating a life table object

Period life tables

Period death probabilities are the simplest type of life table, giving the probabilities of death observed during the corresponding year (the “period”). The death probabilities of different ages refer to different persons, being of the corresponding ages in the observation year. All that is needed to create a period life table are the death probabilities and the corresponding ages:

lt = valuationTable.period(name="Sample period lifetable", ages=1:99, deathProbs=exp(-(99:1)/10))
plot(lt, title="Simple log-linear period mortality table")

deathProbabilities(lt)
#>  [1] 5.017468e-05 5.545160e-05 6.128350e-05 6.772874e-05 7.485183e-05
#>  [6] 8.272407e-05 9.142423e-05 1.010394e-04 1.116658e-04 1.234098e-04
#> [11] 1.363889e-04 1.507331e-04 1.665858e-04 1.841058e-04 2.034684e-04
#> [16] 2.248673e-04 2.485168e-04 2.746536e-04 3.035391e-04 3.354626e-04
#> [21] 3.707435e-04 4.097350e-04 4.528272e-04 5.004514e-04 5.530844e-04
#> [26] 6.112528e-04 6.755388e-04 7.465858e-04 8.251049e-04 9.118820e-04
#> [31] 1.007785e-03 1.113775e-03 1.230912e-03 1.360368e-03 1.503439e-03
#> [36] 1.661557e-03 1.836305e-03 2.029431e-03 2.242868e-03 2.478752e-03
#> [41] 2.739445e-03 3.027555e-03 3.345965e-03 3.697864e-03 4.086771e-03
#> [46] 4.516581e-03 4.991594e-03 5.516564e-03 6.096747e-03 6.737947e-03
#> [51] 7.446583e-03 8.229747e-03 9.095277e-03 1.005184e-02 1.110900e-02
#> [56] 1.227734e-02 1.356856e-02 1.499558e-02 1.657268e-02 1.831564e-02
#> [61] 2.024191e-02 2.237077e-02 2.472353e-02 2.732372e-02 3.019738e-02
#> [66] 3.337327e-02 3.688317e-02 4.076220e-02 4.504920e-02 4.978707e-02
#> [71] 5.502322e-02 6.081006e-02 6.720551e-02 7.427358e-02 8.208500e-02
#> [76] 9.071795e-02 1.002588e-01 1.108032e-01 1.224564e-01 1.353353e-01
#> [81] 1.495686e-01 1.652989e-01 1.826835e-01 2.018965e-01 2.231302e-01
#> [86] 2.465970e-01 2.725318e-01 3.011942e-01 3.328711e-01 3.678794e-01
#> [91] 4.065697e-01 4.493290e-01 4.965853e-01 5.488116e-01 6.065307e-01
#> [96] 6.703200e-01 7.408182e-01 8.187308e-01 9.048374e-01

Observed life tables

The observations for the given years

TODO

Cohort life tables with trend projection

TODO

Cohort life tables with age-shift

TODO

Modifying life table objects

Copying life tables

Life tables are simple pass-by-value S4 objects, so copying works by simple assignment.

b=AVOe2005R.female 
b@name = "Modified Copy"
# only b is modified, not the original table
b@modification = function(qx) pmax(qx, 0.01)  
plot(AVOe2005R.female, b, YOB=2000)

Adding a modification to the raw probabilities

Some uses require post-processing of the death probabilities, like adding a lower bound for the death probabilities. To achive this, all valuationTable-derived classes have a slot modification that takes a function that is passed the vector of death probabilities.

lt.mod = valuationTable.period(name="Sample modified lifetable (lower bound of 3%)", ages=1:99, deathProbs=exp(-(99:1)/10), modification=function (qx) pmax(0.03, qx))
plot(lt, lt.mod, title="Original and modified table")

Adding a modification to the raw probabilities

Some uses require post-processing of the death probabilities, like adding a lower bound for the death probabilities. To achive this, all valuationTable-derived classes have a slot modification that takes a function that is passed the vector of death probabilities.

AVOe2005R.female.mod = setModification(AVOe2005R.female, modification=function (qx) pmax(0.03, qx));
# Make sure the modified table has a new name, otherwise plots might break
AVOe2005R.female.mod@name = "Modified table (lower bound of 3%)"
plot(AVOe2005R.female, AVOe2005R.female.mod, title="Original and modified table")