In this tutorial, we will walk through how to calculate exposures using the ExperienceAnalysis.jl package.
In summary, the package will help calculate the exposure periods given parameters about the kind of period and timepoints under consideration. This will return an array of tuples with a from
and to
date:
using ExperienceAnalysis
using Dates
issue = Date(2016, 7, 4)
termination = Date(2020, 1, 17)
basis = ExperienceAnalysis.Anniversary(Year(1))
exposure(basis, issue, termination)
4-element Vector{@NamedTuple{from::Date, to::Date, policy_timestep::Int64}}:
(from = Date("2016-07-04"), to = Date("2017-07-03"), policy_timestep = 1)
(from = Date("2017-07-04"), to = Date("2018-07-03"), policy_timestep = 2)
(from = Date("2018-07-04"), to = Date("2019-07-03"), policy_timestep = 3)
(from = Date("2019-07-04"), to = Date("2020-01-17"), policy_timestep = 4)
Available Exposure Basis
ExperienceAnalysis.Anniversary(period)
will give exposures periods based on the first date
ExperienceAnalysis.Calendar(period)
will follow calendar periods (e.g. month or year)
ExperienceAnalysis.AnniversaryCalendar(period,period)
will split into the smaller of the calendar or policy anniversary period.
Where period
is a Period Type from the Dates standard library.
Calculate exposures with exposures(basis,from,to,continue_exposure)
.
continue_exposures
indicates whether the exposure should be extended through the full exposure period rather than terminate at the to
date.
Full Example
We’ll start with this as our data:
using DataFrames
df = DataFrame(
id=[1, 2, 3],
issue=[Date(2016, 7, 4), Date(2016, 1, 1), Date(2016, 1, 1)],
end_date=[Date(2020, 1, 17), Date(2018, 5, 4), Date(2020, 12, 31)],
status=["Claim", "Lapse", "Inforce"]
)
1 |
1 |
2016-07-04 |
2020-01-17 |
Claim |
2 |
2 |
2016-01-01 |
2018-05-04 |
Lapse |
3 |
3 |
2016-01-01 |
2020-12-31 |
Inforce |
Define the start and end of the study:
study_end = Date(2020, 6, 30)
study_start = Date(2018, 6, 30)
Calculate the exposure by broadcasting the exposure function over the three arrays we are passing to it:
df.exposure = exposure.(
ExperienceAnalysis.Anniversary(Year(1)), # The basis for our exposures
df.issue, # The `from` date
df.end_date, # the last observed date
df.status .== "Claim"; # a boolean vector indicating continuation
study_start=study_start,
study_end=study_end
)
3-element Vector{Vector{@NamedTuple{from::Date, to::Date, policy_timestep::Int64}}}:
[(from = Date("2018-06-30"), to = Date("2018-07-03"), policy_timestep = 2), (from = Date("2018-07-04"), to = Date("2019-07-03"), policy_timestep = 3), (from = Date("2019-07-04"), to = Date("2020-07-03"), policy_timestep = 4)]
[]
[(from = Date("2018-06-30"), to = Date("2018-12-31"), policy_timestep = 3), (from = Date("2019-01-01"), to = Date("2019-12-31"), policy_timestep = 4), (from = Date("2020-01-01"), to = Date("2020-06-30"), policy_timestep = 5)]
In our dataframe, we actually have a column that contains an array of tuples now, so to expand it so that each exposure period gets a row, we flatten
the dataframe to get our exposures:
df = flatten(df, :exposure)
1 |
1 |
2016-07-04 |
2020-01-17 |
Claim |
(from = Date("2018-06-30"), to = Date("2018-07-03"), policy_timestep = 2) |
2 |
1 |
2016-07-04 |
2020-01-17 |
Claim |
(from = Date("2018-07-04"), to = Date("2019-07-03"), policy_timestep = 3) |
3 |
1 |
2016-07-04 |
2020-01-17 |
Claim |
(from = Date("2019-07-04"), to = Date("2020-07-03"), policy_timestep = 4) |
4 |
3 |
2016-01-01 |
2020-12-31 |
Inforce |
(from = Date("2018-06-30"), to = Date("2018-12-31"), policy_timestep = 3) |
5 |
3 |
2016-01-01 |
2020-12-31 |
Inforce |
(from = Date("2019-01-01"), to = Date("2019-12-31"), policy_timestep = 4) |
6 |
3 |
2016-01-01 |
2020-12-31 |
Inforce |
(from = Date("2020-01-01"), to = Date("2020-06-30"), policy_timestep = 5) |
Exposure Fraction
This can be extended to calculate the decimal fraction of the year under different day count conventions, such as assuming 30/360 or Actual/365, etc. using the DayCounts.jl
package.
using DayCounts
df.exposure_fraction = map(e -> yearfrac(e.from, e.to, DayCounts.Actual360()), df.exposure)
df[:, [:exposure, :exposure_fraction]]
1 |
(from = Date("2018-06-30"), to = Date("2018-07-03"), policy_timestep = 2) |
0.00833333 |
2 |
(from = Date("2018-07-04"), to = Date("2019-07-03"), policy_timestep = 3) |
1.01111 |
3 |
(from = Date("2019-07-04"), to = Date("2020-07-03"), policy_timestep = 4) |
1.01389 |
4 |
(from = Date("2018-06-30"), to = Date("2018-12-31"), policy_timestep = 3) |
0.511111 |
5 |
(from = Date("2019-01-01"), to = Date("2019-12-31"), policy_timestep = 4) |
1.01111 |
6 |
(from = Date("2020-01-01"), to = Date("2020-06-30"), policy_timestep = 5) |
0.502778 |
Discussion and Questions
If you have other ideas or questions, feel free to also open an issue, or discuss on the community Zulip or Slack #actuary channel. We welcome all actuarial and related disciplines!