2.13.3. Denuit (2019 and 2022)

2.13.3.1. Poisson/Discrete Example (6.1)

Example from Denuit [2019], Size-biased transform and conditional mean risk sharing, with application to p2p insurance and tontines.

In [1]: from aggregate import build, qd

In [2]: p = build('''
   ...: port Denuit6.1
   ...:     agg P1 0.08 claims dsev [1 2 3 4] [.1  .2  .4 .3] poisson
   ...:     agg P2 0.08 claims dsev [1 2 3 4] [.15 .25 .3 .3] poisson
   ...:     agg P3 0.10 claims dsev [1 2 3 4] [.1  .2  .4 .3] poisson
   ...:     agg P4 0.10 claims dsev [1 2 3 4] [.15 .25 .3 .3] poisson
   ...: ''', bs=1, log2=10)
   ...: 

In [3]: qd(p)

            E[X] Est E[X]    Err E[X]   CV(X) Est CV(X)  Skew(X) Est Skew(X)
unit  X                                                                     
P1    Freq  0.08                       3.5355             3.5355            
      Sev    2.9      2.9 -1.1102e-16 0.32531   0.32531 -0.51452    -0.51452
      Agg  0.232    0.232  4.0856e-13  3.7179    3.7179   3.9518      3.9518
P2    Freq  0.08                       3.5355             3.5355            
      Sev   2.75     2.75           0 0.37921   0.37921 -0.28106    -0.28106
      Agg   0.22     0.22  3.7326e-13  3.7812    3.7812   4.0928      4.0928
P3    Freq   0.1                       3.1623             3.1623            
      Sev    2.9      2.9 -1.1102e-16 0.32531   0.32531 -0.51452    -0.51452
      Agg   0.29     0.29 -4.5741e-14  3.3254    3.3254   3.5346      3.5346
P4    Freq   0.1                       3.1623             3.1623            
      Sev   2.75     2.75           0 0.37921   0.37921 -0.28106    -0.28106
      Agg  0.275    0.275  1.1036e-13   3.382     3.382   3.6607      3.6607
total Freq  0.36                       1.6667             1.6667            
      Sev  2.825    2.825 -3.3307e-16 0.35299           -0.40097            
      Agg  1.017    1.017 -5.6621e-15  1.7675    1.7675   1.8952      1.8952
log2 = 10, bandwidth = 1, validation: fails agg mean error >> sev, possible aliasing; try larger bs.

Computation of \(\mathsf{E}[X_i\mid X=x]\) and \(\mathsf{E}[X_i\mid X=x]/x\) as a function of \(x\). The first function, called \(\kappa_i(x)\) in PIR, is computed automatically by the portfolio class as exeqa_line (expectation given \(X\) equals \(x\)). The original figure is shown below.

In [4]: bit = p.density_df.query('p_total > 0').iloc[1:]

In [5]: rat = bit.filter(regex='exeqa_P').apply(
   ...:     lambda x: x / bit.loss.to_numpy(), axis=0)
   ...: 

In [6]: ax = rat.plot.bar(ylim=[-0.05,1.05], stacked=True, figsize=(3.5, 2.45))

In [7]: ax.set(xlim=[-0.5, 15.5], ylim=[0,1]);

In [8]: ax.legend().set(visible=False);
../../_images/denuit_19.png Original paper table.

All the values are available as a table. These are consistent with numbers mentioned in the text.

In [9]: from pandas import option_context

In [10]: b = bit.filter(regex='exeqa_P').apply(
   ....:     lambda x: x / bit.loss.to_numpy(), axis=0)
   ....: 

In [11]: b.index = b.index.astype(int)

In [12]: b.index.name = 'a'

In [13]: qd(b)

    exeqa_P1  exeqa_P2  exeqa_P3  exeqa_P4
a                                         
1    0.17778   0.26667   0.22222   0.33333
2    0.19729   0.24716   0.24661   0.30895
3    0.25219   0.19226   0.31523   0.24032
4    0.22212   0.22232   0.27765    0.2779
5    0.22524   0.21921   0.28155   0.27401
6    0.23238   0.21207   0.29047   0.26508
7    0.23486   0.20958   0.29358   0.26198
8    0.22365    0.2208   0.27956     0.276
9    0.23064    0.2138   0.28831   0.26725
10   0.23224    0.2122    0.2903   0.26526
11   0.23056   0.21388    0.2882   0.26735
12   0.22551   0.21893   0.28189   0.27366
..       ...       ...       ...       ...
30   0.22858   0.21586   0.28573   0.26982
31   0.22829   0.21615   0.28537   0.27017
32   0.22872   0.21571    0.2859   0.26963
33   0.22893   0.21549   0.28617   0.26937
34   0.22839     0.216   0.28551   0.26999
35   0.22829    0.2161   0.28537   0.27006
36   0.22865   0.21576   0.28577   0.26966
37   0.22863   0.21571   0.28579   0.26959
38   0.22816   0.21601   0.28522    0.2699
39   0.22816   0.21599   0.28513   0.26976
40   0.22849   0.21582   0.28545   0.26962
41   0.22806   0.21556   0.28498    0.2692

Proportion of expected loss by unit.

In [14]: bb = p.describe.xs('Agg', axis=0, level=1)[['E[X]']]

In [15]: qd(bb / bb.iloc[-1,0])

         E[X]
unit         
P1    0.22812
P2    0.21632
P3    0.28515
P4     0.2704
total       1

2.13.3.2. Mortality Example and Figure

Example from Denuit et al. [2022], Mortality Credits with Large Survivor Funds. Reproducing Figure 4.5.

In [16]: import matplotlib.pyplot as plt; import pandas as pd

In [17]: wl = 0.6; wh = 1 - wl; ql = .1; qh = .2; al = 1; ah = 3

In [18]: ports = {}

In [19]: for n in (10, 20, 50, 100):
   ....:     ports[n] = build(f'port DR.4.3 '
   ....:           f'agg Low.q  {wl * n * ql} claims dsev [{al}] binomial {ql}'
   ....:           f'agg High.q {wh * n * qh} claims dsev [{ah}] binomial {qh}'
   ....:          , bs=1, log2=8)
   ....: 

In [20]: audit = pd.concat([i.describe for i in ports.values()], keys=ports.keys(), names=['n', 'unit', 'X'])

In [21]: qd(audit.xs('Agg', axis=0, level=2)['E[X]'].unstack(1))

unit  Low.q  High.q  total
n                         
10      0.6     2.4      3
20      1.2     4.8      6
50        3      12     15
100       6      24     30

In [22]: fig, axs = plt.subplots(2, 2, figsize=(2 * 3.5, 2 * 3.5), constrained_layout=True, squeeze=True)

In [23]: for ax, (n, port), mx, t in zip(axs.flat, ports.items(), [20, 25, 40, 60], [2, 5, 10, 10]):
   ....:     lm = [-1, mx]
   ....:     port.density_df.query('p_total > 0').filter(regex='exeqa_[LHt]').plot(ax=ax, xlim=lm, ylim=lm)
   ....:     ax.set_xticks(range(0, mx, t))
   ....:     ax.set_yticks(range(0, mx, t))
   ....:     ax.grid(lw=.25, c='w')
   ....:     ax.set(title=f'{n} risks')
   ....: 

In [24]: fig.suptitle('Denuit Figure 4.5')
Out[24]: Text(0.5, 0.98, 'Denuit Figure 4.5')
../../_images/denuit_45.png