Introduction to fastlife¶
This notebooks explores the fastlife model, a parallel processing model, by taking a closer look at some of the Spaces unique to the fastlife model.
If you’re viewing this page as a static HTML page on https://lifelib.io, the same contents are also available here on binder as Jupyter notebook executable online (it may take a while to load). To run this notebook and get all the outputs below, Go to the Cell menu above, and then click Run All.
Reading the fastlife model¶
The fastlife model is saved as a folder named model in the fastlife folder. To create a live model, import modelx and call read_model
function by passing the folder path.
[1]:
import modelx as mx
model = mx.read_model("model")
The previously created model is renamed automatically to avoid name conflict. To get all existing models, get_models
modelx API function can be used. get_models
returns a dict of all the existing models associated with their names.
[2]:
import modelx as mx
mx.get_models()
[2]:
{'fastlife': <Model fastlife>}
Calculating the results in the Projection Space¶
The present values of net cashflows are calculated in PV_NetCashsflow
Cells in the Projection
Space.
[14]:
model.Projection.PV_NetCashflow(0)
[14]:
Policy
1 8.954018e+03
2 7.511092e+03
3 9.173907e+03
4 7.638071e+03
5 9.418541e+03
...
296 2.599794e+06
297 2.298079e+06
298 2.557191e+06
299 2.242406e+06
300 2.510715e+06
Length: 300, dtype: float64
Unlike the simplelife model, PV_NetCashflow
returns a pandas Seris object with Policy index. Each element of the returned Series is the present value of the net cashflow of each model point. Below is the formula of PV_NetCashflow
.
[13]:
model.Projection.PV_NetCashflow.formula
[13]:
def PV_NetCashflow(t):
"""Present value of net cashflow"""
return (PV_PremIncome(t)
+ PV_ExpsTotal(t)
+ PV_BenefitTotal(t))
As you see, PV_NetCashflow
at time 0 is the sum of PV_PremIncome
, PV_ExpsTotal
and PV_BenefitTotal
.
[16]:
model.Projection.PV_PremIncome(0)
[16]:
Policy
1 2.932812e+04
2 2.418886e+04
3 3.019898e+04
4 2.466945e+04
5 3.118342e+04
...
296 3.218643e+06
297 3.066867e+06
298 3.198456e+06
299 3.038678e+06
300 3.176323e+06
Name: PV_PremIncome, Length: 300, dtype: float64
[18]:
model.Projection.PV_PremIncome.formula
[18]:
def PV_PremIncome(t):
"""Present value of premium income"""
exist = (t <= last_t())
if not exist.any():
return 0
else:
result = exist * PremIncome(t) + PV_PremIncome(t+1) / (1 + DiscRate(t))
result.name = "PV_PremIncome"
return result
Most of the Cells in Projection
Space operate on Serieses indexed by Policy just like PV_NetCashflow
, because their precedent Cells operate on Serieses with the same index.
The Policy Space¶
The Projection
Space have a child Space named Policy
. Policy
contains policy data and Cells to calculate policyholder values. The PolicyData
Reference holds a PandasData
object, which internaly stores policy data read from an input file as a pandas DataFrame.
[22]:
model.Projection.Policy.PolicyData
[22]:
<modelx.io.pandasio.PandasData at 0x24259490b48>
To get the DataFrame stored in the PolicyData
object, call it:
[21]:
model.Projection.Policy.PolicyData()
[21]:
Product | PolicyType | Gen | Channel | Duration | Sex | IssueAge | PaymentMode | PremFreq | PolicyTerm | MaxPolicyTerm | PolicyCount | SumAssured | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Policy | |||||||||||||
1 | TERM | 1 | 1 | NaN | 0 | M | 30 | 1 | 12 | 15 | 65 | 1 | 1000000 |
2 | TERM | 1 | 1 | NaN | 0 | F | 30 | 1 | 12 | 15 | 65 | 1 | 1000000 |
3 | TERM | 1 | 1 | NaN | 0 | M | 31 | 1 | 12 | 15 | 64 | 1 | 1000000 |
4 | TERM | 1 | 1 | NaN | 0 | F | 31 | 1 | 12 | 15 | 64 | 1 | 1000000 |
5 | TERM | 1 | 1 | NaN | 0 | M | 32 | 1 | 12 | 15 | 63 | 1 | 1000000 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
296 | ENDW | 1 | 1 | NaN | 0 | F | 77 | 1 | 12 | 10 | 10 | 1 | 1000000 |
297 | ENDW | 1 | 1 | NaN | 0 | M | 78 | 1 | 12 | 10 | 10 | 1 | 1000000 |
298 | ENDW | 1 | 1 | NaN | 0 | F | 78 | 1 | 12 | 10 | 10 | 1 | 1000000 |
299 | ENDW | 1 | 1 | NaN | 0 | M | 79 | 1 | 12 | 10 | 10 | 1 | 1000000 |
300 | ENDW | 1 | 1 | NaN | 0 | F | 79 | 1 | 12 | 10 | 10 | 1 | 1000000 |
300 rows × 13 columns
PolicyData
is a PandasData
object, and it was created by the new_pandas method of UserSpace
. The location of the input file can be acquired as its path
attribute.
[26]:
model.Projection.Policy.PolicyData.path
[26]:
WindowsPath('Input/PoliyData.xlsx')
There are many Cells in Policy
whose roles are for calculating policyholder values such as premiums and cash surrender values from commutation functions and actuarial notations. For example, GrossPremRate
is for calculating gross premium rates:
[29]:
model.Projection.Policy.GrossPremRate.formula
[29]:
def GrossPremRate():
"""Gross Premium Rate per Sum Assured per payment"""
def get_value(pol):
prod = pol['Product']
alpha = pol['LoadAcqSA']
beta = pol['LoadMaintPrem']
delta = pol['LoadMaintPrem2']
gamma = pol['LoadMaintSA']
gamma2 = pol['LoadMaintSA2']
freq = pol['PremFreq']
x, n, m = pol['IssueAge'], pol['PolicyTerm'], pol['PolicyTerm']
comf = LifeTable[pol['Sex'], pol['IntRate_PREM'], pol['TableID_PREM']]
if prod == 'TERM' or prod == 'WL':
return (comf.Axn(x, n) + alpha + gamma * comf.AnnDuenx(x, n, freq)
+ gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / (1-beta-delta) / freq / comf.AnnDuenx(x, m, freq)
elif prod == 'ENDW':
return (comf.Exn(x, n) + comf.Axn(x, n) + alpha + gamma * comf.AnnDuenx(x, n, freq)
+ gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / (1-beta-delta) / freq / comf.AnnDuenx(x, m, freq)
else:
raise ValueError('invalid product')
result = PolicyDataExt1().apply(get_value, axis=1)
result.name = 'GrossPremRate'
return result
As we see in the Projection
Space, GrossPremRate
also retuns results for all model points in a Series with the Policy index.
[28]:
model.Projection.Policy.GrossPremRate()
[28]:
Policy
1 0.000298
2 0.000245
3 0.000307
4 0.000250
5 0.000317
...
296 0.043845
297 0.045945
298 0.044096
299 0.046412
300 0.044383
Name: GrossPremRate, Length: 300, dtype: float64
The Assumptions Space¶
Projection
has another space named Assumptions
. Assumptions
associates projection assumptions to model points, by looking up paramters in a table stored as an ExcelRange
object associated to a Reference named Assumption
.
[32]:
model.Projection.Assumptions.Assumption
[32]:
<modelx.io.excelio.ExcelRange at 0x24259490e88>
Just like PandasData
objects, Assumption
has the path
attribute hoding a path to its input file.
[38]:
model.Projection.Assumptions.Assumption.path
[38]:
WindowsPath('Input/input.xlsx')
Assumption
is a dict-like object, whose keys are tuples of assumption type, product ID, policy type ID and genration ID. For example, For the assumption type ‘Surrender’ and product ‘TERM’
[36]:
model.Projection.Assumptions.Assumption["Surrender", "TERM", None, None]
[36]:
'LapseRate1'
Most of the Cells in the Assumption
Space are for lookup operations just like the above example. The lookup results are also in Series.
[37]:
model.Projection.Assumptions.SurrRateID()
[37]:
Policy
1 LapseRate1
2 LapseRate1
3 LapseRate1
4 LapseRate1
5 LapseRate1
...
296 LapseRate1
297 LapseRate1
298 LapseRate1
299 LapseRate1
300 LapseRate1
Length: 300, dtype: object