Other functions

This article features functions that are not covered in other articles.

import actxps as xp
import polars as pl
from datetime import date

Working with aggregate experience data

Seriatim-level policy experience data is often not available for analysis. This is almost always the case with industry studies that contain experience data submitted by multiple parties. In these cases, experience is grouped by a several common policy attributes and aggregated accordingly.

The typical workflow in actxps of ExposedDF().exp_stats() for termination studies or ExposedDF().add_transactions().trx_stats() for transaction studies doesn’t apply if the starting data is aggregated. That is because another party has already gone through the steps of creating exposure records and performing an initial level of aggregation.

Actxps provides two functions designed to work with aggregate experience data.

  • For termination studies, ExpStats.from_DataFrame() converts a data frame of aggregate experience into an ExpStats object. = For transaction studies, TrxStats.from_DataFrame() converts a data frame of aggregate experience into a TrxStats object.

Both object classes have a summary() method which summarizes experience across any grouping variables passed to the function. The output of summary() will always be another ExpStats (or TrxStats) object, and will look just like the results of exp_stats() (or trx_stats()). For downstream reporting, summary results can be passed to the visualization methods plot() and table().

The agg_sim_dat data set contains aggregate experience on a theoretical block of deferred annuity contracts. Below, ExpStats.from_DataFrame() is used to convert the data to an ExpStats, and summary() is called using multiple grouping variables.

agg_sim_dat = xp.load_agg_sim_dat()
agg_sim_ExpStats = xp.ExpStats.from_DataFrame(
    agg_sim_dat,
    col_exposure="exposure_n",
    col_claims="claims_n",
    conf_int=True,
    start_date=2005,
    end_date=2019,
    target_status="Surrender")

Results summarized by policy year

agg_sim_ExpStats.summary('pol_yr')
Experience study results

Groups: pol_yr
Target status: Surrender
Study range: 2005 to 2019

shape: (15, 7)
┌────────┬──────────┬────────┬──────────────┬──────────┬─────────────┬─────────────┐
│ pol_yr ┆ n_claims ┆ claims ┆ exposure     ┆ q_obs    ┆ q_obs_lower ┆ q_obs_upper │
│ ---    ┆ ---      ┆ ---    ┆ ---          ┆ ---      ┆ ---         ┆ ---         │
│ i64    ┆ i64      ┆ i64    ┆ f64          ┆ f64      ┆ f64         ┆ f64         │
╞════════╪══════════╪════════╪══════════════╪══════════╪═════════════╪═════════════╡
│ 1      ┆ 102      ┆ 102    ┆ 19252.212366 ┆ 0.005298 ┆ 0.004311    ┆ 0.006337    │
│ 2      ┆ 160      ┆ 160    ┆ 17714.780418 ┆ 0.009032 ┆ 0.007677    ┆ 0.010443    │
│ 3      ┆ 124      ┆ 124    ┆ 16097.137376 ┆ 0.007703 ┆ 0.006399    ┆ 0.00907     │
│ 4      ┆ 168      ┆ 168    ┆ 14535.852953 ┆ 0.011558 ┆ 0.009838    ┆ 0.013346    │
│ 5      ┆ 164      ┆ 164    ┆ 12915.526244 ┆ 0.012698 ┆ 0.01084     ┆ 0.014634    │
│ …      ┆ …        ┆ …      ┆ …            ┆ …        ┆ …           ┆ …           │
│ 11     ┆ 804      ┆ 804    ┆ 4390.402949  ┆ 0.183127 ┆ 0.171738    ┆ 0.194515    │
│ 12     ┆ 330      ┆ 330    ┆ 2663.379931  ┆ 0.123903 ┆ 0.111512    ┆ 0.136668    │
│ 13     ┆ 99       ┆ 99     ┆ 1619.90935   ┆ 0.061115 ┆ 0.050003    ┆ 0.072844    │
│ 14     ┆ 62       ┆ 62     ┆ 871.838738   ┆ 0.071114 ┆ 0.055056    ┆ 0.088319    │
│ 15     ┆ 17       ┆ 17     ┆ 268.178711   ┆ 0.063391 ┆ 0.037289    ┆ 0.093221    │
└────────┴──────────┴────────┴──────────────┴──────────┴─────────────┴─────────────┘

Results summarized by income guarantee presence and product

agg_sim_ExpStats.summary('inc_guar', 'product')
Experience study results

Groups: inc_guar, product
Target status: Surrender
Study range: 2005 to 2019

shape: (6, 8)
┌──────────┬─────────┬──────────┬────────┬──────────────┬──────────┬─────────────┬─────────────┐
│ inc_guar ┆ product ┆ n_claims ┆ claims ┆ exposure     ┆ q_obs    ┆ q_obs_lower ┆ q_obs_upper │
│ ---      ┆ ---     ┆ ---      ┆ ---    ┆ ---          ┆ ---      ┆ ---         ┆ ---         │
│ bool     ┆ cat     ┆ i64      ┆ i64    ┆ f64          ┆ f64      ┆ f64         ┆ f64         │
╞══════════╪═════════╪══════════╪════════╪══════════════╪══════════╪═════════════╪═════════════╡
│ false    ┆ a       ┆ 449      ┆ 449    ┆ 12738.212621 ┆ 0.035248 ┆ 0.032108    ┆ 0.038467    │
│ false    ┆ b       ┆ 392      ┆ 392    ┆ 13489.708578 ┆ 0.029059 ┆ 0.026242    ┆ 0.03195     │
│ false    ┆ c       ┆ 760      ┆ 760    ┆ 25895.314881 ┆ 0.029349 ┆ 0.027302    ┆ 0.031434    │
│ true     ┆ a       ┆ 361      ┆ 361    ┆ 19966.02882  ┆ 0.018081 ┆ 0.016278    ┆ 0.019934    │
│ true     ┆ b       ┆ 273      ┆ 273    ┆ 19694.192761 ┆ 0.013862 ┆ 0.012237    ┆ 0.015538    │
│ true     ┆ c       ┆ 634      ┆ 634    ┆ 40850.51051  ┆ 0.01552  ┆ 0.014345    ┆ 0.016719    │
└──────────┴─────────┴──────────┴────────┴──────────────┴──────────┴─────────────┴─────────────┘

ExpStats.from_DataFrame() and TrxStats.from_DataFrame() contain several arguments for optional calculations like confidence intervals, expected values, weighting variables, and more. These arguments mirror the functionality in exp_stats() and trx_stats(). Both functions also contain multiple arguments for specifying column names associated with required values like exposures and claims.

Policy duration functions

The pol_*() family of functions calculates policy years, months, quarters, or weeks. Each function accepts strings, single dates, or Polars series of dates and issue dates.

Example: assume a policy was issued on 2022-05-10 and we are interested in calculating various policy duration values at the end of calendar years 2022-2032.

dates = pl.date_range(
    date(2022, 12, 31),
    date(2033, 12, 31),
    interval = '1y', 
    eager=True)

# policy years
xp.pol_yr(dates, "2022-05-10")
shape: (12,)
u32
1
2
3
4
5
6
7
8
9
10
11
12
# policy quarters
xp.pol_qtr(dates, "2022-05-10")
shape: (12,)
u32
3
7
11
15
19
23
27
31
35
39
43
47
# policy months
xp.pol_mth(dates, "2022-05-10")
shape: (12,)
u32
8
20
32
44
56
68
80
92
104
116
128
140
# policy weeks
xp.pol_wk(dates, "2022-05-10")
shape: (12,)
u32
34
86
139
191
243
295
347
399
452
504
556
608

Fractional period functions

The frac_*() family of functions calculates the fractional number of years, months, quarters, or weeks between two sets of dates. Similar to the pol_*() functions, strings, single dates, or Polars series are accepted.

Example: Continuing from the previous example, fractional durations from an issue date of 2022-05-10 can be calculated as follows:

xp.frac_yr("2022-05-10", dates)
shape: (12,)
f64
0.643836
1.642077
2.643836
3.643836
4.643836
5.642077
6.643836
7.643836
8.643836
9.642077
10.643836
11.643836
xp.frac_qtr("2022-05-10", dates)
shape: (12,)
f64
2.554348
6.554348
10.554348
14.554348
18.554348
22.554348
26.554348
30.554348
34.554348
38.554348
42.554348
46.554348
xp.frac_mth("2022-05-10", dates)
shape: (12,)
f64
7.677419
19.677419
31.677419
43.677419
55.677419
67.677419
79.677419
91.677419
103.677419
115.677419
127.677419
139.677419
xp.frac_wk("2022-05-10", dates)
shape: (12,)
literal
f64
33.571429
85.714286
138.0
190.142857
242.285714
294.428571
346.714286
398.857143
451.0
503.142857
555.428571
607.571429