From b3a5b135b1a920d73ac3c5881c137968f3f6f6e6 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 29 Jun 2026 18:36:04 -0700 Subject: [PATCH 01/27] Chapter 11 prep (#1078) * WIP * making progress * promoting _param_property * fixing bug * full implementation w/ test * bugbot and tests * test fix * rubric * docstring and additional tests * bugbot proof * Update test_disposal.py * docstring and additional tests * bugbot proof * Update test_disposal.py * more bugbot fixes * bug fix * fix * forcing numpy after all * more fixes * more fixes * addressing reviewer comments * added missing dependencies * restructuring disposal attributes into mixin class * adding `is_disposal_rate` attribute to triangle * improving docstring and adding codecov coverage * chapter 11 prep * streamlining single dev weights * doctest fixes * additional tests --- chainladder/core/base.py | 5 +- chainladder/utils/data/_manifest.py | 8 +- .../utils/data/friedland_auto_freq_sev.csv | 112 +++++++-------- .../utils/data/friedland_gl_insurer.csv | 74 +++++----- .../utils/data/friedland_xyz_auto_bi.csv | 134 +++++++++--------- chainladder/utils/data/friedland_xyz_disp.csv | 37 ----- chainladder/utils/tests/test_utilities.py | 30 ++++ chainladder/utils/triangle_weight.py | 58 ++++---- chainladder/utils/weighted_regression.py | 22 ++- docs/friedland/chapter_6.rst | 38 ++--- docs/friedland/chapter_7.rst | 84 +++++------ 11 files changed, 305 insertions(+), 297 deletions(-) delete mode 100644 chainladder/utils/data/friedland_xyz_disp.csv diff --git a/chainladder/core/base.py b/chainladder/core/base.py index ce3aded7f..c5f0e78fe 100644 --- a/chainladder/core/base.py +++ b/chainladder/core/base.py @@ -368,7 +368,7 @@ def nan_triangle(self): This becomes useful when managing array arithmetic. """ xp = self.get_array_module() - if self.is_pattern or self.is_ultimate: + if self.is_ultimate: return xp.ones(self.values.shape[2:], dtype="float16") val_array = np.array(self.valuation).reshape(self.shape[-2:], order="f") nan_triangle = np.array(pd.DataFrame(val_array) > self.valuation_date) @@ -600,6 +600,9 @@ def _drop_subtriangles(self): del self.sigma_ if "std_err_" in sub_tris: del self.std_err_ + #w_ is currently stored as an ndarray + if hasattr(self, "w_"): + del self.w_ @property def subtriangles(self): diff --git a/chainladder/utils/data/_manifest.py b/chainladder/utils/data/_manifest.py index fa336b13d..5b30d9bcc 100644 --- a/chainladder/utils/data/_manifest.py +++ b/chainladder/utils/data/_manifest.py @@ -148,6 +148,7 @@ "Reported Claim Counts", "Disposal Rate", "Paid Claims", + "Reported Claims", ], "cumulative": True, }, @@ -277,13 +278,6 @@ "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium"], "cumulative": True, }, - "friedland_xyz_disp": { - "origin": "Accident Year", - "development": "Calendar Year", - "index": None, - "columns": ["Disposal Rate", "Closed Claim Counts", "Paid Claims"], - "cumulative": True, - }, "genins": { "origin": "origin", "development": "development", diff --git a/chainladder/utils/data/friedland_auto_freq_sev.csv b/chainladder/utils/data/friedland_auto_freq_sev.csv index bd91893cd..6a22a655a 100644 --- a/chainladder/utils/data/friedland_auto_freq_sev.csv +++ b/chainladder/utils/data/friedland_auto_freq_sev.csv @@ -1,56 +1,56 @@ -Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims -2003-07,2003-07,2547,3556,14235000,4003, -2003-07,2004-01,3262,3314,14960000,4514, -2003-07,2004-07,3287,3301,14921000,4520, -2003-07,2005-01,3291,3299,14911000,4520, -2003-07,2005-07,3292,3295,14926000,4530, -2003-07,2006-01,3292,3294,14864000,4512, -2003-07,2006-07,3292,3293,14860000,4513, -2003-07,2007-01,3292,3293,14854000,4511, -2003-07,2007-07,3292,3293,14850000,4510, -2003-07,2008-01,3292,3292,14847000,4510,14846000 -2004-01,2004-01,2791,3492,14548000,4166, -2004-01,2004-07,3217,3262,14674000,4498, -2004-01,2005-01,3240,3250,14643000,4506, -2004-01,2005-07,3242,3247,14626000,4505, -2004-01,2006-01,3243,3247,14621000,4503, -2004-01,2006-07,3243,3245,14610000,4502, -2004-01,2007-01,3243,3245,14610000,4502, -2004-01,2007-07,3243,3244,14611000,4504, -2004-01,2008-01,3242,3243,14617000,4507,14614000 -2004-07,2004-07,2099,2980,12129000,4070, -2004-07,2005-01,2677,2712,12576000,4637, -2004-07,2005-07,2695,2704,12541000,4638, -2004-07,2006-01,2697,2702,12531000,4638, -2004-07,2006-07,2697,2700,12523000,4683, -2004-07,2007-01,2698,2700,12523000,4638, -2004-07,2007-07,2698,2699,12510000,4635, -2004-07,2008-01,2698,2699,12502000,4632,12502000 -2005-01,2005-01,2370,2896,11980000,4137, -2005-01,2005-07,2735,2768,11921000,4307, -2005-01,2006-01,2751,2761,11882000,4304, -2005-01,2006-07,2754,2758,11862000,4301, -2005-01,2007-01,2755,2758,11854000,4298, -2005-01,2007-07,2755,2758,11844000,4294, -2005-01,2008-01,2756,2757,11841000,4295,11840000 -2005-07,2005-07,1966,2814,11283000,4010, -2005-07,2006-01,2609,2650,11843000,4469, -2005-07,2006-07,2630,2640,11805000,4472, -2005-07,2007-01,2634,2639,11789000,4467, -2005-07,2007-07,2634,2638,11772000,4462, -2005-07,2008-01,2634,2636,11770000,4465,11765000 -2006-01,2006-01,2261,2808,11947000,4254, -2006-01,2006-07,2671,2712,11856000,4372, -2006-01,2007-01,2694,2704,11820000,4371, -2006-01,2007-07,2696,2701,11772000,4359, -2006-01,2008-01,2697,2700,11760000,4356,11755000 -2006-07,2006-07,1949,2799,12503000,4467, -2006-07,2007-01,2637,2675,12762000,4771, -2006-07,2007-07,2659,2670,12706000,4759, -2006-07,2008-01,2662,2668,12697000,4759,12679000 -2007-01,2007-01,2059,2578,11662000,4524, -2007-01,2007-07,2496,2533,11523000,4549, -2007-01,2008-01,2520,2529,11492000,4544,11406000 -2007-07,2007-07,2083,2791,12647000,4531, -2007-07,2008-01,2732,2778,12854000,4627,12648000 -2008-01,2008-01,2533,3139,14071000,4483,11833000 +Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims +2003-07,2003-07,2547,3556,14234668,4003, +2003-07,2004-01,3262,3314,14959396,4514, +2003-07,2004-07,3287,3301,14920520,4520, +2003-07,2005-01,3291,3299,14911480,4520, +2003-07,2005-07,3292,3295,14926350,4530, +2003-07,2006-01,3292,3294,14862528,4512, +2003-07,2006-07,3292,3293,14861309,4513, +2003-07,2007-01,3292,3293,14854723,4511, +2003-07,2007-07,3292,3293,14851430,4510, +2003-07,2008-01,3292,3292,14846920,4510,14846000 +2004-01,2004-01,2791,3492,14547672,4166, +2004-01,2004-07,3217,3262,14672476,4498, +2004-01,2005-01,3240,3250,14644500,4506, +2004-01,2005-07,3242,3247,14627735,4505, +2004-01,2006-01,3243,3247,14621241,4503, +2004-01,2006-07,3243,3245,14608990,4502, +2004-01,2007-01,3243,3245,14608990,4502, +2004-01,2007-07,3243,3244,14610976,4504, +2004-01,2008-01,3242,3243,14616201,4507,14614000 +2004-07,2004-07,2099,2980,12128600,4070, +2004-07,2005-01,2677,2712,12575544,4637, +2004-07,2005-07,2695,2704,12541152,4638, +2004-07,2006-01,2697,2702,12531876,4638, +2004-07,2006-07,2697,2700,12522600,4683, +2004-07,2007-01,2698,2700,12522600,4638, +2004-07,2007-07,2698,2699,12509865,4635, +2004-07,2008-01,2698,2699,12501768,4632,12502000 +2005-01,2005-01,2370,2896,11980752,4137, +2005-01,2005-07,2735,2768,11921776,4307, +2005-01,2006-01,2751,2761,11883344,4304, +2005-01,2006-07,2754,2758,11862158,4301, +2005-01,2007-01,2755,2758,11853884,4298, +2005-01,2007-07,2755,2758,11842852,4294, +2005-01,2008-01,2756,2757,11841315,4295,11840000 +2005-07,2005-07,1966,2814,11284140,4010, +2005-07,2006-01,2609,2650,11842850,4469, +2005-07,2006-07,2630,2640,11806080,4472, +2005-07,2007-01,2634,2639,11788413,4467, +2005-07,2007-07,2634,2638,11770756,4462, +2005-07,2008-01,2634,2636,11769740,4465,11765000 +2006-01,2006-01,2261,2808,11945232,4254, +2006-01,2006-07,2671,2712,11856864,4372, +2006-01,2007-01,2694,2704,11819184,4371, +2006-01,2007-07,2696,2701,11773659,4359, +2006-01,2008-01,2697,2700,11761200,4356,11755000 +2006-07,2006-07,1949,2799,12503133,4467, +2006-07,2007-01,2637,2675,12762425,4771, +2006-07,2007-07,2659,2670,12706530,4759, +2006-07,2008-01,2662,2668,12697012,4759,12679000 +2007-01,2007-01,2059,2578,11662872,4524, +2007-01,2007-07,2496,2533,11522617,4549, +2007-01,2008-01,2520,2529,11491776,4544,11406000 +2007-07,2007-07,2083,2791,12646021,4531, +2007-07,2008-01,2732,2778,12853806,4627,12648000 +2008-01,2008-01,2533,3139,14072137,4483,11833000 diff --git a/chainladder/utils/data/friedland_gl_insurer.csv b/chainladder/utils/data/friedland_gl_insurer.csv index 66804cdae..b4bb74346 100644 --- a/chainladder/utils/data/friedland_gl_insurer.csv +++ b/chainladder/utils/data/friedland_gl_insurer.csv @@ -1,37 +1,37 @@ -Accident Year,Calendar Year,Closed Claim Counts,Reported Claim Counts,Disposal Rate,Paid Claims -2001,2001,195,1299,0.223,1119962 -2001,2002,375,1077,0.43,4373268 -2001,2003,510,1057,0.584,8398345 -2001,2004,625,965,0.716,13490793 -2001,2005,702,930,0.804,17372233 -2001,2006,752,917,0.862,22052662 -2001,2007,780,864,0.894,27359691 -2001,2008,796,870,0.912,29901361 -2002,2002,199,847,0.277,1411957 -2002,2003,349,945,0.485,6287005 -2002,2004,445,864,0.618,11443820 -2002,2005,508,787,0.706,15520552 -2002,2006,563,784,0.782,21295572 -2002,2007,594,743,0.826,28410418 -2002,2008,626,731,0.87,32468911 -2003,2003,106,800,0.169,984748 -2003,2004,294,831,0.47,6128957 -2003,2005,383,762,0.612,10470758 -2003,2006,453,704,0.724,14604684 -2003,2007,499,669,0.797,21936647 -2003,2008,542,636,0.866,23942499 -2004,2004,126,823,0.2,1158659 -2004,2005,281,862,0.447,5811172 -2004,2006,377,797,0.599,10497504 -2004,2007,445,728,0.707,15087416 -2004,2008,494,684,0.785,18242570 -2005,2005,114,828,0.194,1198767 -2005,2006,249,850,0.423,5103837 -2005,2007,315,765,0.536,9042134 -2005,2008,403,687,0.685,15443929 -2006,2006,114,824,0.206,1220778 -2006,2007,229,809,0.414,4594746 -2006,2008,300,734,0.543,8983864 -2007,2007,79,604,0.18,796774 -2007,2008,188,620,0.429,4233641 -2008,2008,127,812,0.209,1445365 +Accident Year,Calendar Year,Closed Claim Counts,Reported Claim Counts,Disposal Rate,Paid Claims,Reported Claims +2001,2001,195,1299,0.223,1119962, +2001,2002,375,1077,0.43,4373268, +2001,2003,510,1057,0.584,8398345, +2001,2004,625,965,0.716,13490793, +2001,2005,702,930,0.804,17372233, +2001,2006,752,917,0.862,22052662, +2001,2007,780,864,0.894,27359691, +2001,2008,796,870,0.912,29901361,35592000 +2002,2002,199,847,0.277,1411957, +2002,2003,349,945,0.485,6287005, +2002,2004,445,864,0.618,11443820, +2002,2005,508,787,0.706,15520552, +2002,2006,563,784,0.782,21295572, +2002,2007,594,743,0.826,28410418, +2002,2008,626,731,0.87,32468911,36330000 +2003,2003,106,800,0.169,984748, +2003,2004,294,831,0.47,6128957, +2003,2005,383,762,0.612,10470758, +2003,2006,453,704,0.724,14604684, +2003,2007,499,669,0.797,21936647, +2003,2008,542,636,0.866,23942499,31900000 +2004,2004,126,823,0.2,1158659, +2004,2005,281,862,0.447,5811172, +2004,2006,377,797,0.599,10497504, +2004,2007,445,728,0.707,15087416, +2004,2008,494,684,0.785,18242570,39716000 +2005,2005,114,828,0.194,1198767, +2005,2006,249,850,0.423,5103837, +2005,2007,315,765,0.536,9042134, +2005,2008,403,687,0.685,15443929,32667000 +2006,2006,114,824,0.206,1220778, +2006,2007,229,809,0.414,4594746, +2006,2008,300,734,0.543,8983864,27774000 +2007,2007,79,604,0.18,796774, +2007,2008,188,620,0.429,4233641,16246000 +2008,2008,127,812,0.209,1445365,8216000 diff --git a/chainladder/utils/data/friedland_xyz_auto_bi.csv b/chainladder/utils/data/friedland_xyz_auto_bi.csv index 4f4753c30..a98639033 100644 --- a/chainladder/utils/data/friedland_xyz_auto_bi.csv +++ b/chainladder/utils/data/friedland_xyz_auto_bi.csv @@ -1,67 +1,67 @@ -Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium -1998,1998,,,,,,,20000 -1998,1999,,,,,,,20000 -1998,2000,6309,11171,,,4861,,20000 -1998,2001,8521,12380,510,634,3859,19526,20000 -1998,2002,10082,13216,547,635,3134,20813,20000 -1998,2003,11620,14067,575,635,2447,22152,20000 -1998,2004,13242,14688,598,637,1446,23058,20000 -1998,2005,14419,16366,612,637,1947,25692,20000 -1998,2006,15311,16163,620,637,853,25374,20000 -1998,2007,15764,15835,635,637,71,24859,20000 -1998,2008,15822,15822,637,637,0,24839,20000 -1999,1999,,,,,,,31500 -1999,2000,4666,13255,,,8589,,31500 -1999,2001,9861,16405,686,1026,6544,15989,31500 -1999,2002,13971,19639,819,1039,5668,18902,31500 -1999,2003,18127,22473,910,1047,4347,21464,31500 -1999,2004,22032,23764,980,1050,1732,22632,31500 -1999,2005,23511,25094,1007,1053,1583,23831,31500 -1999,2006,24146,24795,1036,1047,649,23682,31500 -1999,2007,24592,25071,1039,1047,479,23946,31500 -1999,2008,24817,25107,1044,1047,290,23980,31500 -2000,2000,1302,15676,,,14374,,45000 -2000,2001,6513,18749,650,1354,12237,13847,45000 -2000,2002,12139,21900,932,1397,9760,15676,45000 -2000,2003,17828,27144,1095,1411,9316,19237,45000 -2000,2004,24030,29488,1216,1410,5458,20914,45000 -2000,2005,28853,34458,1292,1408,5605,24473,45000 -2000,2006,33222,36949,1367,1408,3727,26242,45000 -2000,2007,35902,37505,1391,1408,1603,26637,45000 -2000,2008,36782,37246,1402,1408,465,26453,45000 -2001,2001,1539,11827,304,1305,10288,9063,50000 -2001,2002,5952,16004,681,1421,10052,11262,50000 -2001,2003,12319,21022,936,1449,8703,14508,50000 -2001,2004,18609,26578,1092,1458,7969,18229,50000 -2001,2005,24387,34205,1225,1458,9818,23460,50000 -2001,2006,31090,37136,1357,1455,6046,25523,50000 -2001,2007,37070,38541,1432,1455,1471,26489,50000 -2001,2008,38519,38798,1446,1455,278,26665,50000 -2002,2002,2318,12811,203,1342,10494,9546,61183 -2002,2003,7932,20370,607,1514,12439,13455,61183 -2002,2004,13822,26656,841,1548,12833,17219,61183 -2002,2005,22095,37667,1089,1557,15572,24192,61183 -2002,2006,31945,44414,1327,1549,12469,28673,61183 -2002,2007,40629,48701,1464,1552,8072,31379,61183 -2002,2008,44437,48169,1523,1554,3731,30997,61183 -2003,2003,1743,9651,181,1373,7908,7029,69175 -2003,2004,6240,16995,614,1616,10755,10517,69175 -2003,2005,12683,30354,941,1630,17671,18622,69175 -2003,2006,22892,40594,1263,1626,17702,24966,69175 -2003,2007,34505,44231,1507,1629,9726,27152,69175 -2003,2008,39320,44373,1568,1629,5052,27239,69175 -2004,2004,2221,16995,235,1932,14774,8796,99322 -2004,2005,9898,40180,848,2168,30281,18533,99322 -2004,2006,25950,58866,1442,2234,32916,26350,99322 -2004,2007,43439,71707,1852,2249,28268,31884,99322 -2004,2008,52811,70288,2029,2258,17477,31129,99322 -2005,2005,3043,28674,295,2067,25631,13872,138151 -2005,2006,12219,47432,1119,2293,35213,20686,138151 -2005,2007,27073,70340,1664,2367,43268,29717,138151 -2005,2008,40026,70655,1946,2390,30629,29563,138151 -2006,2006,3531,27066,307,1473,23535,18375,107578 -2006,2007,11778,46783,906,1645,35005,28440,107578 -2006,2008,22819,48804,1201,1657,25985,29453,107578 -2007,2007,3529,19477,329,1192,15948,16340,62438 -2007,2008,11865,31732,791,1264,19867,25104,62438 -2008,2008,3409,18632,276,1036,15223,17985,47797 +Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium +1998,1998,,,,,,,20000 +1998,1999,,,,,,,20000 +1998,2000,6309,11171,,,4861,,20000 +1998,2001,8521,12380,510,634,3859,19526,20000 +1998,2002,10082,13216,547,635,3134,20813,20000 +1998,2003,11620,14067,575,635,2447,22152,20000 +1998,2004,13242,14688,598,637,1446,23058,20000 +1998,2005,14419,16366,612,637,1947,25692,20000 +1998,2006,15311,16163,620,637,853,25374,20000 +1998,2007,15764,15835,635,637,71,24859,20000 +1998,2008,15822,15822,637,637,0,24839,20000 +1999,1999,,,,,,,31500 +1999,2000,4666,13255,,,8589,,31500 +1999,2001,9861,16405,686,1026,6544,15989,31500 +1999,2002,13971,19639,819,1039,5668,18902,31500 +1999,2003,18127,22473,910,1047,4347,21464,31500 +1999,2004,22032,23764,980,1050,1732,22632,31500 +1999,2005,23511,25094,1007,1053,1583,23831,31500 +1999,2006,24146,24795,1036,1047,649,23682,31500 +1999,2007,24592,25071,1039,1047,479,23946,31500 +1999,2008,24817,25107,1044,1047,290,23980,31500 +2000,2000,1302,15676,,,14374,,45000 +2000,2001,6513,18749,650,1354,12237,13847,45000 +2000,2002,12139,21900,932,1397,9760,15676,45000 +2000,2003,17828,27144,1095,1411,9316,19237,45000 +2000,2004,24030,29488,1216,1410,5458,20914,45000 +2000,2005,28853,34458,1292,1408,5605,24473,45000 +2000,2006,33222,36949,1367,1408,3727,26242,45000 +2000,2007,35902,37505,1391,1408,1603,26637,45000 +2000,2008,36782,37246,1402,1408,465,26453,45000 +2001,2001,1539.456,11827,304,1305,10288,9063,50000 +2001,2002,5952.241,16004,681,1421,10052,11262,50000 +2001,2003,12319.336,21022,936,1449,8703,14508,50000 +2001,2004,18608.788,26578,1092,1458,7969,18229,50000 +2001,2005,24386.973,34205,1225,1458,9818,23460,50000 +2001,2006,31090.065,37136,1357,1455,6046,25523,50000 +2001,2007,37069.815,38541,1432,1455,1471,26489,50000 +2001,2008,38519.529,38798,1446,1455,278,26665,50000 +2002,2002,2317.651,12811,203,1342,10494,9546,61183 +2002,2003,7931.635,20370,607,1514,12439,13455,61183 +2002,2004,13822.585,26656,841,1548,12833,17219,61183 +2002,2005,22095.617,37667,1089,1557,15572,24192,61183 +2002,2006,31945.485,44414,1327,1549,12469,28673,61183 +2002,2007,40628.819,48701,1464,1552,8072,31379,61183 +2002,2008,44437.623,48169,1523,1554,3731,30997,61183 +2003,2003,1743.211,9651,181,1373,7908,7029,69175 +2003,2004,6240.349,16995,614,1616,10755,10517,69175 +2003,2005,12683.23,30354,941,1630,17671,18622,69175 +2003,2006,22892.562,40594,1263,1626,17702,24966,69175 +2003,2007,34505.01,44231,1507,1629,9726,27152,69175 +2003,2008,39320.472,44373,1568,1629,5052,27239,69175 +2004,2004,2221.22,16995,235,1932,14774,8796,99322 +2004,2005,9898.432,40180,848,2168,30281,18533,99322 +2004,2006,25950.094,58866,1442,2234,32916,26350,99322 +2004,2007,43439.464,71707,1852,2249,28268,31884,99322 +2004,2008,52811.083,70288,2029,2258,17477,31129,99322 +2005,2005,3042.925,28674,295,2067,25631,13872,138151 +2005,2006,12218.989,47432,1119,2293,35213,20686,138151 +2005,2007,27072.964,70340,1664,2367,43268,29717,138151 +2005,2008,40026.352,70655,1946,2390,30629,29563,138151 +2006,2006,3531.114,27066,307,1473,23535,18375,107578 +2006,2007,11778.146,46783,906,1645,35005,28440,107578 +2006,2008,22819.111,48804,1201,1657,25985,29453,107578 +2007,2007,3528.854,19477,329,1192,15948,16340,62438 +2007,2008,11864.72,31732,791,1264,19867,25104,62438 +2008,2008,3408.876,18632,276,1036,15223,17985,47797 diff --git a/chainladder/utils/data/friedland_xyz_disp.csv b/chainladder/utils/data/friedland_xyz_disp.csv deleted file mode 100644 index 8f9607efc..000000000 --- a/chainladder/utils/data/friedland_xyz_disp.csv +++ /dev/null @@ -1,37 +0,0 @@ -Accident Year,Calendar Year,Disposal Rate,Closed Claim Counts,Paid Claims -2001,2001,0.209,304,1539 -2001,2002,0.468,681,5952 -2001,2003,0.643,936,12319 -2001,2004,0.751,1092,18609 -2001,2005,0.842,1225,24387 -2001,2006,0.933,1357,31090 -2001,2007,0.984,1432,37070 -2001,2008,0.994,1446,38519 -2002,2002,0.131,203,2318 -2002,2003,0.391,607,7932 -2002,2004,0.541,841,13822 -2002,2005,0.701,1089,22095 -2002,2006,0.854,1327,31945 -2002,2007,0.842,1464,40629 -2002,2008,0.98,1523,44437 -2003,2003,0.111,181,1743 -2003,2004,0.377,614,6240 -2003,2005,0.577,941,12683 -2003,2006,0.775,1263,22892 -2003,2007,0.924,1507,34505 -2003,2008,0.962,1568,39320 -2004,2004,0.104,235,2221 -2004,2005,0.375,848,9898 -2004,2006,0.637,1442,25950 -2004,2007,0.819,1852,43439 -2004,2008,0.897,2029,52811 -2005,2005,0.123,295,3043 -2005,2006,0.466,1119,12219 -2005,2007,0.693,1664,27073 -2005,2008,0.81,1946,40026 -2006,2006,0.183,307,3531 -2006,2007,0.54,906,11778 -2006,2008,0.716,1201,22819 -2007,2007,0.251,329,3529 -2007,2008,0.605,791,11865 -2008,2008,0.236,276,3409 diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 9c4615b27..1e516922c 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -1242,3 +1242,33 @@ def test_describe_option_invalid_regex() -> None: """ with pytest.raises(ValueError, match="not a valid regular expression"): cl.options.describe_option('[') + + +def test_triangleweight_drop_valuation_all(raa: Triangle) -> None: + ''' + Testing error raising from dropping too many valuations + ''' + with pytest.raises(Exception): + _ = cl.TriangleWeight( + drop_valuation=[ + '1981', + '1982', + '1983', + '1984', + '1985', + '1986', + '1987', + '1988', + '1989', + '1990', + ] + ).fit(raa) + +def test_triangleweight_full_triangle(raa: Triangle) -> None: + ''' + Testing new path that allows weights on full triangles + ''' + ult = cl.Chainladder().fit(raa) + tw = cl.TriangleWeight(n_periods = 4).fit(raa) + tw_full = cl.TriangleWeight(n_periods = 4).fit(ult.full_triangle_) + assert tw.w_.iloc[:,:,:,0] == tw_full.w_.iloc[:,:,:,0] \ No newline at end of file diff --git a/chainladder/utils/triangle_weight.py b/chainladder/utils/triangle_weight.py index a0ebbfae2..7399c7418 100644 --- a/chainladder/utils/triangle_weight.py +++ b/chainladder/utils/triangle_weight.py @@ -15,7 +15,9 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from chainladder.core.typing import TriangleLike + from chainladder import Triangle + from chainladder.core.typing import TriangleProtocol + class TriangleWeight(BaseEstimator,TransformerMixin): """ @@ -84,7 +86,7 @@ def __init__( drop_high: bool | int | list[bool] | list[int] | None = None, drop_low: bool | int | list[bool] | list[int] | None = None, preserve: int = 1, - drop_valuation: str | list[str] = None, + drop_valuation: str | list[str] | None = None, drop_above: float = np.inf, drop_below: float = 0.00, ): @@ -97,7 +99,7 @@ def __init__( self.drop_below = drop_below self.drop = drop - def fit(self, X: TriangleLike, y: None = None, sample_weight: None = None): + def fit(self, X: TriangleProtocol, y: None = None, sample_weight: None = None): """ Fit the model with X. @@ -115,17 +117,16 @@ def fit(self, X: TriangleLike, y: None = None, sample_weight: None = None): self : object Returns the instance itself. """ - self.w_ = self._set_weight_func(X=X,secondary_rank=sample_weight) return self - def transform(self, X: TriangleLike): + def transform(self, X: TriangleProtocol) -> Triangle: """If X and self are of different shapes, align self to X, else return self. Parameters ---------- - X : Triangle + X : TriangleProtocol The triangle to be transformed Returns @@ -175,9 +176,9 @@ def _cascade_param( def _set_weight_func( self, - X: TriangleLike, - secondary_rank: TriangleLike | None = None - ) -> TriangleLike: + X: TriangleProtocol, + secondary_rank: TriangleProtocol | None = None + ) -> Triangle: """ Combines weights from all parameters @@ -212,18 +213,18 @@ def _set_weight_func( return w_tri - def _assign_n_periods_weight_func(self, X: TriangleLike) -> TriangleLike: + def _assign_n_periods_weight_func(self, X: TriangleProtocol) -> np.ndarray: """ Generates weights for the `n_periods` parameter Parameters ---------- - X: TriangleLike + X: TriangleProtocol Triangle of values to be weighted Returns ------- - A Triangle of weights + numpy array of weights """ # cascading n_periods across all columns @@ -242,11 +243,16 @@ def _assign_n_periods_weight_int(X, n_periods): if n_periods < 1 or n_periods >= X.shape[-2]: return X.values * 0 + 1 else: - val_date_min = X.valuation[X.valuation <= X.valuation_date] - val_date_min = val_date_min.drop_duplicates().sort_values() z = -n_periods * val_offset[X.development_grain][X.origin_grain] - val_date_min = val_date_min[z] - w = X[X.valuation >= val_date_min] + #adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) + if X.is_full: + w = X.copy() + w.values[:,:,:z,:] = np.nan + else: + val_date_min = X.valuation[X.valuation <= X.valuation_date] + val_date_min = val_date_min.drop_duplicates().sort_values() + val_date_min = val_date_min[z] + w = X[X.valuation >= val_date_min] return xp.nan_to_num((w / w).values) * X.nan_triangle xp = X.get_array_module() @@ -265,9 +271,9 @@ def _assign_n_periods_weight_int(X, n_periods): def _drop_n_func( self, - X: TriangleLike, - secondary_rank: TriangleLike | None = None - ) -> TriangleLike: + X: TriangleProtocol, + secondary_rank: TriangleProtocol | None = None + ) -> np.ndarray: """ Generates weights for the `drop_high` and `drop_low` parameter @@ -280,7 +286,7 @@ def _drop_n_func( Returns ------- - A Triangle of weights + numpy array of weights """ # Preparing to set up 3D array for drop_n parameters @@ -362,7 +368,7 @@ def _drop_n_func( return w.astype(float) - def _drop_func(self, X: TriangleLike) -> TriangleLike: + def _drop_func(self, X: TriangleProtocol) -> np.ndarray: """ Generates weights for the `drop` parameter @@ -373,7 +379,7 @@ def _drop_func(self, X: TriangleLike) -> TriangleLike: Returns ------- - A Triangle of weights + numpy array of weights """ # get the appropriate backend for nan_to_num @@ -399,7 +405,7 @@ def _drop_func(self, X: TriangleLike) -> TriangleLike: w[(origin_ind, dev_ind)] = 0 return xp.nan_to_num(w)[None, None] - def _drop_valuation_func(self, X: TriangleLike) -> TriangleLike: + def _drop_valuation_func(self, X: TriangleProtocol) -> np.ndarray: """ Generates weights for the `drop` parameter @@ -410,7 +416,7 @@ def _drop_valuation_func(self, X: TriangleLike) -> TriangleLike: Returns ------- - A Triangle of weights + numpy array of weights """ # get the appropriate backend for nan_to_num @@ -434,7 +440,7 @@ def _drop_valuation_func(self, X: TriangleLike) -> TriangleLike: raise Exception("The entire triangle has been dropped via drop_valuation.") return w - def _drop_x_func(self, X: TriangleLike) -> TriangleLike: + def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: """ Generates weights for the `drop_above` and `drop_below` parameters @@ -445,7 +451,7 @@ def _drop_x_func(self, X: TriangleLike) -> TriangleLike: Returns ------- - A Triangle of weights + numpy array of weights """ # Preparing to set up 3D array for drop_x parameters diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 925ac5d63..cc375af32 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -144,17 +144,29 @@ def _fit_OLS(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) + x_mean = xp.nansum(w * x, axis) / xp.nansum(w, axis) + y_mean = xp.nansum(y * w, axis) / xp.nansum(w, axis) slope = num_to_nan( - xp.nansum(w * x * y, axis) - - xp.nansum(x * w, axis) * xp.nanmean(y, axis) + xp.nansum(w * x * y, axis) - xp.nansum(x * w, axis) * y_mean ) / num_to_nan( - xp.nansum(w * x * x, axis) - - xp.nanmean(x, axis) * xp.nansum(w * x, axis) + xp.nansum(w * x * x, axis) - xp.nansum(x * w, axis) * x_mean ) - intercept = xp.nanmean(y, axis) - slope * xp.nanmean(x, axis) + intercept = y_mean - slope * x_mean + + fitted_value = xp.repeat(xp.expand_dims(slope, axis), x.shape[axis], axis) + fitted_value = fitted_value * x * (y * 0 + 1) + xp.repeat(xp.expand_dims(intercept, axis), x.shape[axis], axis) + + residual = (y - fitted_value) + + wss_residual = xp.nansum(residual ** 2 * w, axis) + + r2 = 1 - wss_residual / num_to_nan( + xp.nansum(w * y * y, axis) - xp.nansum(y * w, axis) * y_mean + ) self.slope_ = slope[..., None] self.intercept_ = intercept[..., None] + self.rsq_ = r2[..., None] return self diff --git a/docs/friedland/chapter_6.rst b/docs/friedland/chapter_6.rst index 6af1648a9..95a4f2f04 100644 --- a/docs/friedland/chapter_6.rst +++ b/docs/friedland/chapter_6.rst @@ -87,14 +87,14 @@ Table 3 - Paid Claim Development Triangle .. doctest:: >>> tri['Paid Claims'] - 12 24 36 48 60 72 84 - 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 - 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN - 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN - 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN - 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN - 2007 3529.0 11865.0 NaN NaN NaN NaN NaN - 2008 3409.0 NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 + 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 + 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN + 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN + 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN + 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN + 2007 3528.854 11864.720 NaN NaN NaN NaN NaN + 2008 3408.876 NaN NaN NaN NaN NaN NaN Table 4 - Ratio of Reported Claims to Earned Premium ####################################################### @@ -188,7 +188,7 @@ Table 6 - Ratio of Paid Claims-to-Reported Claims 2002 0.181 0.389 0.519 0.587 0.719 0.834 0.923 2003 0.181 0.367 0.418 0.564 0.780 0.886 NaN 2004 0.131 0.246 0.441 0.606 0.751 NaN NaN - 2005 0.106 0.258 0.385 0.566 NaN NaN NaN + 2005 0.106 0.258 0.385 0.567 NaN NaN NaN 2006 0.130 0.252 0.468 NaN NaN NaN NaN 2007 0.181 0.374 NaN NaN NaN NaN NaN 2008 0.183 NaN NaN NaN NaN NaN NaN @@ -279,10 +279,10 @@ Table 13 – Average Paid Claim Development Triangle >>> (tri["Paid Claims"] / tri["Closed Claim Counts"] * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 11419.0 13068.0 16435.0 20289.0 24073.0 27752.0 29177.0 - 2003 9630.0 10163.0 13478.0 18125.0 22896.0 25077.0 NaN - 2004 9451.0 11672.0 17996.0 23455.0 26028.0 NaN NaN - 2005 10315.0 10920.0 16270.0 20568.0 NaN NaN NaN + 2002 11417.0 13067.0 16436.0 20290.0 24073.0 27752.0 29178.0 + 2003 9631.0 10163.0 13478.0 18126.0 22896.0 25077.0 NaN + 2004 9452.0 11673.0 17996.0 23455.0 26028.0 NaN NaN + 2005 10315.0 10920.0 16270.0 20569.0 NaN NaN NaN 2006 11502.0 13000.0 19000.0 NaN NaN NaN NaN 2007 10726.0 15000.0 NaN NaN NaN NaN NaN 2008 12351.0 NaN NaN NaN NaN NaN NaN @@ -294,10 +294,10 @@ Table 14 – Average Case Outstanding Development Triangle >>> ((tri["Reported Claims"] - tri["Paid Claims"]) / (tri["Reported Claim Counts"] - tri["Closed Claim Counts"]) * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 9212.0 13713.0 18153.0 33274.0 56167.0 91727.0 120387.0 - 2003 6634.0 10734.0 25647.0 48766.0 79721.0 82836.0 NaN - 2004 8706.0 22941.0 41561.0 71204.0 76319.0 NaN NaN - 2005 14464.0 29994.0 61546.0 68984.0 NaN NaN NaN - 2006 20184.0 47368.0 56985.0 NaN NaN NaN NaN - 2007 18480.0 42002.0 NaN NaN NaN NaN NaN + 2002 9213.0 13714.0 18152.0 33272.0 56164.0 91729.0 120367.0 + 2003 6634.0 10733.0 25647.0 48764.0 79721.0 82828.0 NaN + 2004 8706.0 22941.0 41560.0 71203.0 76318.0 NaN NaN + 2005 14464.0 29994.0 61546.0 68983.0 NaN NaN NaN + 2006 20184.0 47368.0 56984.0 NaN NaN NaN NaN + 2007 18480.0 42003.0 NaN NaN NaN NaN NaN 2008 20030.0 NaN NaN NaN NaN NaN NaN diff --git a/docs/friedland/chapter_7.rst b/docs/friedland/chapter_7.rst index 9b2630a8e..63c7110c8 100644 --- a/docs/friedland/chapter_7.rst +++ b/docs/friedland/chapter_7.rst @@ -494,35 +494,35 @@ Exhibit II Sheet 2 p111 >>> paid_devs = dev_exhibit(tri['Paid Claims'],assumptions,'volume_2',1.01) PART 1 - Data Triangle - 12 24 36 48 60 72 84 96 108 120 132 - 1998 NaN NaN 6309.0 8521.0 10082.0 11620.0 13242.0 14419.0 15311.0 15764.0 15822.0 - 1999 NaN 4666.0 9861.0 13971.0 18127.0 22032.0 23511.0 24146.0 24592.0 24817.0 NaN - 2000 1302.0 6513.0 12139.0 17828.0 24030.0 28853.0 33222.0 35902.0 36782.0 NaN NaN - 2001 1539.0 5952.0 12319.0 18609.0 24387.0 31090.0 37070.0 38519.0 NaN NaN NaN - 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 NaN NaN NaN NaN - 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN NaN NaN NaN NaN - 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN NaN NaN NaN NaN - 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN NaN NaN NaN NaN - 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3529.0 11865.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN - 2008 3409.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 96 108 120 132 + 1998 NaN NaN 6309.000 8521.000 10082.000 11620.000 13242.000 14419.000 15311.0 15764.0 15822.0 + 1999 NaN 4666.000 9861.000 13971.000 18127.000 22032.000 23511.000 24146.000 24592.0 24817.0 NaN + 2000 1302.000 6513.000 12139.000 17828.000 24030.000 28853.000 33222.000 35902.000 36782.0 NaN NaN + 2001 1539.456 5952.241 12319.336 18608.788 24386.973 31090.065 37069.815 38519.529 NaN NaN NaN + 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 NaN NaN NaN NaN + 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN NaN NaN NaN NaN + 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN NaN NaN NaN NaN + 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN NaN NaN NaN NaN + 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3528.854 11864.720 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2008 3408.876 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 2 - Age-to-Age Factors 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 1998 NaN NaN 1.350610 1.183194 1.152549 1.139587 1.088884 1.061863 1.029587 1.003679 1999 NaN 2.113373 1.416793 1.297473 1.215425 1.067130 1.027009 1.018471 1.009149 NaN 2000 5.002304 1.863811 1.468655 1.347880 1.200707 1.151423 1.080669 1.024511 NaN NaN - 2001 3.867446 2.069724 1.510593 1.310495 1.274860 1.192345 1.039088 NaN NaN NaN - 2002 3.421915 1.742562 1.598539 1.445802 1.271842 1.093726 NaN NaN NaN NaN - 2003 3.580034 2.032532 1.804936 1.507295 1.139545 NaN NaN NaN NaN NaN - 2004 4.456551 2.621742 1.673950 1.215751 NaN NaN NaN NaN NaN NaN - 2005 4.015445 2.215648 1.478447 NaN NaN NaN NaN NaN NaN NaN - 2006 3.335599 1.937426 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3.362142 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2001 3.866457 2.069697 1.510535 1.310508 1.274864 1.192336 1.039108 NaN NaN NaN + 2002 3.422273 1.742716 1.598516 1.445784 1.271817 1.093746 NaN NaN NaN NaN + 2003 3.579801 2.032455 1.804947 1.507259 1.139558 NaN NaN NaN NaN NaN + 2004 4.456304 2.621637 1.673962 1.215740 NaN NaN NaN NaN NaN NaN + 2005 4.015541 2.215647 1.478462 NaN NaN NaN NaN NaN NaN NaN + 2006 3.335533 1.937411 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3.362202 NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 3 - Average Age-to-Age Factor 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 simple_5 3.750 2.110 1.613 1.365 1.220 1.129 1.059 1.035 1.019 1.004 simple_3 3.571 2.258 1.652 1.390 1.229 1.146 1.049 1.035 1.019 1.004 - simple_2 3.349 2.077 1.576 1.362 1.206 1.143 1.060 1.021 1.019 1.004 + simple_2 3.349 2.077 1.576 1.361 1.206 1.143 1.060 1.021 1.019 1.004 volume_4 3.713 2.206 1.615 1.342 1.218 1.128 1.056 1.030 1.017 1.004 volume_3 3.550 2.238 1.619 1.349 1.222 1.141 1.051 1.030 1.017 1.004 volume_2 3.349 2.079 1.574 1.316 1.203 1.136 1.059 1.022 1.017 1.004 @@ -543,17 +543,17 @@ Exhibit II Sheet 3 p112 >>> exhibit = rounded_development_summary(reported_devs["Selected"],paid_devs["Selected"]) >>> exhibit Age Reported Claims Paid Claims Reported CDF Paid CDF Reported Ultimate Paid Ultimate - 1998 132 15822.0 15822.0 1.000 1.010 15822.0 15980.0 - 1999 120 25107.0 24817.0 0.999 1.014 25082.0 25164.0 - 2000 108 37246.0 36782.0 0.992 1.031 36948.0 37922.0 - 2001 96 38798.0 38519.0 0.992 1.054 38488.0 40599.0 - 2002 84 48169.0 44437.0 1.003 1.116 48314.0 49592.0 - 2003 72 44373.0 39320.0 1.013 1.268 44950.0 49858.0 - 2004 60 70288.0 52811.0 1.064 1.525 74786.0 80537.0 - 2005 48 70655.0 40026.0 1.085 2.007 76661.0 80332.0 - 2006 36 48804.0 22819.0 1.196 3.160 58370.0 72108.0 - 2007 24 31732.0 11865.0 1.512 6.569 47979.0 77941.0 - 2008 12 18632.0 3409.0 2.551 21.999 47530.0 74995.0 + 1998 132 15822.0 15822.000 1.000 1.010 15822.0 15980.0 + 1999 120 25107.0 24817.000 0.999 1.014 25082.0 25164.0 + 2000 108 37246.0 36782.000 0.992 1.031 36948.0 37922.0 + 2001 96 38798.0 38519.529 0.992 1.054 38488.0 40600.0 + 2002 84 48169.0 44437.623 1.003 1.116 48314.0 49592.0 + 2003 72 44373.0 39320.472 1.013 1.268 44950.0 49858.0 + 2004 60 70288.0 52811.083 1.064 1.525 74786.0 80537.0 + 2005 48 70655.0 40026.352 1.085 2.007 76661.0 80333.0 + 2006 36 48804.0 22819.111 1.196 3.160 58370.0 72108.0 + 2007 24 31732.0 11864.720 1.512 6.569 47979.0 77939.0 + 2008 12 18632.0 3408.876 2.551 21.999 47530.0 74992.0 Exhibit II Sheet 4 p113 ########################## @@ -563,17 +563,17 @@ Exhibit II Sheet 4 p113 >>> unpaid_exhibit = unpaid_summary(exhibit) >>> unpaid_exhibit[['Case Outstanding','Reported Method IBNR','Paid Method IBNR','Reported Method Unpaid','Paid Method Unpaid']] Case Outstanding Reported Method IBNR Paid Method IBNR Reported Method Unpaid Paid Method Unpaid - 1998 0.0 0.0 158.0 0.0 158.0 - 1999 290.0 -25.0 57.0 265.0 347.0 - 2000 464.0 -298.0 676.0 166.0 1140.0 - 2001 279.0 -310.0 1801.0 -31.0 2080.0 - 2002 3732.0 145.0 1423.0 3877.0 5155.0 - 2003 5053.0 577.0 5485.0 5630.0 10538.0 - 2004 17477.0 4498.0 10249.0 21975.0 27726.0 - 2005 30629.0 6006.0 9677.0 36635.0 40306.0 - 2006 25985.0 9566.0 23304.0 35551.0 49289.0 - 2007 19867.0 16247.0 46209.0 36114.0 66076.0 - 2008 15223.0 28898.0 56363.0 44121.0 71586.0 + 1998 0.000 0.0 158.0 0.000 158.000 + 1999 290.000 -25.0 57.0 265.000 347.000 + 2000 464.000 -298.0 676.0 166.000 1140.000 + 2001 278.471 -310.0 1802.0 -31.529 2080.471 + 2002 3731.377 145.0 1423.0 3876.377 5154.377 + 2003 5052.528 577.0 5485.0 5629.528 10537.528 + 2004 17476.917 4498.0 10249.0 21974.917 27725.917 + 2005 30628.648 6006.0 9678.0 36634.648 40306.648 + 2006 25984.889 9566.0 23304.0 35550.889 49288.889 + 2007 19867.280 16247.0 46207.0 36114.280 66074.280 + 2008 15223.124 28898.0 56360.0 44121.124 71583.124 Exhibit III Sheet 1 p114 ########################## From fea1475e7cbad1dc651e8ae9b1999acd4a7ab402 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 29 Jun 2026 18:40:57 -0700 Subject: [PATCH 02/27] adding chapter 11 --- docs/friedland/chapter_11.ipynb | 3412 +++++++++++++++++++++++++++++++ 1 file changed, 3412 insertions(+) create mode 100644 docs/friedland/chapter_11.ipynb diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb new file mode 100644 index 000000000..871b5d122 --- /dev/null +++ b/docs/friedland/chapter_11.ipynb @@ -0,0 +1,3412 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "24730109-b724-4a80-817b-c11900453da4", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "#Chapter 11 - Frequency-Severity Techniques" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "ec44a0a9-6db8-40e4-908d-df63caaec823", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "Recreating Chapter 11 of the Friendland text on reserving from Exam 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "fb4d15b5-f3b3-4fc2-90d2-7399e1cf534e", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import chainladder as cl\n", + "from IPython.display import display as nb_display" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "ff40a83c-496e-4345-8d03-5dbc368f6ba0", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#helper functions for exhibits\n", + "\n", + "#create reported/paid summary exhibit\n", + "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", + " output = reported_tri.latest_diagonal.to_frame()\n", + " output.columns = ['Reported']\n", + " output['Paid'] = paid_tri.latest_diagonal.to_frame()\n", + " #using floating point offset to achieve standard rounding\n", + " output['Ult Claims'] = (ult.latest_diagonal + 1e-9).round(0).to_frame()\n", + " output['Case Outstanding'] = output[\"Reported\"] - output[\"Paid\"]\n", + " output['IBNR'] = output[\"Ult Claims\"] - output[\"Reported\"]\n", + " output['Unpaid'] = output[\"Ult Claims\"] - output[\"Paid\"]\n", + " return output.round(0)\n", + "\n", + "#create a dict of developed triangles for each of the selection assumptions on a given page\n", + "def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", + " return {k:cl.Development(**v).fit_transform(tri) for k,v in avg_params.items()}\n", + "\n", + "#combine a dict of triangles into a singla triangle\n", + "def combine_tri(devs: dict[cl.Triangle]) -> cl.Triangle:\n", + " avgs = [v.rename('index',k) for k,v in devs.items()]\n", + " return cl.concat(avgs,axis=0)\n", + "\n", + "#combine the ldf_ of a dict of triangles into a singla triangle\n", + "def combine_ldf(devs: dict[cl.Triangle]) -> cl.Triangle:\n", + " return combine_tri({k:v.ldf_ for k,v in devs.items()})\n", + "\n", + "#create a dict of DisposalRate estimators for each of the selection assumptions on a given page\n", + "def average_dr(tri: cl.Triangle, w:cl.Triangle, avg_params: dict[str,int]) -> dict[cl.DisposalRate]:\n", + " return {k:cl.DisposalRate(**v).fit(X=tri,sample_weight = w) for k,v in avg_params.items()}\n", + "\n", + "#combine the disposal_rate_ of a dict of triangles into a singla triangle\n", + "def combine_disposal(devs: dict[cl.Triangle]) -> cl.Triangle:\n", + " return combine_tri({k:v.disposal_rate_ for k,v in devs.items()})\n", + "\n", + "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def regs(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.WeightedRegression]:\n", + " return {k:cl.WeightedRegression(\n", + " axis=2,\n", + " thru_orig=False,\n", + " xp=tri.get_array_module()\n", + " ).fit(\n", + " np.ones(tri.shape) * tri.origin.year.values[None,None,:,None],\n", + " np.log(tri.values),\n", + " cl.TriangleWeight(**v).fit(tri).w_.values\n", + " ) for k,v in avg_params.items()}\n", + "\n", + "#combine all the trends and r-squared values into pandas DataFrame\n", + "def reg_outputs(regs: dict[cl.WeightedRegression],devs:pd.Series) -> pd.DataFrame:\n", + " trend = {k:(np.exp(v.slope_)-1).flatten() for k,v in regs.items()}\n", + " trend = pd.DataFrame.from_dict(trend,orient='index')\n", + " trend.columns = devs\n", + " rsq = {k:v.rsq_.flatten() for k,v in regs.items()}\n", + " rsq = pd.DataFrame.from_dict(rsq,orient='index')\n", + " rsq.columns = devs\n", + " return trend,rsq\n", + "\n", + "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def average_sev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", + " output = {}\n", + " for k,v in avg_params.items():\n", + " w = cl.TriangleWeight(**v).fit(tri)\n", + " weighted_tri = tri * w.w_.values\n", + " output[k] = weighted_tri.mean()\n", + " return output" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "1f3f99fd-cb60-4901-807f-71273d213005", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Analysis\n", + "\n", + "Displaying all the triangles and dataframes take up quite a bit of code. We will distill the core analysis code at the top of each exhibit" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "68273ff4-16dd-4239-842e-e4c602ca6e08", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "e1_tri = cl.load_sample('friedland_auto_freq_sev')\n", + "e1_cnt_assumptions = {}\n", + "e1_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e1_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e1_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e1_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", + "e1_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e1_sev_assumptions = {}\n", + "e1_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e1_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e1_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "\n", + "#developing closed claim counts\n", + "e1_ccc_devs = average_dev(e1_tri['Closed Claim Counts'],e1_cnt_assumptions)\n", + "e1_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_ccc_devs['simple_3'])\n", + "e1_ccc_selected.ldf_ = e1_ccc_selected.ldf_.round(3)\n", + "\n", + "#developing reported claim counts\n", + "e1_rcc_devs = average_dev(e1_tri['Reported Claim Counts'],e1_cnt_assumptions)\n", + "e1_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rcc_devs['simple_3'])\n", + "e1_rcc_selected.ldf_ = e1_rcc_selected.ldf_.round(3)\n", + "\n", + "#combining closed and reported claim counts\n", + "e1_ccc_cl = cl.Chainladder().fit(e1_ccc_selected)\n", + "e1_rcc_cl = cl.Chainladder().fit(e1_rcc_selected)\n", + "e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_)/2\n", + "e1_cc_ult.iloc[:,:,-1,:] = e1_rcc_cl.ultimate_.iloc[:,:,-1,:]\n", + "\n", + "#calculating and developing reported severity\n", + "e1_rsev = e1_tri['Reported Claims'] / e1_tri['Reported Claim Counts']\n", + "e1_rsev_devs = average_dev(e1_rsev,e1_sev_assumptions)\n", + "e1_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rsev_devs['medial_5x1'])\n", + "e1_rsev_selected.ldf_ = e1_rsev_selected.ldf_.round(3)\n", + "\n", + "#combining developed count and severity\n", + "e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)\n", + "e1_ult = e1_rsev_cl.ultimate_ * e1_cc_ult / 1000" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "65209fce-b487-4443-92bc-17da6ca31835", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "1762546d-c6e0-4fa7-8f27-a780fad04a77", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e1_tri['Closed Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e1_ccc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e1_ccc_selected.cdf_.round(3))\n", + "print('Percent Closed')\n", + "nb_display(1/e1_ccc_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d91cc78f-5868-4711-a5a0-d49a9b331ec9", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "b76a2ed5-adf7-4ca3-900d-757daf1b730c", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e1_tri['Reported Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e1_rcc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e1_rcc_selected.cdf_.round(3))\n", + "print('Percent Reported')\n", + "nb_display(1/e1_rcc_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "10316a34-7ae5-41f6-b266-70fdda592cd6", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d5ebe00f-0b16-47dc-94a5-41a188939fb1", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]\n", + "e1_s3[\"Selected Ult CC\"] = e1_cc_ult.latest_diagonal.to_frame()\n", + "e1_s3[['Closed CDF','Reported CDF']] = e1_s3[['Closed CDF','Reported CDF']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", + "e1_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "a8b310a4-5622-4d1a-8217-7fac37ac21c4", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 4" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "114ad9f1-ac4f-49a0-ba34-cd5abbf598b9", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "(e1_tri['Closed Claim Counts']/e1_tri['Reported Claim Counts']).round(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "b876aa8e-280f-4e37-9b2b-0d83ebaf86e1", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "6f4ad9ef-4ff1-4939-b282-702b7312d298", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "nb_display(e1_tri['Reported Claims']/1000)\n", + "nb_display(e1_rsev)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "090cd8df-b99a-438a-b727-80d208b93a6d", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 6" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "33761774-ce20-472f-9f3f-fe94fa470f0f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e1_rsev)\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e1_rsev.age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factors')\n", + "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "nb_display(e1_rsev_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e1_rsev_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "16ae37e3-32c6-44c0-9d8f-6a9295b0d113", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 7" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "900d666c-de59-444c-b9d2-47d547895f00", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']\n", + "e1_s7[\"Ult Claims\"] = e1_ult.latest_diagonal.to_frame()\n", + "e1_s7['CDF'] = e1_s7['CDF'].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e1_s7[['Reported Severity','Ult Severity','Ult Claims']] = (e1_s7[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", + "e1_s7" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "3135d7a1-01f8-46aa-bf62-3cd6e0953696", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit I Sheet 8" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5e3ea0bc-d45c-4d62-9864-0497606a78f0", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "79910de8-366a-45a5-8706-704fccdf4a6c", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit I Sheet 1\n", + "assert np.all(e1_ccc_selected.cdf_.round(3).values == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", + "#Exhibit I Sheet 2\n", + "assert np.all(e1_rcc_selected.cdf_.round(3).values == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", + "#Exhibit I Sheet 3\n", + "assert np.allclose(\n", + " e1_s3['Selected Ult CC'].values,\n", + " np.array([3292, 3243, 2699, 2757, 2635, 2699, 2665, 2524, 2764, 3061]),\n", + " atol=1\n", + ")\n", + "#Exhibit I Sheet 6\n", + "assert np.all(e1_rsev_selected.cdf_.round(3).values == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", + "#Exhibit I Sheet 7\n", + "assert np.allclose(\n", + " e1_s7['Ult Severity'].values,\n", + " np.array([4510, 4507, 4632, 4295, 4465, 4356, 4754, 4535, 4613, 4644]),\n", + " atol=1\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "dd3aa676-954a-470b-b812-3f9c1492ed66", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit II Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "a36cd4db-3d8d-481b-b702-a58cd3b36f8a", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "e2_tri = cl.load_sample('friedland_xyz_auto_bi')\n", + "e2_cnt_assumptions = {}\n", + "e2_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e2_cnt_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", + "e2_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e2_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e2_cnt_assumptions['volume_2'] = {'n_periods':2, 'average':'volume'}\n", + "e2_sev_assumptions = {}\n", + "e2_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e2_sev_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", + "e2_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "\n", + "#developing closed claim counts\n", + "e2_ccc_devs = average_dev(e2_tri['Closed Claim Counts'],e2_cnt_assumptions)\n", + "e2_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_ccc_devs['volume_2'])\n", + "e2_ccc_selected.ldf_ = e2_ccc_selected.ldf_.round(3)\n", + "\n", + "#developing reported claim counts\n", + "e2_rcc_devs = average_dev(e2_tri['Reported Claim Counts'],e2_cnt_assumptions)\n", + "e2_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rcc_devs['volume_2'])\n", + "e2_rcc_selected.ldf_ = e2_rcc_selected.ldf_.round(3)\n", + "\n", + "#combining closed and reported claim counts\n", + "e2_ccc_cl = cl.Chainladder().fit(e2_ccc_selected)\n", + "e2_rcc_cl = cl.Chainladder().fit(e2_rcc_selected)\n", + "e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_)/2\n", + "\n", + "#calculating and developing reported severity\n", + "e2_rsev = e2_tri['Reported Claims'] / e2_tri['Reported Claim Counts'] * 1000\n", + "e2_rsev_devs = average_dev(e2_rsev,e2_sev_assumptions)\n", + "e2_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rsev_devs['simple_2'])\n", + "e2_rsev_selected.ldf_ = e2_rsev_selected.ldf_.round(3)\n", + "\n", + "#combining developed count and severity\n", + "e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)\n", + "e2_ult = e2_rsev_cl.ultimate_ * e2_cc_ult / 1000" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c2e97f2e-16f7-4b58-ab54-ac46fb34f31c", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 1" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5f470719-d2a6-4f85-a907-653d6fe0b9f2", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e2_tri['Closed Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e2_tri['Closed Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e2_ccc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e2_ccc_selected.cdf_.round(3))\n", + "print('Percent Closed')\n", + "nb_display(1/e2_ccc_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5a9022d4-50c6-4f97-bfa3-1ce4d122a2a1", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 2" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "8795f064-01d3-440d-846f-7b402e560c40", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e2_tri['Reported Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e2_rcc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e2_rcc_selected.cdf_.round(3))\n", + "print('Percent Reported')\n", + "nb_display(1/e2_rcc_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "ff41fe54-f723-4bfa-a166-73ff106e63d1", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 3" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "e44a01bd-add3-43b6-9d16-e2d8c4303e1e", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]\n", + "e2_s3[\"Selected Ult CC\"] = e2_cc_ult.latest_diagonal.to_frame()\n", + "e2_s3[['Closed CDF','Reported CDF']] = e2_s3[['Closed CDF','Reported CDF']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", + "e2_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0f7e73e8-737b-4909-87a6-c68d0f621dbf", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 4" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 4" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0e4dd781-cb17-4cb6-b5df-c86854361fd4", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "nb_display(e2_tri['Reported Claims'])\n", + "nb_display(e2_rsev)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "be51ed2d-bf1e-4ca4-8b6a-cb891adda7ff", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 5" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f2a1fda4-9392-4509-863e-77bc09c6cc46", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e2_rsev)\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e2_rsev.age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factors')\n", + "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "nb_display(e2_rsev_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e2_rsev_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d1006396-3dfd-46b2-a0a4-96461ba2d5f2", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 6" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 6" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "710aef82-31e7-4990-b66b-6913dd2671a9", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']\n", + "e2_s6[\"Ult Claims\"] = e2_ult.latest_diagonal.to_frame()\n", + "e2_s6['CDF'] = e2_s6['CDF'].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e2_s6[['Reported Severity','Ult Severity','Ult Claims']] = (e2_s6[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", + "e2_s6" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "fb3533d3-36e4-4971-8f9a-49e76ec75d43", + "showTitle": true, + "tableResultSettingsMap": {}, + "title": "Exhibit II Sheet 7" + } + }, + "source": [ + " \n", + "##Exhibit II Sheet 7" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0375e545-7922-488a-a798-47d95b6a14d5", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "bb21e80c-521d-4fbd-a68b-ac2b7e7b6035", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit II Sheet 1\n", + "assert np.all(e2_ccc_selected.cdf_.round(3).values == np.array([6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]))\n", + "#Exhibit II Sheet 2\n", + "assert np.all(e2_rcc_selected.cdf_.round(3).values == np.array([1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", + "#Exhibit II Sheet 3\n", + "assert np.allclose(\n", + " e2_s3['Selected Ult CC'].values,\n", + " np.array([637, 1047, 1416, 1466, 1565, 1666, 2309, 2483, 1807, 1556, 1426]),\n", + " atol=1\n", + ")\n", + "#Exhibit II Sheet 5\n", + "assert np.all(e2_rsev_selected.cdf_.round(3).values == np.array([2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]))\n", + "#Exhibit II Sheet 6\n", + "assert np.allclose(\n", + " e2_s6['Ult Severity'].values,\n", + " np.array([24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]),\n", + " rtol=0.001\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "e9c5436c-4e87-448f-bc10-72e08932caf9", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "67dad577-b2db-4d73-be35-e13bbb176e56", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "e3_tri = cl.load_sample('friedland_wc_self_insurer')\n", + "e3_cnt_assumptions = {}\n", + "e3_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e3_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e3_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e3_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", + "e3_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e3_sev_assumptions = {}\n", + "e3_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e3_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e3_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e3_sevavg_assumptions = {}\n", + "e3_sevavg_assumptions['all_years'] = {}\n", + "e3_sevavg_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", + "e3_sevavg_assumptions['latest_3'] = {'n_periods':3}\n", + "\n", + "#developing closed claim counts\n", + "#There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", + "e3_ccc_devs = average_dev(e3_tri['Closed Claim Counts'],e3_cnt_assumptions)\n", + "e3_ccc_selected = cl.TailConstant(tail = 1.002, projection_period = 0).fit_transform(e3_ccc_devs['volume_5'])\n", + "e3_ccc_selected.ldf_ = e3_ccc_selected.ldf_.round(3)\n", + "\n", + "#developing reported claim counts\n", + "e3_rcc_devs = average_dev(e3_tri['Reported Claim Counts'],e3_cnt_assumptions)\n", + "e3_rcc_selected = cl.TailConstant(tail = 1.000, projection_period = 0).fit_transform(e3_rcc_devs['volume_5'])\n", + "e3_rcc_selected.ldf_ = e3_rcc_selected.ldf_.round(3)\n", + "\n", + "#combining closed and reported claim counts\n", + "e3_ccc_cl = cl.Chainladder().fit(e3_ccc_selected)\n", + "e3_rcc_cl = cl.Chainladder().fit(e3_rcc_selected)\n", + "e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_)/2\n", + "\n", + "#calculating trended frequency\n", + "e3_freq_trend = cl.Trend(-.01).fit(e3_tri['Reported Claim Counts']).trend_\n", + "e3_payroll_trend = cl.Trend(.025).fit(e3_tri['Payroll']).trend_\n", + "e3_freq_2008 = .0036\n", + "e3_freq_2007 = e3_freq_2008 / e3_freq_trend.values[0,0,-2,0] * e3_payroll_trend.latest_diagonal.values[0,0,-2,0]\n", + "\n", + "#calculating and developing paid severity\n", + "e3_psev = e3_tri[\"Paid Claims\"] / e3_tri['Closed Claim Counts']\n", + "e3_psev_devs = average_dev(e3_psev,e3_sev_assumptions)\n", + "e3_psev_selected = cl.TailConstant(tail = 1.150, projection_period = 0).fit_transform(e3_psev_devs['medial_5x1'])\n", + "e3_psev_selected.ldf_ = e3_psev_selected.ldf_.round(3)\n", + "\n", + "#calculating and developing reported severity\n", + "e3_rsev = e3_tri[\"Reported Claims\"] / e3_tri['Reported Claim Counts']\n", + "e3_rsev_devs = average_dev(e3_rsev,e3_sev_assumptions)\n", + "e3_rsev_selected = cl.TailConstant(tail = 1.025, projection_period = 0).fit_transform(e3_rsev_devs['medial_5x1'])\n", + "e3_rsev_selected.ldf_ = e3_rsev_selected.ldf_.round(3)\n", + "\n", + "#combining developed count and severity\n", + "e3_psev_cl = cl.Chainladder().fit(e3_psev_selected)\n", + "e3_resv_cl = cl.Chainladder().fit(e3_rsev_selected)\n", + "e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_)/2\n", + "\n", + "#calculated trended severity\n", + "e3_sev_trend = cl.Trend(.075).fit(e3_tri['Reported Claims']).trend_\n", + "e3_sev_ult_trended = e3_sev_ult * e3_sev_trend.latest_diagonal.values\n", + "#prep ult severity for average calculations\n", + "e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin<='2006']\n", + "e3_sevs = average_sev(e3_sev_ult_trended_2006,e3_sevavg_assumptions)\n", + "#calculate selected severities\n", + "e3_sev_2008 = 7100.\n", + "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0,0,-2,0]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "14152443-201f-4a5e-be50-5515548bea24", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_rsev.development" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "7082b089-f92a-489f-9285-2b2b808464ee", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_sev_2008" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "b8b09c7a-0457-4e83-8be5-30b2082ee425", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0e9d7db4-9db5-407e-9a4f-59b3facc0025", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e3_tri[\"Closed Claim Counts\"])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e3_ccc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e3_ccc_selected.cdf_.round(3))\n", + "print('Percent Closed')\n", + "nb_display((1/e3_ccc_selected.cdf_).round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "80da84e0-c7d8-4dd9-930c-6956ac1bcb3b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "b915789e-e2ee-4d57-b770-3dff07a10c8c", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e3_tri[\"Reported Claim Counts\"])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "nb_display(e3_rcc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e3_rcc_selected.cdf_.round(3))\n", + "print('Percent Reported')\n", + "nb_display((1/e3_rcc_selected.cdf_).round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "efa10e00-52c8-4fd8-87ac-acd7e3358960", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "23e54b21-3bea-43fb-a86f-d3f3358a303f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]\n", + "e3_s3[\"Selected Ult CC\"] = e3_cc_ult.latest_diagonal.to_frame()\n", + "e3_s3[['Closed CDF','Reported CDF']] = e3_s3[['Closed CDF','Reported CDF']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", + "e3_s3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "7a7c251e-b567-464c-bc82-597c2836ccdb", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 4" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "93b3d0d0-418d-475e-bbc9-89e5965122dc", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s4 = e3_s3[['Selected Ult CC']].copy()\n", + "e3_s4['CC Trend'] = e3_freq_trend.latest_diagonal.to_frame()\n", + "e3_s4['Trended Ult CC'] = e3_s4['Selected Ult CC'] * e3_s4['CC Trend']\n", + "e3_s4['Payroll'] = e3_tri['Payroll'].latest_diagonal.to_frame()\n", + "e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()\n", + "e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']\n", + "e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']\n", + "e3_s4[['CC Trend','Payroll Trend']] = e3_s4[['CC Trend','Payroll Trend']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e3_s4[['Trended Ult CC','Trended Payroll']] = (e3_s4[['Trended Ult CC','Trended Payroll']] + 1e-9).round(0)\n", + "e3_s4['Trended Ultimate Freq'] = e3_s4[\"Trended Ultimate Freq\"].map(\"{:.2%}\".format)\n", + "nb_display(e3_s4)\n", + "print(f\"Selected Frequency at 2008 level {e3_freq_2008:.2%}\")\n", + "print(f\"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f3e4e3fe-5297-4079-bb12-103c43e28820", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "2cb73994-4108-48d0-8c33-d4d4d6eb3972", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Paid Claims')\n", + "nb_display(e3_tri[\"Paid Claims\"])\n", + "print('Paid Severities')\n", + "nb_display(e3_psev)\n", + "print('Reported Claims')\n", + "nb_display(e3_tri[\"Reported Claims\"])\n", + "print('Reported Severities')\n", + "nb_display(e3_rsev)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d28ce8b5-8add-43d5-9609-6dac464d77ac", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 6" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "6d0d24ec-90ae-4eec-9092-a9ff7542ae12", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e3_psev)\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e3_psev.age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e3_psev_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e3_psev_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f9f3d551-9208-4773-8059-8ee160821d09", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 7" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "364d734d-6b08-46ae-a5fd-862d8ae25267", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e3_rsev)\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e3_rsev.age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e3_rsev_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e3_rsev_selected.cdf_.round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "3dda605a-2dd1-472a-bd42-ef193c95c1b1", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 8" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5540a4e8-848b-447e-aa4d-13c7bf2983ca", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Paid Sev','CDF':'Paid CDF','Ultimate':'Ult Paid Sev'})\n", + "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", + "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", + "e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)\n", + "e3_s8.iloc[:-2,:][['development','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "781252e1-a9d8-4a65-b502-ba78541d132e", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 9" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "88faaca5-a479-4753-9020-c4f7c269aa7b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s9 = e3_s8[['Selected Ult Sev']].copy()\n", + "e3_s9['Sev Trend'] = e3_sev_trend.latest_diagonal.to_frame()\n", + "e3_s9['Trended Ult Sev'] = e3_sev_ult_trended.latest_diagonal.to_frame()\n", + "e3_s9[['Sev Trend']] = e3_s9[['Sev Trend']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e3_s9[['Trended Ult Sev']] = (e3_s9[['Trended Ult Sev']] + 1e-9).round(0)\n", + "nb_display(e3_s9.iloc[:-2,:])\n", + "print('Average Trended Severity at 2008 Cost Level')\n", + "for k,v in e3_sevs.items():\n", + " print(f\"\\t{k:<20} \\t{v.round(0)}\")\n", + "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:.0f}\")\n", + "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:.0f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0d86dcac-eb94-4810-910d-8207388ffff6", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit III Sheet 10" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "e4d1cc6d-027b-4ad3-8f66-8f519f0f8f01", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s10 = e3_s4[['Payroll']].copy()[-2:]\n", + "e3_s10['Selected Frequency'] = [e3_freq_2007,e3_freq_2008]\n", + "e3_s10['Ult CC'] = e3_s10['Payroll'] * e3_s10['Selected Frequency']\n", + "e3_s10['Selected Severity'] = [e3_sev_2007,e3_sev_2008]\n", + "e3_s10['Ult Claims'] = e3_s10['Ult CC'] * e3_s10['Selected Severity']\n", + "e3_s10['Reported Claims'] = e3_tri['Reported Claims'].latest_diagonal.to_frame()\n", + "e3_s10['Paid Claims'] = e3_tri['Paid Claims'].latest_diagonal.to_frame()\n", + "e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']\n", + "e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']\n", + "e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']\n", + "e3_s10.T" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "20f78f9f-0bc0-4d12-9c6b-659fcccb0c37", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit III Sheet 1\n", + "assert np.all(e3_ccc_selected.cdf_.round(3).values[...,:7] == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010]))\n", + "#Exhibit III Sheet 2\n", + "assert np.all(e3_rcc_selected.cdf_.round(3).values == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000]))\n", + "#Exhibit III Sheet 3\n", + "assert np.allclose(\n", + " e3_s3['Selected Ult CC'].values[...,1:],\n", + " np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),\n", + " atol=1\n", + ")\n", + "#Exhibit III Sheet 4\n", + "assert np.all(e3_s4['Trended Ultimate Freq'].values[...,1:] == np.array(['0.53%', '0.53%', '0.54%', '0.43%', '0.35%', '0.36%', '0.36%']))\n", + "#Exhibit III Sheet 6\n", + "assert np.allclose(\n", + " e3_psev_selected.ldf_.round(3).values,\n", + " np.array([1.447, 1.249, 1.108, 1.066, 1.038, 1.037, 1.021, 1.150]),\n", + " atol=.001\n", + ")\n", + "#Exhibit III Sheet 7\n", + "assert np.all(e3_rsev_selected.cdf_.round(3).values == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025]))\n", + "#Exhibit III Sheet 8\n", + "assert np.allclose(\n", + " e3_s8['Selected Ult Sev'].values[:-2],\n", + " np.array([4371, 4587, 4963, 5242, 5635, 6169]),\n", + " atol=1\n", + ")\n", + "#Exhibit III Sheet 9\n", + "assert np.allclose(\n", + " e3_s9['Trended Ult Sev'].values[:-2],\n", + " np.array([7251, 7079, 7125, 7000, 7001, 7129]),\n", + " rtol=0.001\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "fb822f26-db0f-4035-aa8c-4511509cb6af", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit IV Analysis\n", + "\n", + "In this Exhibit, there are no full-size triangles. This gives us an opportunity to demontrate utilizing `columns` in `Triangle`, as if we were in Pandas. " + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "85c7cc4a-a1ae-48d4-a707-f613ab3087b0", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "xyz_tort_data = pd.DataFrame({\n", + " \"EffDate\": [\"2006-01-01\", \"2007-01-01\"],\n", + " \"RateChange\": [(0.67/0.75) - 1, 0.75-1]\n", + "})\n", + "xyz_tort_adjustment = cl.ParallelogramOLF(\n", + " rate_history=xyz_tort_data,\n", + " change_col=\"RateChange\",\n", + " date_col=\"EffDate\",\n", + " vertical_line=True\n", + ")\n", + "#Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given\n", + "olf = cl.Triangle(\n", + " data = {\n", + " 'origin':[1998,2002,2003,2004,2005,2006,2007,2008],\n", + " 'valuation':[2008,2008,2008,2008,2008,2008,2008,2008],\n", + " 'On-Level Adjustment':[0,.914,.87,.81,.704,.64,.8,1],\n", + " },\n", + " origin='origin',\n", + " development='valuation',\n", + " columns='On-Level Adjustment'\n", + ")\n", + "e4_freq_assumptions = {}\n", + "e4_freq_assumptions['all_years'] = {}\n", + "e4_freq_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", + "e4_freq_assumptions['latest_2'] = {'n_periods':2}\n", + "e4_sev_assumptions = {}\n", + "e4_sev_assumptions['latest_5_years'] = {'n_periods':5}\n", + "e4_sev_assumptions['latest_5_years_excl_hilo'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "e4_sev_assumptions['latest_3'] = {'n_periods':3}\n", + "\n", + "# In Exhibit II, the selected ultimate was actually the average of reported and closed ultimates.\n", + "e4_s1_tri = e2_rcc_cl.ultimate_.copy()\n", + "e4_s1_tri = e4_s1_tri.rename(axis='columns',value='Ult CC')\n", + "e4_s1_tri['CC Trend'] = cl.Trend(-.015,dates=('2008-12-31','2002-01-01')).fit(e4_s1_tri).trend_\n", + "e4_s1_tri['Trended Ult CC'] = e4_s1_tri['CC Trend'] * e4_s1_tri['Ult CC']\n", + "e4_s1_tri['Earned Premium'] = e2_tri['Earned Premium'].latest_diagonal\n", + "e4_s1_tri['On-Level Adjustment'] = olf['On-Level Adjustment']\n", + "e4_s1_tri['On-Level Premium'] = e4_s1_tri['On-Level Adjustment'] * e4_s1_tri['Earned Premium']\n", + "e4_s1_tri['Trended Ult Freq'] = e4_s1_tri['Trended Ult CC'] / e4_s1_tri['On-Level Premium']\n", + "e4_s1_tri_2006 = e4_s1_tri.iloc[:,:,4:9,:]\n", + "#calculate average frequencies\n", + "e4_freq = average_sev(e4_s1_tri_2006['Trended Ult Freq'],e4_freq_assumptions)\n", + "#calculate selected frequencies\n", + "e4_freq_2008 = e4_freq['latest_2'].round(4)\n", + "e4_freq_2007 = e4_freq_2008 / e4_s1_tri['CC Trend'].values[0,0,-2,0] * e4_s1_tri['On-Level Adjustment'].values[0,0,-2,0]\n", + "e4_freq_2007 = e4_freq_2007.round(4)\n", + "\n", + "e4_s2_tri = e2_rsev_cl.ultimate_.copy()\n", + "e4_s2_tri = e4_s2_tri.rename(axis='columns',value='Ult Sev')\n", + "e4_s2_tri['Sev Trend'] = cl.Trend(.05,dates=('2008-12-31','1998-01-01')).fit(e4_s2_tri).trend_.round(3)\n", + "#from Chapter 8 Exhibit III sheet 1\n", + "e4_s2_tri['Tort Reform Factors'] = xyz_tort_adjustment.fit(e4_s2_tri['Ult Sev']).olf_\n", + "e4_s2_tri['Trended Ult Sev'] = e4_s2_tri['Ult Sev'] * e4_s2_tri['Sev Trend'] * e4_s2_tri['Tort Reform Factors']\n", + "e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= '2006']\n", + "#calculate average severities\n", + "e4_sev = average_sev(e4_s2_tri_2006['Trended Ult Sev'],e4_sev_assumptions)\n", + "#calculate selected severities\n", + "e4_sev_2008 = e4_sev['latest_5_years_excl_hilo'].round(0)\n", + "e4_sev_2007 = e4_sev_2008 / e4_s2_tri['Sev Trend'].values[0,0,-2,0] / e4_s2_tri['Tort Reform Factors'].values[0,0,-2,0]\n", + "e4_sev_2007 = e4_sev_2007.round(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "cca2c635-f303-4d44-9dd3-de27a63faa4b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit IV Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d2bf3b32-f70b-489c-a4ea-130eca08c6ec", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e4_s1 = e4_s1_tri.to_frame().T\n", + "e4_s1[['CC Trend','On-Level Adjustment']] = e4_s1[['CC Trend','On-Level Adjustment']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e4_s1[['Trended Ult CC','On-Level Premium']] = (e4_s1[['Trended Ult CC','On-Level Premium']] + 1e-9).round(0)\n", + "e4_s1['Trended Ult Freq'] = e4_s1[\"Trended Ult Freq\"].map(\"{:.2%}\".format)\n", + "nb_display(e4_s1.iloc[4:9])\n", + "print('Average Trended Frequency at 2008 Cost Level')\n", + "for k,v in e4_freq.items():\n", + " print(f\"\\t{k:<20} \\t{v:.2%}\")\n", + "print(f\"Selected 2008 Frequency \\t{e4_freq_2008:.2%}\")\n", + "print(f\"Estimated 2007 Frequency \\t{e4_freq_2007:.2%}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "6111c76b-a5be-4a64-ab78-a6bea6a931a9", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit IV Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "2cdf6c3d-7ad5-4d64-ad79-169375ec9daa", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e4_s2 = e4_s2_tri.to_frame().T\n", + "e4_s2[['Sev Trend','Tort Reform Factors']] = e4_s2[['Sev Trend','Tort Reform Factors']].round(3)\n", + "#using floating point offset to achieve standard rounding\n", + "e4_s2[['Trended Ult Sev','Ult Sev']] = (e4_s2[['Trended Ult Sev','Ult Sev']] + 1e-9).round(0)\n", + "nb_display(e4_s2.iloc[:9])\n", + "print('Average Trended Severity at 2008 Cost Level')\n", + "for k,v in e4_sev.items():\n", + " print(f\"\\t{k:<25} \\t{v.round(0)}\")\n", + "print(f\"Selected 2008 Severity \\t\\t\\t{e4_sev_2008}\")\n", + "print(f\"Estimated 2007 Severity \\t\\t{e4_sev_2007}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f01f14d6-2449-46e0-87e3-2215d2fcf7e7", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit IV Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "9101f7e8-27d6-4575-9da8-ceef012553cc", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e4_s3 = e4_s1_tri['Earned Premium'].to_frame(keepdims=True).set_index('origin').iloc[-2:][['Earned Premium']]\n", + "e4_s3['Selected Frequency'] = [e4_freq_2007,e4_freq_2008]\n", + "e4_s3['Ult CC'] = e4_s3['Earned Premium'] * e4_s3['Selected Frequency']\n", + "e4_s3['Selected Severity'] = [e4_sev_2007,e4_sev_2008]\n", + "e4_s3['Ult Claims'] = e4_s3['Ult CC'] * e4_s3['Selected Severity']\n", + "e4_s3['Reported Claims'] = (e2_tri['Reported Claims']*1000).latest_diagonal.to_frame()\n", + "e4_s3['Paid Claims'] = (e2_tri['Paid Claims']*1000).latest_diagonal.to_frame()\n", + "e4_s3['Case Outstanding'] = e4_s3['Reported Claims'] - e4_s3['Paid Claims']\n", + "e4_s3['IBNR'] = e4_s3['Ult Claims'] - e4_s3['Reported Claims']\n", + "e4_s3['Unpaid'] = e4_s3['IBNR'] + e4_s3['Case Outstanding']\n", + "e4_s3.T" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "2ab9589d-8c3a-4023-977a-2fd18373b46a", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit IV Sheet 1\n", + "assert np.all(e4_s1.iloc[4:9]['Trended Ult Freq'].values == np.array(['2.54%', '2.51%', '2.65%', '2.36%', '2.37%']))\n", + "#Exhibit IV Sheet 2\n", + "assert np.allclose(\n", + " list(e4_sev.values()),\n", + " np.array([26669., 26720., 27254.]),\n", + " rtol=0.001\n", + ")\n", + "#Exhibit IV Sheet 3\n", + "assert np.allclose(\n", + " e4_s3.iloc[:,4].values,\n", + " np.array([30512152., 30140260.]),\n", + " rtol=0.001\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5b49ffa0-f939-4d71-917d-1ab1ee88c000", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c5398a7e-ddf9-4f84-be70-b57d1e6cde23", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "e5_tri = cl.load_sample('friedland_gl_insurer')\n", + "e5_cnt_assumptions = {}\n", + "e5_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e5_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e5_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e5_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", + "e5_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e5_disp_assumptions = {}\n", + "e5_disp_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", + "e5_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e5_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e5_trend_assumptions = {}\n", + "e5_trend_assumptions['all_years'] = {}\n", + "e5_trend_assumptions['latest_6'] = {'n_periods':6}\n", + "e5_trend_assumptions['latest_4'] = {'n_periods':4}\n", + "e5_sev_assumptions = {}\n", + "e5_sev_assumptions['simple_5'] = {'n_periods':5}\n", + "e5_sev_assumptions['simple_3'] = {'n_periods':3}\n", + "e5_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "\n", + "#developing closed claim counts\n", + "e5_ccc_devs = average_dev(e5_tri['Closed Claim Counts'],e5_cnt_assumptions)\n", + "e5_ccc_selected = cl.TailConstant(tail = 1.100, projection_period = 0).fit_transform(e5_ccc_devs['volume_3'])\n", + "e5_ccc_selected.ldf_ = e5_ccc_selected.ldf_.round(3)\n", + "\n", + "#developing reported claim counts\n", + "e5_rcc_devs = average_dev(e5_tri['Reported Claim Counts'],e5_cnt_assumptions)\n", + "e5_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e5_rcc_devs['volume_3'])\n", + "e5_rcc_selected.ldf_ = e5_rcc_selected.ldf_.round(3)\n", + "\n", + "#combining closed and reported claim counts\n", + "e5_ccc_cl = cl.Chainladder().fit(e5_ccc_selected)\n", + "e5_rcc_cl = cl.Chainladder().fit(e5_rcc_selected)\n", + "e5_cc_ult = ((e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_)/2)\n", + "\n", + "#calculating disposal rate and complete the closed count triangle\n", + "e5_drs = average_dr(e5_tri['Closed Claim Counts'],e5_cc_ult,e5_disp_assumptions)\n", + "e5_dr_selected = e5_drs['medial_5x1']\n", + "e5_dr_selected.disposal_rate_ = e5_dr_selected.disposal_rate_.round(3)\n", + "e5_dr_tri = e5_dr_selected.transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult)\n", + "\n", + "#calculate and select incremental paid severity\n", + "e5_ipsev = e5_tri[\"Paid Claims\"].cum_to_incr() / e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + "e5_regs = regs(e5_ipsev,e5_trend_assumptions)\n", + "e5_trend,e5_rsq = reg_outputs(e5_regs,e5_ipsev.development)\n", + "e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin>'2001'],{'all_years_ex_2001':{}})\n", + "e5_trend_ex2001,e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001,e5_ipsev.development)\n", + "e5_sevs = average_sev(e5_ipsev.trend(0.05),e5_sev_assumptions)\n", + "e5_sevs_sel = e5_sevs['simple_3'].copy()\n", + "e5_sevs_sel.origin = ['2008']\n", + "\n", + "#calculate and select tail paid severity\n", + "e5_incr_ccc = e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + "e5_incr_paid = e5_tri[\"Paid Claims\"].cum_to_incr()\n", + "e5_incr_paid_trended = e5_incr_paid.trend(0.05)\n", + "\n", + "#complete the incremental paid severity triangle\n", + "e5_ipsev_full = e5_ipsev.copy()\n", + "#extending the severity triangle to 108 months\n", + "e5_ipsev_full = cl.concat((e5_ipsev_full,e5_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", + "#setting the before 60 months to selected incremental\n", + "e5_ipsev_full.iloc[:,:,:,:5] = e5_sevs_sel.iloc[:,:,:,:5]\n", + "#setting the after 72 months to selected tail\n", + "e5_ipsev_full.iloc[:,:,:,5:] = (\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", + " /\n", + " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum() \n", + ")\n", + "#setting the valuation_date to the future\n", + "e5_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", + "#trending back from latest accident year\n", + "e5_ipsev_full_detrended = e5_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01')\n", + "#compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle\n", + "e5_ipsev_full_complete = e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date] + e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]\n", + "\n", + "#complete the incremental paid triangle\n", + "e5_s11_ccc = e5_dr_tri.full_triangle_.cum_to_incr()\n", + "e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "905e7cd2-2188-4bb0-a656-d0cec6558dd8", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "8a85abd3-ff5f-4ac7-bc5e-82bf9214ecce", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e5_tri['Closed Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e5_tri['Closed Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "print('Selected')\n", + "nb_display(e5_ccc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e5_ccc_selected.cdf_.round(3))\n", + "print('Percent Closed')\n", + "nb_display((1/e5_ccc_selected.cdf_).round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f105d40b-944b-4c23-b2aa-b335748d5855", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "04a8e098-1264-40c4-bb0a-47a928a3a396", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('PART 1 - Data Triangle')\n", + "nb_display(e5_tri['Reported Claim Counts'])\n", + "print('PART 2 - Age-to-Age Factors')\n", + "nb_display(e5_tri['Reported Claim Counts'].age_to_age.round(3))\n", + "print('PART 3 - Average Age-to-Age Factor')\n", + "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame())\n", + "print('PART 4 - Selected Age-to-Age Factors')\n", + "nb_display(e5_rcc_selected.ldf_)\n", + "print('CDF to Ultimate')\n", + "nb_display(e5_rcc_selected.cdf_.round(3))\n", + "print('Percent Reported')\n", + "nb_display((1/e5_rcc_selected.cdf_).round(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "6a8888f3-14e0-4fcf-ad12-2531b7e9e244", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f83ec473-543a-45c5-8b53-22ac543d4877", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e5_ccc_df = cl.model_diagnostics(e5_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e5_rcc_df = cl.model_diagnostics(e5_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", + "e5_s3 = e5_ccc_df[['development','Latest','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','Ultimate':'Ult Count Using CCC'})\n", + "e5_s3[['Reported Claim Counts','Ult Count Using RCC']] = e5_rcc_df[['Latest','Ultimate']]\n", + "e5_s3[\"Selected Ult CC\"] = e5_cc_ult.round(0).latest_diagonal.to_frame()\n", + "e5_s3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "7f896e42-0389-4483-878e-2b6e4ede5d4f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 4" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "39ffb70b-472b-4bff-b428-0d3a2238908b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Part 1 - Disposal Rate Triangle')\n", + "nb_display(cl.DisposalRate().fit_transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult).disposal_rate_tri.round(3))\n", + "print('PART 2 - Average Disposal Rate Factors')\n", + "nb_display(combine_disposal(e5_drs).round(3).to_frame())\n", + "print('PART 3 - Selected Disposal Rate Factors')\n", + "nb_display(e5_dr_selected.disposal_rate_)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c8d9acc3-9ee7-4390-b017-5e54a8a2d15f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "bd6b314e-832d-4b19-aa73-003c91bd567f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Closed Claim Counts')\n", + "nb_display(e5_tri['Closed Claim Counts'])\n", + "print('Projected Incremental Closed Claim Counts')\n", + "nb_display(e5_dr_tri.full_triangle_.cum_to_incr().round(0))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5431abfc-5da3-4fe1-9d75-1d33c25e237b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 6" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "babf6982-d9e2-4840-9888-d43181813d79", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Paid Claims')\n", + "nb_display(e5_tri[\"Paid Claims\"])\n", + "print('Incremental Paid CLaims')\n", + "nb_display(e5_tri[\"Paid Claims\"].cum_to_incr())\n", + "print('Incremental Closed Claim Counts')\n", + "nb_display(e5_tri[\"Closed Claim Counts\"].cum_to_incr())\n", + "print('Incremental Paid Severities')\n", + "nb_display(e5_ipsev)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c783393d-2550-4d5b-8163-0d84f31204cf", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 7" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c1f6f621-0582-48ee-807b-8eceb4e6309a", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Paid Severities')\n", + "nb_display(e5_ipsev)\n", + "print('Annual Change based on Exponential Regression')\n", + "nb_display(pd.concat([e5_trend.round(3),e5_trend_ex2001.round(3)]))\n", + "print('Goodness of Fit Test of Exponential Regression (R-Squared)')\n", + "nb_display(pd.concat([e5_rsq.round(3),e5_rsq_ex2001.round(3)]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "1aa1d6f7-c11f-4cc5-aa18-6787929851f2", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 8" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "8a8b31e9-22af-4bb8-9f95-17e15c836ff6", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Paid Severities')\n", + "nb_display(e5_ipsev)\n", + "print('Trended Incremental Paid Severities Assuming 5% Annual Trend')\n", + "nb_display(e5_ipsev.trend(0.05))\n", + "print('Average Trended Incremental Paid Severities')\n", + "nb_display(combine_tri(e5_sevs).round(0).to_frame())\n", + "print('Selected Incremental Paid Severities')\n", + "nb_display(e5_sevs_sel[e5_sevs_sel.development<=60])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d1042988-724d-44ea-8d13-9df69dd70f04", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 9" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "cd014e1e-b5f1-41c8-96be-52fabeb53f5d", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e5_s9 = pd.DataFrame(\n", + " data = [[\n", + " e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(), \n", + " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()\n", + " ]],\n", + " index = ['Total Closed Claim Counts'],\n", + " columns = ['Age 60 & Older','Age 72 & Older']\n", + ")\n", + "e5_s9.loc['Total Trended Paid Claims'] = [\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(), \n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", + " ]\n", + "e5_s9.loc['Estimated Trended Tail Severity'] = e5_s9.iloc[1] / e5_s9.iloc[0]\n", + "e5_s9.loc['Estimated Incremental Trended Tail Severity'] = e5_sevs_sel.iloc[:,:,:,4:6].values.flatten()\n", + "e5_s9.loc['Selected Trended Paid Severity'] = e5_ipsev_full.values[0,0,-1,4:6]\n", + "print('Incremental Closed Claim Counts')\n", + "nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= '2004'])\n", + "print('Incremental Paid CLaims')\n", + "nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= '2004'])\n", + "print('Trended Incremental Paid Severities')\n", + "nb_display(e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][e5_incr_paid_trended.origin <= '2004'])\n", + "nb_display(e5_s9)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "7473207a-ed4d-441d-869d-9043a338bae2", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 10" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "5724e17c-ed8e-40b8-8b78-d47af30118e0", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Paid Severities')\n", + "nb_display(e5_ipsev)\n", + "print('Selected Incremental Paid Severities')\n", + "nb_display(e5_ipsev_full[e5_ipsev_full.origin == '2008'])\n", + "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "nb_display(e5_ipsev_full_complete)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "0b84f3d7-71f8-4a5f-8113-186020db4f0f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 11" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "e5789b39-1bb9-4dac-b5bf-5f646cf49352", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Projected Incremental Closed Claim Counts')\n", + "nb_display(e5_s11_ccc.round(0))\n", + "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "nb_display(e5_ipsev_full_complete)\n", + "print('Projected Incremental Paid Claims')\n", + "nb_display(e5_s11_ip)\n", + "print('Projected Cumulative Paid Claims')\n", + "nb_display(e5_s11_ip.incr_to_cum())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "9fb3d9d7-1038-4615-a512-c088f2ac3f34", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit V Sheet 12" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f1d08176-4f1b-46f7-bdff-1b565b5cdee6", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "summary_exh(\n", + " e5_tri['Reported Claims']/1000,\n", + " e5_tri['Paid Claims']/1000,\n", + " e5_s11_ip.incr_to_cum()/1000\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "9742a88e-12f9-423f-96d8-4a24264bc5b0", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e5_dr_selected.disposal_rate_" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "2a46eef8-f279-4172-b5e1-a23f079ad80e", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit V Sheet 1\n", + "assert np.all(e5_ccc_selected.cdf_.round(3).values == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100]))\n", + "#Exhibit V Sheet 2\n", + "assert np.all(e5_rcc_selected.cdf_.round(3).values == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000]))\n", + "#Exhibit V Sheet 3\n", + "assert np.all(e5_s3['Selected Ult CC'].values==np.array([873., 720., 626., 629., 588., 553, 438., 609.]))\n", + "#Exhibit V Sheet 4\n", + "assert np.all(e5_dr_selected.disposal_rate_.values == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000]))\n", + "#Exhibit V Sheet 5\n", + "lhs = (e5_dr_tri.full_triangle_.cum_to_incr()-e5_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", + "rhs = np.array([\n", + " 77., \n", + " 24., 70., \n", + " 12., 18., 54., \n", + " 46., 13., 19., 57., \n", + " 52., 45., 13., 19., 56., \n", + " 76., 49., 43., 12., 18., 54., \n", + " 67., 55., 36., 31., 9., 13., 39., \n", + "140., 91., 75., 49., 43., 12., 18., 53.\n", + "])\n", + "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", + "#Exhibit V Sheet 7\n", + "assert np.all(e5_trend.loc['latest_4'].values[:7].round(3) == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331]))\n", + "assert np.all(e5_rsq.loc['latest_6'].values[:7].round(3) == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000]))\n", + "#Exhibit V Sheet 8\n", + "assert np.all(e5_sevs_sel.values[...,:5].round(0) == np.array([11259., 32980., 65523., 80544., 140802.]))\n", + "#Exhibit V Sheet 9\n", + "assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160., 175816.]))\n", + "#Exhibit V Sheet 10\n", + "lhs = e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date].round(0).values.flatten()\n", + "rhs = np.array([\n", + " 124949.,\n", + " 131196.,131196.,\n", + " 137756.,137756.,137756.,\n", + " 144644.,144644.,144644.,144644.,\n", + " 121630.,151876.,151876.,151876.,151876.,\n", + " 73056.,\t127711.,159470.,159470.,159470.,159470.,\n", + " 62403.,\t76709.,\t134097.,167443.,167443.,167443.,167443.,\n", + "32980., 65523.,\t80544.,\t140802.,175816.,175816.,175816.,175816.,\n", + "])\n", + "assert np.all(lhs[~np.isnan(lhs)] == rhs)\n", + "#Exhibit V Sheet 11\n", + "assert np.allclose(\n", + " e5_s11_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", + " np.array([39497433., 44743308., 35510981., 37766027., 42007442., 41113459., 32859080., 47109641.]),\n", + " rtol = .001\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "9c887dd3-d12f-4f0d-b9a1-75dbe3f4b79f", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "## Exhibit VI Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "205c41a2-9f0f-4de1-b184-17a50daa2b30", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#loading data and assumptions\n", + "e6_tri = e2_tri[e2_tri.origin >= '2001'][e2_tri.development <= 96]\n", + "e6_disp_assumptions = {}\n", + "e6_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", + "e6_disp_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", + "e6_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e6_sev_assumptions = {}\n", + "e6_sev_assumptions['simple_3'] = {'n_periods':3}\n", + "e6_sev_assumptions['simple_2'] = {'n_periods':2}\n", + "e6_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "\n", + "#calculating disposal rate and complete the closed count triangle\n", + "e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= '2001']\n", + "e6_drs = average_dr(e6_tri['Closed Claim Counts'],e6_rcc_ult,e6_disp_assumptions)\n", + "e6_dr_selected = e6_drs['simple_2']\n", + "e6_dr_selected.disposal_rate_ = e6_dr_selected.disposal_rate_.round(3)\n", + "e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)\n", + "\n", + "#calculate and select incremental paid severity\n", + "e6_ipsev = e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", + "e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", + "e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)\n", + "e6_sevs_sel = e6_sevs['simple_2'].copy()\n", + "e6_sevs_sel.origin = ['2008']\n", + "\n", + "#calculate and select tail paid severity\n", + "e6_incr_ccc = e6_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + "e6_incr_paid = e6_tri[\"Paid Claims\"].cum_to_incr() * 1000\n", + "e6_incr_paid_trended = e6_ipsev_adj * e6_incr_ccc\n", + "\n", + "e6_ipsev_full = e6_ipsev_adj.copy()\n", + "#extending the severity triangle to 108 months\n", + "e6_ipsev_full = cl.concat((e6_ipsev_full,e6_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", + "#setting the before 72 months to selected incremental\n", + "e6_ipsev_full.iloc[:,:,:,:6] = e6_sevs_sel.iloc[:,:,:,:6]\n", + "#setting the after 86 months to selected tail\n", + "e6_ipsev_full.iloc[:,:,:,6:] = (\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", + " /\n", + " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum() \n", + ")\n", + "#setting the valuation_date to the future\n", + "e6_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", + "#trending back from latest accident year\n", + "e6_ipsev_full_detrended = e6_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01') / xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", + "#compositing the actual incremental severities with dtrended selected\n", + "e6_ipsev_full_complete = e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date] + e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date]\n", + "\n", + "#complete the incremental paid triangle\n", + "e6_s7_ccc = e6_dr_tri.full_triangle_.cum_to_incr()\n", + "e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "8c663ba3-5416-4bc7-a4bd-c6b6fd456f52", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 1" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "a35f5d52-f6da-410b-8066-2326407fe0be", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Part 1 - Disposal Rate Triangle')\n", + "nb_display(cl.DisposalRate().fit_transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult).disposal_rate_tri.round(3))\n", + "\n", + "print('PART 2 - Average Disposal Rate Factors')\n", + "nb_display(combine_disposal(e6_drs).round(3).to_frame())\n", + "\n", + "print('PART 3 - Selected Disposal Rate Factors')\n", + "nb_display(e6_dr_selected.disposal_rate_)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "3cea4559-9ad7-4d0d-80c9-4275bc3f6749", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 2" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "7e65b816-1c9f-4aa3-be14-4ecfe68e3643", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Closed Claim Counts')\n", + "nb_display(e6_tri['Closed Claim Counts'])\n", + "print('Projected Incremental Closed Claim Counts')\n", + "nb_display(e6_dr_tri.full_triangle_.cum_to_incr().round(0))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d82ad2af-f1b4-4723-8f59-d8c8c8e18651", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 3" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "644ce450-62db-4c07-8f8a-b8a40f0057e8", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Paid Claims')\n", + "nb_display(e6_tri[\"Paid Claims\"])\n", + "print('Incremental Paid CLaims')\n", + "nb_display(e6_tri[\"Paid Claims\"].cum_to_incr())\n", + "print('Incremental Closed Claim Counts')\n", + "nb_display(e6_tri[\"Closed Claim Counts\"].cum_to_incr())\n", + "print('Incremental Paid Severities')\n", + "nb_display(e6_ipsev)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "c513183d-95a5-48b6-a956-5d9b5361b1f2", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 4" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "77cd2e1a-89bd-4f73-8944-614b98453ac7", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Paid Severities')\n", + "nb_display(e6_ipsev)\n", + "print('Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform')\n", + "nb_display(e6_ipsev_adj)\n", + "print('Average Trended Incremental Paid Severities')\n", + "nb_display(combine_tri(e6_sevs).round(0).to_frame())\n", + "print('Selected Incremental Paid Severities')\n", + "nb_display(e6_sevs_sel[e6_sevs_sel.development<=72])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "a58fc306-7907-45c7-ae2b-9a6ad2b4e5cc", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 5" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "f3366d2a-55dd-44db-b92e-0337841fe391", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Closed Claim Counts')\n", + "nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= '2003'])\n", + "print('Incremental Paid CLaims')\n", + "nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= '2003'])\n", + "print('Trended Incremental Paid Severities')\n", + "nb_display(e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][e6_incr_paid_trended.origin <= '2003'])\n", + "e6_s5 = pd.DataFrame(\n", + " data = [[\n", + " e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(), \n", + " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()\n", + " ]],\n", + " index = ['Total Closed Claim Counts'],\n", + " columns = ['Age 72 & Older','Age 84 & Older']\n", + ")\n", + "e6_s5.loc['Total Trended Paid Claims'] = [\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(), \n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", + " ]\n", + "e6_s5.loc['Estimated Trended Tail Severity'] = e6_s5.iloc[1] / e6_s5.iloc[0]\n", + "e6_s5.loc['Estimated Incremental Trended Tail Severity'] = e6_sevs_sel.iloc[:,:,:,5:7].values.flatten()\n", + "e6_s5.loc['Selected Trended Paid Severity'] = e6_ipsev_full.values[0,0,-1,5:7]\n", + "nb_display(e6_s5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "cec56e19-6976-43e9-862d-b866a49fbd84", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 6" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "266fee5b-ccab-4936-a652-a352177cc829", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Incremental Paid Severities')\n", + "nb_display(e6_ipsev)\n", + "print('Selected Incremental Paid Severities')\n", + "nb_display(e6_ipsev_full[e6_ipsev_full.origin == '2008'])\n", + "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "nb_display(e6_ipsev_full_complete)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "b9a1663c-421e-4f69-bedc-b59dd431dda1", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 7" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "a96a03cd-6c86-4a61-9118-4d3744c1a709", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "print('Projected Incremental Closed Claim Counts')\n", + "nb_display(e6_s7_ccc.round(0))\n", + "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "nb_display(e6_ipsev_full_complete)\n", + "print('Projected Incremental Paid Claims')\n", + "nb_display(e6_s7_ip)\n", + "print('Projected Cumulative Paid Claims')\n", + "nb_display(e6_s7_ip.incr_to_cum())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "d60ebfc1-1517-4d32-85e1-fd79fda6785e", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "##Exhibit VI Sheet 8" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "27234f18-968a-4e23-8b29-19698845193d", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "summary_exh(\n", + " e6_tri['Reported Claims'],\n", + " e6_tri['Paid Claims'],\n", + " e6_s7_ip.incr_to_cum()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "e03f91fe-ff00-47ee-9994-3caf56a38ab1", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "#Exhibit VI Sheet 1\n", + "assert np.all(e6_dr_selected.disposal_rate_.values == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000]))\n", + "#Exhibit VI Sheet 2\n", + "lhs = (e6_dr_tri.full_triangle_.cum_to_incr()-e6_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", + "rhs = np.array([\n", + " 9., \n", + " 21., 10., \n", + " 39., 16., 8., \n", + " 109., 78., 31., 16., \n", + " 235., 103., 74., 29., 15., \n", + " 177., 155., 68., 48., 19., 10., \n", + " 160., 133., 116., 51., 36., 15., 7., \n", + "389., 156., 130., 114., 50., 36., 14., 7.\n", + "])\n", + "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", + "#Exhibit VI Sheet 4\n", + "assert np.all(e6_sevs_sel.values[...,:6].round(0) == np.array([11807.,15165.,26043.,35183.,41908.,62206.]))\n", + "#Exhibit VI Sheet 5\n", + "assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.,70432.]))\n", + "#Exhibit VI Sheet 6\n", + "lhs = e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date].values.flatten()\n", + "rhs = np.array([\n", + " 74709., \n", + " 78444., 78444., \n", + " 82367., 82367., 82367., \n", + " 76384., 86485., 86485., 86485., \n", + " 54032., 80203., 90809., 90809., 90809., \n", + " 42549., 50682., 75230., 85179., 85179., 85179., \n", + " 24803., 33508., 39912., 59244., 67079., 67079., 67079., \n", + "15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., \n", + "])\n", + "assert np.allclose(lhs[~np.isnan(lhs)], rhs, atol=1)\n", + "#Exhibit VI Sheet 7\n", + "assert np.allclose(\n", + " e6_s7_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", + " np.array([39192.,46869.,44479.,71906.,71684.,49913.,31805.,29828.]),\n", + " rtol = .001\n", + ")\n" + ] + } + ], + "metadata": { + "application/vnd.databricks.v1+notebook": { + "computePreferences": { + "hardware": { + "accelerator": null, + "gpuPoolId": null, + "memory": null + }, + "software": null + }, + "dashboards": [], + "environmentMetadata": { + "base_environment": "", + "environment_version": "5" + }, + "inputWidgetPreferences": null, + "language": "python", + "notebookMetadata": { + "mostRecentlyExecutedCommandWithImplicitDF": { + "commandId": 8042421325130824, + "dataframes": [ + "_sqldf" + ] + }, + "pythonIndentUnit": 4 + }, + "notebookName": "Chapter 11", + "widgets": {} + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From b7be9568020d2581df49ed674b2ab0cba0675434 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 29 Jun 2026 18:41:31 -0700 Subject: [PATCH 03/27] Update _toc.yml --- docs/_toc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_toc.yml b/docs/_toc.yml index 87f3af4d0..05681b7f5 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -35,6 +35,7 @@ parts: sections: - file: friedland/chapter_6.rst - file: friedland/chapter_7.rst + - file: friedland/chapter_11.ipynb - chapters: - file: gallery/index.md - chapters: From 52a53b05ee80857c52e626f7a677f05c6340ade4 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:32:49 -0700 Subject: [PATCH 04/27] adding narrative --- docs/friedland/chapter_11.ipynb | 230 ++++++++++++-------------------- 1 file changed, 88 insertions(+), 142 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 871b5d122..bf82fb9e3 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -16,26 +16,11 @@ } }, "source": [ - "#Chapter 11 - Frequency-Severity Techniques" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "ec44a0a9-6db8-40e4-908d-df63caaec823", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "source": [ - "Recreating Chapter 11 of the Friendland text on reserving from Exam 5" + "# Chapter 11 - Frequency-Severity Techniques\n", + "\n", + "On this page, we will recreating Chapter 11 of Friendland. There is not a dedicated ``FrequencySeverity`` method in the package. Instead, we will chain together existing methods and use ``Triangle`` arithmetic to calculate the utimate. \n", + "\n", + "We will begin by import packages and setting up some helper functions." ] }, { @@ -48,7 +33,7 @@ "rowLimit": 10000 }, "inputWidgets": {}, - "nuid": "fb4d15b5-f3b3-4fc2-90d2-7399e1cf534e", + "nuid": "ff40a83c-496e-4345-8d03-5dbc368f6ba0", "showTitle": false, "tableResultSettingsMap": {}, "title": "" @@ -59,28 +44,7 @@ "import numpy as np\n", "import pandas as pd\n", "import chainladder as cl\n", - "from IPython.display import display as nb_display" - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "ff40a83c-496e-4345-8d03-5dbc368f6ba0", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "#helper functions for exhibits\n", + "from IPython.display import display as nb_display\n", "\n", "#create reported/paid summary exhibit\n", "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", @@ -163,9 +127,11 @@ } }, "source": [ - "##Exhibit I Analysis\n", + "## Exhibit I Analysis\n", + "\n", + "Displaying all the triangles and dataframes take up quite a bit of code. We will distill the core analysis code at the top of each exhibit. \n", "\n", - "Displaying all the triangles and dataframes take up quite a bit of code. We will distill the core analysis code at the top of each exhibit" + "Exhibit I follows Approach 1. Count and Severity are developed separately and multiplied together. " ] }, { @@ -220,9 +186,9 @@ "e1_rsev_devs = average_dev(e1_rsev,e1_sev_assumptions)\n", "e1_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rsev_devs['medial_5x1'])\n", "e1_rsev_selected.ldf_ = e1_rsev_selected.ldf_.round(3)\n", + "e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)\n", "\n", "#combining developed count and severity\n", - "e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)\n", "e1_ult = e1_rsev_cl.ultimate_ * e1_cc_ult / 1000" ] }, @@ -242,7 +208,7 @@ } }, "source": [ - "##Exhibit I Sheet 1" + "## Exhibit I Sheet 1" ] }, { @@ -294,7 +260,7 @@ } }, "source": [ - "##Exhibit I Sheet 2" + "## Exhibit I Sheet 2" ] }, { @@ -346,7 +312,7 @@ } }, "source": [ - "##Exhibit I Sheet 3" + "## Exhibit I Sheet 3" ] }, { @@ -394,7 +360,7 @@ } }, "source": [ - "##Exhibit I Sheet 4" + "## Exhibit I Sheet 4" ] }, { @@ -434,7 +400,7 @@ } }, "source": [ - "##Exhibit I Sheet 5" + "## Exhibit I Sheet 5" ] }, { @@ -475,7 +441,7 @@ } }, "source": [ - "##Exhibit I Sheet 6" + "## Exhibit I Sheet 6" ] }, { @@ -524,7 +490,7 @@ } }, "source": [ - "##Exhibit I Sheet 7" + "## Exhibit I Sheet 7" ] }, { @@ -571,7 +537,7 @@ } }, "source": [ - "##Exhibit I Sheet 8" + "## Exhibit I Sheet 8" ] }, { @@ -595,6 +561,22 @@ "summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)" ] }, + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "2caf8e32-c2a4-4552-bd8d-03f2790fcf26", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "At the end of each exhibit, we reconcile to hardcoded figures from Friedland using a series of ``assert`` statemenets. When any of these statements errors out, we know some bug has been introduced into the package. " + ] + }, { "cell_type": "code", "execution_count": 0, @@ -649,7 +631,9 @@ } }, "source": [ - "##Exhibit II Analysis" + "## Exhibit II Analysis\n", + "\n", + "Exhibit II also follows Approach 1, and is largely similar to Exhibit I. " ] }, { @@ -703,9 +687,9 @@ "e2_rsev_devs = average_dev(e2_rsev,e2_sev_assumptions)\n", "e2_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rsev_devs['simple_2'])\n", "e2_rsev_selected.ldf_ = e2_rsev_selected.ldf_.round(3)\n", + "e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)\n", "\n", "#combining developed count and severity\n", - "e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)\n", "e2_ult = e2_rsev_cl.ultimate_ * e2_cc_ult / 1000" ] }, @@ -726,7 +710,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 1" + "## Exhibit II Sheet 1" ] }, { @@ -779,7 +763,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 2" + "## Exhibit II Sheet 2" ] }, { @@ -832,7 +816,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 3" + "## Exhibit II Sheet 3" ] }, { @@ -881,7 +865,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 4" + "## Exhibit II Sheet 4" ] }, { @@ -923,7 +907,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 5" + "## Exhibit II Sheet 5" ] }, { @@ -973,7 +957,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 6" + "## Exhibit II Sheet 6" ] }, { @@ -1021,7 +1005,7 @@ }, "source": [ " \n", - "##Exhibit II Sheet 7" + "## Exhibit II Sheet 7" ] }, { @@ -1099,7 +1083,9 @@ } }, "source": [ - "##Exhibit III Analysis" + "## Exhibit III Analysis\n", + "\n", + "Exhibit III follows Approach 2, which adds explicit selections for the most recent frequency and severity. To calculate various averages for frequency and severity, we use the ``TriangleWeight`` utility class, and supply ``n_periods`` and ``drop_high`` as we would with ``Development``. We also leverage the ``Trend`` adjustment method. " ] }, { @@ -1187,48 +1173,6 @@ "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0,0,-2,0]\n" ] }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "14152443-201f-4a5e-be50-5515548bea24", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "e3_rsev.development" - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "7082b089-f92a-489f-9285-2b2b808464ee", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "e3_sev_2008" - ] - }, { "cell_type": "markdown", "metadata": { @@ -1245,7 +1189,7 @@ } }, "source": [ - "##Exhibit III Sheet 1" + "## Exhibit III Sheet 1" ] }, { @@ -1297,7 +1241,7 @@ } }, "source": [ - "##Exhibit III Sheet 2" + "## Exhibit III Sheet 2" ] }, { @@ -1348,7 +1292,7 @@ } }, "source": [ - "##Exhibit III Sheet 3" + "## Exhibit III Sheet 3" ] }, { @@ -1396,7 +1340,7 @@ } }, "source": [ - "##Exhibit III Sheet 4" + "## Exhibit III Sheet 4" ] }, { @@ -1449,7 +1393,7 @@ } }, "source": [ - "##Exhibit III Sheet 5" + "## Exhibit III Sheet 5" ] }, { @@ -1496,7 +1440,7 @@ } }, "source": [ - "##Exhibit III Sheet 6" + "## Exhibit III Sheet 6" ] }, { @@ -1546,7 +1490,7 @@ } }, "source": [ - "##Exhibit III Sheet 7" + "## Exhibit III Sheet 7" ] }, { @@ -1596,7 +1540,7 @@ } }, "source": [ - "##Exhibit III Sheet 8" + "## Exhibit III Sheet 8" ] }, { @@ -1644,7 +1588,7 @@ } }, "source": [ - "##Exhibit III Sheet 9" + "## Exhibit III Sheet 9" ] }, { @@ -1695,7 +1639,7 @@ } }, "source": [ - "##Exhibit III Sheet 10" + "## Exhibit III Sheet 10" ] }, { @@ -1797,9 +1741,9 @@ } }, "source": [ - "##Exhibit IV Analysis\n", + "## Exhibit IV Analysis\n", "\n", - "In this Exhibit, there are no full-size triangles. This gives us an opportunity to demontrate utilizing `columns` in `Triangle`, as if we were in Pandas. " + "Exhibit IV continues to follow Approach 2. We demonstrate two separate ways in the package to achievement the on-leveling and law-change adjustments. Also, there are no full-size triangles in this exhibit. This gives us an opportunity to demontrate utilizing `columns` in `Triangle`, as if we were in Pandas." ] }, { @@ -1899,7 +1843,7 @@ } }, "source": [ - "##Exhibit IV Sheet 1" + "## Exhibit IV Sheet 1" ] }, { @@ -1949,7 +1893,7 @@ } }, "source": [ - "##Exhibit IV Sheet 2" + "## Exhibit IV Sheet 2" ] }, { @@ -1998,7 +1942,7 @@ } }, "source": [ - "##Exhibit IV Sheet 3" + "## Exhibit IV Sheet 3" ] }, { @@ -2082,7 +2026,9 @@ } }, "source": [ - "##Exhibit V Analysis" + "## Exhibit V Analysis\n", + "\n", + "Exhibit V follows Approach 3, which uses full incremental closed count and paid triangles to estimate the ultimate. Full incremental closed count triangle is estimated after first estimating ultimate count using reported count. We use the ``DisposalRate`` adjustment method to perform this calculation. Full incremental paid severity triangle is estimated through trending. " ] }, { @@ -2200,7 +2146,7 @@ } }, "source": [ - "##Exhibit V Sheet 1" + "## Exhibit V Sheet 1" ] }, { @@ -2252,7 +2198,7 @@ } }, "source": [ - "##Exhibit V Sheet 2" + "## Exhibit V Sheet 2" ] }, { @@ -2303,7 +2249,7 @@ } }, "source": [ - "##Exhibit V Sheet 3" + "## Exhibit V Sheet 3" ] }, { @@ -2348,7 +2294,7 @@ } }, "source": [ - "##Exhibit V Sheet 4" + "## Exhibit V Sheet 4" ] }, { @@ -2393,7 +2339,7 @@ } }, "source": [ - "##Exhibit V Sheet 5" + "## Exhibit V Sheet 5" ] }, { @@ -2436,7 +2382,7 @@ } }, "source": [ - "##Exhibit V Sheet 6" + "## Exhibit V Sheet 6" ] }, { @@ -2483,7 +2429,7 @@ } }, "source": [ - "##Exhibit V Sheet 7" + "## Exhibit V Sheet 7" ] }, { @@ -2528,7 +2474,7 @@ } }, "source": [ - "##Exhibit V Sheet 8" + "## Exhibit V Sheet 8" ] }, { @@ -2575,7 +2521,7 @@ } }, "source": [ - "##Exhibit V Sheet 9" + "## Exhibit V Sheet 9" ] }, { @@ -2636,7 +2582,7 @@ } }, "source": [ - "##Exhibit V Sheet 10" + "## Exhibit V Sheet 10" ] }, { @@ -2681,7 +2627,7 @@ } }, "source": [ - "##Exhibit V Sheet 11" + "## Exhibit V Sheet 11" ] }, { @@ -2728,7 +2674,7 @@ } }, "source": [ - "##Exhibit V Sheet 12" + "## Exhibit V Sheet 12" ] }, { @@ -2950,7 +2896,7 @@ } }, "source": [ - "##Exhibit VI Sheet 1" + "## Exhibit VI Sheet 1" ] }, { @@ -2997,7 +2943,7 @@ } }, "source": [ - "##Exhibit VI Sheet 2" + "## Exhibit VI Sheet 2" ] }, { @@ -3040,7 +2986,7 @@ } }, "source": [ - "##Exhibit VI Sheet 3" + "## Exhibit VI Sheet 3" ] }, { @@ -3087,7 +3033,7 @@ } }, "source": [ - "##Exhibit VI Sheet 4" + "## Exhibit VI Sheet 4" ] }, { @@ -3134,7 +3080,7 @@ } }, "source": [ - "##Exhibit VI Sheet 5" + "## Exhibit VI Sheet 5" ] }, { @@ -3195,7 +3141,7 @@ } }, "source": [ - "##Exhibit VI Sheet 6" + "## Exhibit VI Sheet 6" ] }, { @@ -3240,7 +3186,7 @@ } }, "source": [ - "##Exhibit VI Sheet 7" + "## Exhibit VI Sheet 7" ] }, { @@ -3287,7 +3233,7 @@ } }, "source": [ - "##Exhibit VI Sheet 8" + "## Exhibit VI Sheet 8" ] }, { From 5fc60fdcc1d077b5a68e8b11ea85fa4010ae63a5 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:04:24 -0700 Subject: [PATCH 05/27] Update triangle.py --- chainladder/core/triangle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chainladder/core/triangle.py b/chainladder/core/triangle.py index bd5e710e3..cd94e6892 100644 --- a/chainladder/core/triangle.py +++ b/chainladder/core/triangle.py @@ -109,7 +109,7 @@ class Triangle(TriangleBase): link_ratio, age_to_age: Triangle Displays age-to-age ratios for the triangle. disposal_rate_tri: Triangle - Displays actual disposal rates by origin and development; must have ultimate_ + Displays actual disposal rates by origin and development; must have ``ultimate_`` valuation_date : date The latest valuation date of the data loc: Triangle From a108767a044e23de102f5c33fea771841327da28 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Fri, 31 Jul 2026 20:24:49 -0700 Subject: [PATCH 06/27] Update triangle_weight.py --- chainladder/utils/triangle_weight.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/chainladder/utils/triangle_weight.py b/chainladder/utils/triangle_weight.py index 7399c7418..9073e87f8 100644 --- a/chainladder/utils/triangle_weight.py +++ b/chainladder/utils/triangle_weight.py @@ -117,7 +117,13 @@ def fit(self, X: TriangleProtocol, y: None = None, sample_weight: None = None): self : object Returns the instance itself. """ - self.w_ = self._set_weight_func(X=X,secondary_rank=sample_weight) + # bulk of the logic was refactored from `DevelopmentBase`, which only had numpy support + # using _set_fit_group to explicitly guard against fitting sparse triangles + # forcing numpy here to avoid major refactoring + # in practie, there is no realistic use case for a sparse triangle to need weights + backend = "numpy" if X.array_backend in ["sparse", "numpy"] else "cupy" + obj = X.set_backend(backend) + self.w_ = self._set_weight_func(X=obj,secondary_rank=sample_weight) return self def transform(self, X: TriangleProtocol) -> Triangle: @@ -514,4 +520,4 @@ def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: ) warnings.warn(warning) - return w.astype(float) \ No newline at end of file + return w.astype(float) From dc2919e454cfe869fabaeeeaa60452499eb1700d Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Fri, 31 Jul 2026 20:47:34 -0700 Subject: [PATCH 07/27] Update triangle_weight.py --- chainladder/utils/triangle_weight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chainladder/utils/triangle_weight.py b/chainladder/utils/triangle_weight.py index 9073e87f8..d32618409 100644 --- a/chainladder/utils/triangle_weight.py +++ b/chainladder/utils/triangle_weight.py @@ -253,7 +253,7 @@ def _assign_n_periods_weight_int(X, n_periods): #adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) if X.is_full: w = X.copy() - w.values[:,:,:z,:] = np.nan + w.values[:,:,:-n_periods,:] = np.nan else: val_date_min = X.valuation[X.valuation <= X.valuation_date] val_date_min = val_date_min.drop_duplicates().sort_values() From 6508949b235b401c740e739b507bf0a3e0c7ee0e Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:39:22 +0000 Subject: [PATCH 08/27] reverting to data given by the text --- chainladder/utils/data/_manifest.py | 2 +- .../utils/data/friedland_xyz_auto_bi.csv | 134 +++++++++--------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/chainladder/utils/data/_manifest.py b/chainladder/utils/data/_manifest.py index aa47dc060..b215bbb74 100644 --- a/chainladder/utils/data/_manifest.py +++ b/chainladder/utils/data/_manifest.py @@ -282,7 +282,7 @@ "origin": "Accident Year", "development": "Calendar Year", "index": None, - "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium"], + "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium","Incremental Paid Severity"], "cumulative": True, }, "genins": { diff --git a/chainladder/utils/data/friedland_xyz_auto_bi.csv b/chainladder/utils/data/friedland_xyz_auto_bi.csv index a98639033..1ff6366f2 100644 --- a/chainladder/utils/data/friedland_xyz_auto_bi.csv +++ b/chainladder/utils/data/friedland_xyz_auto_bi.csv @@ -1,67 +1,67 @@ -Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium -1998,1998,,,,,,,20000 -1998,1999,,,,,,,20000 -1998,2000,6309,11171,,,4861,,20000 -1998,2001,8521,12380,510,634,3859,19526,20000 -1998,2002,10082,13216,547,635,3134,20813,20000 -1998,2003,11620,14067,575,635,2447,22152,20000 -1998,2004,13242,14688,598,637,1446,23058,20000 -1998,2005,14419,16366,612,637,1947,25692,20000 -1998,2006,15311,16163,620,637,853,25374,20000 -1998,2007,15764,15835,635,637,71,24859,20000 -1998,2008,15822,15822,637,637,0,24839,20000 -1999,1999,,,,,,,31500 -1999,2000,4666,13255,,,8589,,31500 -1999,2001,9861,16405,686,1026,6544,15989,31500 -1999,2002,13971,19639,819,1039,5668,18902,31500 -1999,2003,18127,22473,910,1047,4347,21464,31500 -1999,2004,22032,23764,980,1050,1732,22632,31500 -1999,2005,23511,25094,1007,1053,1583,23831,31500 -1999,2006,24146,24795,1036,1047,649,23682,31500 -1999,2007,24592,25071,1039,1047,479,23946,31500 -1999,2008,24817,25107,1044,1047,290,23980,31500 -2000,2000,1302,15676,,,14374,,45000 -2000,2001,6513,18749,650,1354,12237,13847,45000 -2000,2002,12139,21900,932,1397,9760,15676,45000 -2000,2003,17828,27144,1095,1411,9316,19237,45000 -2000,2004,24030,29488,1216,1410,5458,20914,45000 -2000,2005,28853,34458,1292,1408,5605,24473,45000 -2000,2006,33222,36949,1367,1408,3727,26242,45000 -2000,2007,35902,37505,1391,1408,1603,26637,45000 -2000,2008,36782,37246,1402,1408,465,26453,45000 -2001,2001,1539.456,11827,304,1305,10288,9063,50000 -2001,2002,5952.241,16004,681,1421,10052,11262,50000 -2001,2003,12319.336,21022,936,1449,8703,14508,50000 -2001,2004,18608.788,26578,1092,1458,7969,18229,50000 -2001,2005,24386.973,34205,1225,1458,9818,23460,50000 -2001,2006,31090.065,37136,1357,1455,6046,25523,50000 -2001,2007,37069.815,38541,1432,1455,1471,26489,50000 -2001,2008,38519.529,38798,1446,1455,278,26665,50000 -2002,2002,2317.651,12811,203,1342,10494,9546,61183 -2002,2003,7931.635,20370,607,1514,12439,13455,61183 -2002,2004,13822.585,26656,841,1548,12833,17219,61183 -2002,2005,22095.617,37667,1089,1557,15572,24192,61183 -2002,2006,31945.485,44414,1327,1549,12469,28673,61183 -2002,2007,40628.819,48701,1464,1552,8072,31379,61183 -2002,2008,44437.623,48169,1523,1554,3731,30997,61183 -2003,2003,1743.211,9651,181,1373,7908,7029,69175 -2003,2004,6240.349,16995,614,1616,10755,10517,69175 -2003,2005,12683.23,30354,941,1630,17671,18622,69175 -2003,2006,22892.562,40594,1263,1626,17702,24966,69175 -2003,2007,34505.01,44231,1507,1629,9726,27152,69175 -2003,2008,39320.472,44373,1568,1629,5052,27239,69175 -2004,2004,2221.22,16995,235,1932,14774,8796,99322 -2004,2005,9898.432,40180,848,2168,30281,18533,99322 -2004,2006,25950.094,58866,1442,2234,32916,26350,99322 -2004,2007,43439.464,71707,1852,2249,28268,31884,99322 -2004,2008,52811.083,70288,2029,2258,17477,31129,99322 -2005,2005,3042.925,28674,295,2067,25631,13872,138151 -2005,2006,12218.989,47432,1119,2293,35213,20686,138151 -2005,2007,27072.964,70340,1664,2367,43268,29717,138151 -2005,2008,40026.352,70655,1946,2390,30629,29563,138151 -2006,2006,3531.114,27066,307,1473,23535,18375,107578 -2006,2007,11778.146,46783,906,1645,35005,28440,107578 -2006,2008,22819.111,48804,1201,1657,25985,29453,107578 -2007,2007,3528.854,19477,329,1192,15948,16340,62438 -2007,2008,11864.72,31732,791,1264,19867,25104,62438 -2008,2008,3408.876,18632,276,1036,15223,17985,47797 +Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium,Incremental Paid Severity +1998,1998,,,,,,,20000, +1998,1999,,,,,,,20000, +1998,2000,6309,11171,,,4861,,20000, +1998,2001,8521,12380,510,634,3859,19526,20000, +1998,2002,10082,13216,547,635,3134,20813,20000, +1998,2003,11620,14067,575,635,2447,22152,20000, +1998,2004,13242,14688,598,637,1446,23058,20000, +1998,2005,14419,16366,612,637,1947,25692,20000, +1998,2006,15311,16163,620,637,853,25374,20000, +1998,2007,15764,15835,635,637,71,24859,20000, +1998,2008,15822,15822,637,637,0,24839,20000, +1999,1999,,,,,,,31500, +1999,2000,4666,13255,,,8589,,31500, +1999,2001,9861,16405,686,1026,6544,15989,31500, +1999,2002,13971,19639,819,1039,5668,18902,31500, +1999,2003,18127,22473,910,1047,4347,21464,31500, +1999,2004,22032,23764,980,1050,1732,22632,31500, +1999,2005,23511,25094,1007,1053,1583,23831,31500, +1999,2006,24146,24795,1036,1047,649,23682,31500, +1999,2007,24592,25071,1039,1047,479,23946,31500, +1999,2008,24817,25107,1044,1047,290,23980,31500, +2000,2000,1302,15676,,,14374,,45000, +2000,2001,6513,18749,650,1354,12237,13847,45000, +2000,2002,12139,21900,932,1397,9760,15676,45000, +2000,2003,17828,27144,1095,1411,9316,19237,45000, +2000,2004,24030,29488,1216,1410,5458,20914,45000, +2000,2005,28853,34458,1292,1408,5605,24473,45000, +2000,2006,33222,36949,1367,1408,3727,26242,45000, +2000,2007,35902,37505,1391,1408,1603,26637,45000, +2000,2008,36782,37246,1402,1408,465,26453,45000, +2001,2001,1539,11827,304,1305,10288,9063,50000,5064 +2001,2002,5952,16004,681,1421,10052,11262,50000,11705 +2001,2003,12319,21022,936,1449,8703,14508,50000,24969 +2001,2004,18609,26578,1092,1458,7969,18229,50000,40317 +2001,2005,24387,34205,1225,1458,9818,23460,50000,43445 +2001,2006,31090,37136,1357,1455,6046,25523,50000,50781 +2001,2007,37070,38541,1432,1455,1471,26489,50000,79730 +2001,2008,38519,38798,1446,1455,278,26665,50000,103551 +2002,2002,2318,12811,203,1342,10494,9546,61183,11417 +2002,2003,7932,20370,607,1514,12439,13455,61183,13896 +2002,2004,13822,26656,841,1548,12833,17219,61183,25175 +2002,2005,22095,37667,1089,1557,15572,24192,61183,33359 +2002,2006,31945,44414,1327,1549,12469,28673,61183,41386 +2002,2007,40629,48701,1464,1552,8072,31379,61183,63382 +2002,2008,44437,48169,1523,1554,3731,30997,61183,64556 +2003,2003,1743,9651,181,1373,7908,7029,69175,9631 +2003,2004,6240,16995,614,1616,10755,10517,69175,10386 +2003,2005,12683,30354,941,1630,17671,18622,69175,19703 +2003,2006,22892,40594,1263,1626,17702,24966,69175,31706 +2003,2007,34505,44231,1507,1629,9726,27152,69175,47592 +2003,2008,39320,44373,1568,1629,5052,27239,69175,78942 +2004,2004,2221,16995,235,1932,14774,8796,99322,9452 +2004,2005,9898,40180,848,2168,30281,18533,99322,12524 +2004,2006,25950,58866,1442,2234,32916,26350,99322,27023 +2004,2007,43439,71707,1852,2249,28268,31884,99322,42657 +2004,2008,52811,70288,2029,2258,17477,31129,99322,52947 +2005,2005,3043,28674,295,2067,25631,13872,138151,10315 +2005,2006,12219,47432,1119,2293,35213,20686,138151,11136 +2005,2007,27073,70340,1664,2367,43268,29717,138151,27255 +2005,2008,40026,70655,1946,2390,30629,29563,138151,45934 +2006,2006,3531,27066,307,1473,23535,18375,107578,11502 +2006,2007,11778,46783,906,1645,35005,28440,107578,13768 +2006,2008,22819,48804,1201,1657,25985,29453,107578,37427 +2007,2007,3529,19477,329,1192,15948,16340,62438,10726 +2007,2008,11865,31732,791,1264,19867,25104,62438,18043 +2008,2008,3409,18632,276,1036,15223,17985,47797,12351 From 0b9b92607bb39eab7226e2d0de3e962c0aff103b Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:41:56 +0000 Subject: [PATCH 09/27] updating exhibit --- docs/friedland/chapter_11.ipynb | 504 +++++++++++++++----------------- 1 file changed, 243 insertions(+), 261 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index bf82fb9e3..d63e79ea8 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -4,10 +4,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "24730109-b724-4a80-817b-c11900453da4", "showTitle": false, @@ -46,17 +43,37 @@ "import chainladder as cl\n", "from IPython.display import display as nb_display\n", "\n", + "def format_exh(\n", + " exh_df:pd.DataFrame,\n", + " value_cols: list[str] = [],\n", + " factor_cols: list[str] = [],\n", + " other_formats: dict = {}\n", + "):\n", + " return exh_df \\\n", + " .reset_index() \\\n", + " .rename(columns={'origin':'Accident Year'}) \\\n", + " .style \\\n", + " .hide(axis='index') \\\n", + " .set_properties(**{\"text-align\":\"right\"}) \\\n", + " .format({\n", + " 'Accident Year':\"{:%Y}\",\n", + " **{x:\"{:,.0f}\" for x in value_cols},\n", + " **{x:\"{:,.3f}\" for x in factor_cols},\n", + " **other_formats\n", + " }) \n", + "\n", "#create reported/paid summary exhibit\n", "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", " output = reported_tri.latest_diagonal.to_frame()\n", " output.columns = ['Reported']\n", " output['Paid'] = paid_tri.latest_diagonal.to_frame()\n", " #using floating point offset to achieve standard rounding\n", - " output['Ult Claims'] = (ult.latest_diagonal + 1e-9).round(0).to_frame()\n", + " output['Ult Claims'] = ult.latest_diagonal.to_frame()\n", " output['Case Outstanding'] = output[\"Reported\"] - output[\"Paid\"]\n", " output['IBNR'] = output[\"Ult Claims\"] - output[\"Reported\"]\n", " output['Unpaid'] = output[\"Ult Claims\"] - output[\"Paid\"]\n", - " return output.round(0)\n", + " output.index.name = 'origin'\n", + " return output\n", "\n", "#create a dict of developed triangles for each of the selection assumptions on a given page\n", "def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", @@ -115,10 +132,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "1f3f99fd-cb60-4901-807f-71273d213005", "showTitle": false, @@ -196,10 +210,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "65209fce-b487-4443-92bc-17da6ca31835", "showTitle": false, @@ -248,10 +259,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d91cc78f-5868-4711-a5a0-d49a9b331ec9", "showTitle": false, @@ -300,10 +308,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "10316a34-7ae5-41f6-b266-70fdda592cd6", "showTitle": false, @@ -335,23 +340,37 @@ "source": [ "e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]\n", "e1_s3[\"Selected Ult CC\"] = e1_cc_ult.latest_diagonal.to_frame()\n", - "e1_s3[['Closed CDF','Reported CDF']] = e1_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e1_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + "format_exh(\n", + " e1_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ") " ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "a8b310a4-5622-4d1a-8217-7fac37ac21c4", "showTitle": false, @@ -388,10 +407,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b876aa8e-280f-4e37-9b2b-0d83ebaf86e1", "showTitle": false, @@ -429,10 +445,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "090cd8df-b99a-438a-b727-80d208b93a6d", "showTitle": false, @@ -478,10 +491,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "16ae37e3-32c6-44c0-9d8f-6a9295b0d113", "showTitle": false, @@ -512,23 +522,32 @@ "outputs": [], "source": [ "e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Reported Severity',\n", + " 'Ultimate':'Ult Severity'\n", + " }\n", + ")\n", "e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']\n", "e1_s7[\"Ult Claims\"] = e1_ult.latest_diagonal.to_frame()\n", - "e1_s7['CDF'] = e1_s7['CDF'].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e1_s7[['Reported Severity','Ult Severity','Ult Claims']] = (e1_s7[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", - "e1_s7" + "format_exh(\n", + " e1_s7,\n", + " [\n", + " 'Reported Severity',\n", + " 'Ult Severity',\n", + " 'Selected Ult CC',\n", + " 'Ult Claims',\n", + " ],\n", + " ['CDF']\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3135d7a1-01f8-46aa-bf62-3cd6e0953696", "showTitle": false, @@ -558,7 +577,8 @@ }, "outputs": [], "source": [ - "summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)" + "e1_s8 = summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)\n", + "format_exh(e1_s8,e1_s8.columns)" ] }, { @@ -619,10 +639,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "dd3aa676-954a-470b-b812-3f9c1492ed66", "showTitle": false, @@ -697,10 +714,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c2e97f2e-16f7-4b58-ab54-ac46fb34f31c", "showTitle": true, @@ -750,10 +764,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5a9022d4-50c6-4f97-bfa3-1ce4d122a2a1", "showTitle": true, @@ -803,10 +814,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "ff41fe54-f723-4bfa-a166-73ff106e63d1", "showTitle": true, @@ -839,23 +847,37 @@ "source": [ "e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]\n", "e2_s3[\"Selected Ult CC\"] = e2_cc_ult.latest_diagonal.to_frame()\n", - "e2_s3[['Closed CDF','Reported CDF']] = e2_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e2_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + "format_exh(\n", + " e2_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ") " ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0f7e73e8-737b-4909-87a6-c68d0f621dbf", "showTitle": true, @@ -894,10 +916,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "be51ed2d-bf1e-4ca4-8b6a-cb891adda7ff", "showTitle": true, @@ -944,10 +963,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d1006396-3dfd-46b2-a0a4-96461ba2d5f2", "showTitle": true, @@ -979,23 +995,34 @@ "outputs": [], "source": [ "e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Reported Severity',\n", + " 'Ultimate':'Ult Severity'\n", + " }\n", + ")\n", "e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']\n", "e2_s6[\"Ult Claims\"] = e2_ult.latest_diagonal.to_frame()\n", - "e2_s6['CDF'] = e2_s6['CDF'].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e2_s6[['Reported Severity','Ult Severity','Ult Claims']] = (e2_s6[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", - "e2_s6" + "format_exh(\n", + " e2_s6,\n", + " [\n", + " 'Reported Severity',\n", + " 'Ult Severity',\n", + " 'Selected Ult CC',\n", + " 'Ult Claims',\n", + " ],\n", + " [\n", + " 'CDF',\n", + " ]\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "fb3533d3-36e4-4971-8f9a-49e76ec75d43", "showTitle": true, @@ -1026,7 +1053,8 @@ }, "outputs": [], "source": [ - "summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)" + "e2_s7 = summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)\n", + "format_exh(e2_s7,e2_s7.columns)" ] }, { @@ -1071,10 +1099,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "e9c5436c-4e87-448f-bc10-72e08932caf9", "showTitle": false, @@ -1177,10 +1202,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b8b09c7a-0457-4e83-8be5-30b2082ee425", "showTitle": false, @@ -1229,10 +1251,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "80da84e0-c7d8-4dd9-930c-6956ac1bcb3b", "showTitle": false, @@ -1280,10 +1299,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "efa10e00-52c8-4fd8-87ac-acd7e3358960", "showTitle": false, @@ -1315,23 +1331,37 @@ "source": [ "e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]\n", "e3_s3[\"Selected Ult CC\"] = e3_cc_ult.latest_diagonal.to_frame()\n", - "e3_s3[['Closed CDF','Reported CDF']] = e3_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e3_s3" + "format_exh(\n", + " e3_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7a7c251e-b567-464c-bc82-597c2836ccdb", "showTitle": false, @@ -1368,11 +1398,20 @@ "e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()\n", "e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']\n", "e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']\n", - "e3_s4[['CC Trend','Payroll Trend']] = e3_s4[['CC Trend','Payroll Trend']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s4[['Trended Ult CC','Trended Payroll']] = (e3_s4[['Trended Ult CC','Trended Payroll']] + 1e-9).round(0)\n", - "e3_s4['Trended Ultimate Freq'] = e3_s4[\"Trended Ultimate Freq\"].map(\"{:.2%}\".format)\n", - "nb_display(e3_s4)\n", + "nb_display(format_exh(\n", + " e3_s4,\n", + " [\n", + " 'Selected Ult CC',\n", + " 'Trended Ult CC',\n", + " 'Payroll',\n", + " 'Trended Payroll',\n", + " ],\n", + " [\n", + " 'CC Trend',\n", + " 'Payroll Trend',\n", + " ],\n", + " {'Trended Ultimate Freq' : \"{:.2%}\"}\n", + "))\n", "print(f\"Selected Frequency at 2008 level {e3_freq_2008:.2%}\")\n", "print(f\"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}\")" ] @@ -1381,10 +1420,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f3e4e3fe-5297-4079-bb12-103c43e28820", "showTitle": false, @@ -1428,10 +1464,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d28ce8b5-8add-43d5-9609-6dac464d77ac", "showTitle": false, @@ -1478,10 +1511,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f9f3d551-9208-4773-8059-8ee160821d09", "showTitle": false, @@ -1528,10 +1558,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3dda605a-2dd1-472a-bd42-ef193c95c1b1", "showTitle": false, @@ -1563,23 +1590,27 @@ "source": [ "e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Paid Sev','CDF':'Paid CDF','Ultimate':'Ult Paid Sev'})\n", + "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Paid Sev',\n", + " 'CDF':'Paid CDF',\n", + " 'Ultimate':'Ult Paid Sev'\n", + " }\n", + ")\n", "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", "e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)\n", "#using floating point offset to achieve standard rounding\n", "e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)\n", - "e3_s8.iloc[:-2,:][['development','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" + "e3_s8.iloc[:-2,:][['Age','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "781252e1-a9d8-4a65-b502-ba78541d132e", "showTitle": false, @@ -1627,10 +1658,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0d86dcac-eb94-4810-910d-8207388ffff6", "showTitle": false, @@ -1673,6 +1701,27 @@ "e3_s10.T" ] }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s4['Trended Ultimate Freq']" + ] + }, { "cell_type": "code", "execution_count": 0, @@ -1699,10 +1748,14 @@ "assert np.allclose(\n", " e3_s3['Selected Ult CC'].values[...,1:],\n", " np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),\n", - " atol=1\n", + " rtol=0.001\n", ")\n", "#Exhibit III Sheet 4\n", - "assert np.all(e3_s4['Trended Ultimate Freq'].values[...,1:] == np.array(['0.53%', '0.53%', '0.54%', '0.43%', '0.35%', '0.36%', '0.36%']))\n", + "assert np.allclose(\n", + " e3_s4['Trended Ultimate Freq'].values[...,1:],\n", + " np.array([0.0053, 0.0053, 0.0054, 0.0043, 0.0035, 0.0036, 0.0036]),\n", + " atol = 0.0001\n", + " )\n", "#Exhibit III Sheet 6\n", "assert np.allclose(\n", " e3_psev_selected.ldf_.round(3).values,\n", @@ -1729,10 +1782,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "fb822f26-db0f-4035-aa8c-4511509cb6af", "showTitle": false, @@ -1831,10 +1881,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "cca2c635-f303-4d44-9dd3-de27a63faa4b", "showTitle": false, @@ -1881,10 +1928,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "6111c76b-a5be-4a64-ab78-a6bea6a931a9", "showTitle": false, @@ -1930,10 +1974,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f01f14d6-2449-46e0-87e3-2215d2fcf7e7", "showTitle": false, @@ -2014,10 +2055,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5b49ffa0-f939-4d71-917d-1ab1ee88c000", "showTitle": false, @@ -2134,10 +2172,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "905e7cd2-2188-4bb0-a656-d0cec6558dd8", "showTitle": false, @@ -2186,10 +2221,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f105d40b-944b-4c23-b2aa-b335748d5855", "showTitle": false, @@ -2237,10 +2269,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "6a8888f3-14e0-4fcf-ad12-2531b7e9e244", "showTitle": false, @@ -2282,10 +2311,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7f896e42-0389-4483-878e-2b6e4ede5d4f", "showTitle": false, @@ -2327,10 +2353,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c8d9acc3-9ee7-4390-b017-5e54a8a2d15f", "showTitle": false, @@ -2370,10 +2393,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5431abfc-5da3-4fe1-9d75-1d33c25e237b", "showTitle": false, @@ -2417,10 +2437,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c783393d-2550-4d5b-8163-0d84f31204cf", "showTitle": false, @@ -2462,10 +2479,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "1aa1d6f7-c11f-4cc5-aa18-6787929851f2", "showTitle": false, @@ -2509,10 +2523,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d1042988-724d-44ea-8d13-9df69dd70f04", "showTitle": false, @@ -2570,10 +2581,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7473207a-ed4d-441d-869d-9043a338bae2", "showTitle": false, @@ -2615,10 +2623,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0b84f3d7-71f8-4a5f-8113-186020db4f0f", "showTitle": false, @@ -2662,10 +2667,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "9fb3d9d7-1038-4615-a512-c088f2ac3f34", "showTitle": false, @@ -2794,10 +2796,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "9c887dd3-d12f-4f0d-b9a1-75dbe3f4b79f", "showTitle": false, @@ -2846,7 +2845,11 @@ "e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)\n", "\n", "#calculate and select incremental paid severity\n", - "e6_ipsev = e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", + "#in a real world scenario, it's possible to calculate incremental paid severity like this\n", + "# e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", + "#however, the cumulative paid data in Friedland is only given in thousands\n", + "#therefore, we will directly use the incremental paid severity data as given\n", + "e6_ipsev = e6_tri['Incremental Paid Severity']\n", "e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", "e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)\n", "e6_sevs_sel = e6_sevs['simple_2'].copy()\n", @@ -2884,10 +2887,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "8c663ba3-5416-4bc7-a4bd-c6b6fd456f52", "showTitle": false, @@ -2931,10 +2931,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3cea4559-9ad7-4d0d-80c9-4275bc3f6749", "showTitle": false, @@ -2974,10 +2971,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d82ad2af-f1b4-4723-8f59-d8c8c8e18651", "showTitle": false, @@ -3021,10 +3015,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c513183d-95a5-48b6-a956-5d9b5361b1f2", "showTitle": false, @@ -3068,10 +3059,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "a58fc306-7907-45c7-ae2b-9a6ad2b4e5cc", "showTitle": false, @@ -3129,10 +3117,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "cec56e19-6976-43e9-862d-b866a49fbd84", "showTitle": false, @@ -3174,10 +3159,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b9a1663c-421e-4f69-bedc-b59dd431dda1", "showTitle": false, @@ -3221,10 +3203,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d60ebfc1-1517-4d32-85e1-fd79fda6785e", "showTitle": false, @@ -3333,6 +3312,9 @@ "dashboards": [], "environmentMetadata": { "base_environment": "", + "dependencies": [ + "-e ./chainladder-python" + ], "environment_version": "5" }, "inputWidgetPreferences": null, @@ -3346,7 +3328,7 @@ }, "pythonIndentUnit": 4 }, - "notebookName": "Chapter 11", + "notebookName": "chapter_11", "widgets": {} }, "language_info": { @@ -3355,4 +3337,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 916ae0b461ac4fd154b3a93d87ff6fa54ff1f993 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:55:03 +0000 Subject: [PATCH 10/27] reverting xyz changes --- docs/friedland/chapter_6.rst | 38 ++++++++-------- docs/friedland/chapter_7.rst | 84 ++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/friedland/chapter_6.rst b/docs/friedland/chapter_6.rst index 95a4f2f04..6af1648a9 100644 --- a/docs/friedland/chapter_6.rst +++ b/docs/friedland/chapter_6.rst @@ -87,14 +87,14 @@ Table 3 - Paid Claim Development Triangle .. doctest:: >>> tri['Paid Claims'] - 12 24 36 48 60 72 84 - 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 - 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN - 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN - 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN - 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN - 2007 3528.854 11864.720 NaN NaN NaN NaN NaN - 2008 3408.876 NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 + 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 + 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN + 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN + 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN + 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN + 2007 3529.0 11865.0 NaN NaN NaN NaN NaN + 2008 3409.0 NaN NaN NaN NaN NaN NaN Table 4 - Ratio of Reported Claims to Earned Premium ####################################################### @@ -188,7 +188,7 @@ Table 6 - Ratio of Paid Claims-to-Reported Claims 2002 0.181 0.389 0.519 0.587 0.719 0.834 0.923 2003 0.181 0.367 0.418 0.564 0.780 0.886 NaN 2004 0.131 0.246 0.441 0.606 0.751 NaN NaN - 2005 0.106 0.258 0.385 0.567 NaN NaN NaN + 2005 0.106 0.258 0.385 0.566 NaN NaN NaN 2006 0.130 0.252 0.468 NaN NaN NaN NaN 2007 0.181 0.374 NaN NaN NaN NaN NaN 2008 0.183 NaN NaN NaN NaN NaN NaN @@ -279,10 +279,10 @@ Table 13 – Average Paid Claim Development Triangle >>> (tri["Paid Claims"] / tri["Closed Claim Counts"] * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 11417.0 13067.0 16436.0 20290.0 24073.0 27752.0 29178.0 - 2003 9631.0 10163.0 13478.0 18126.0 22896.0 25077.0 NaN - 2004 9452.0 11673.0 17996.0 23455.0 26028.0 NaN NaN - 2005 10315.0 10920.0 16270.0 20569.0 NaN NaN NaN + 2002 11419.0 13068.0 16435.0 20289.0 24073.0 27752.0 29177.0 + 2003 9630.0 10163.0 13478.0 18125.0 22896.0 25077.0 NaN + 2004 9451.0 11672.0 17996.0 23455.0 26028.0 NaN NaN + 2005 10315.0 10920.0 16270.0 20568.0 NaN NaN NaN 2006 11502.0 13000.0 19000.0 NaN NaN NaN NaN 2007 10726.0 15000.0 NaN NaN NaN NaN NaN 2008 12351.0 NaN NaN NaN NaN NaN NaN @@ -294,10 +294,10 @@ Table 14 – Average Case Outstanding Development Triangle >>> ((tri["Reported Claims"] - tri["Paid Claims"]) / (tri["Reported Claim Counts"] - tri["Closed Claim Counts"]) * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 9213.0 13714.0 18152.0 33272.0 56164.0 91729.0 120367.0 - 2003 6634.0 10733.0 25647.0 48764.0 79721.0 82828.0 NaN - 2004 8706.0 22941.0 41560.0 71203.0 76318.0 NaN NaN - 2005 14464.0 29994.0 61546.0 68983.0 NaN NaN NaN - 2006 20184.0 47368.0 56984.0 NaN NaN NaN NaN - 2007 18480.0 42003.0 NaN NaN NaN NaN NaN + 2002 9212.0 13713.0 18153.0 33274.0 56167.0 91727.0 120387.0 + 2003 6634.0 10734.0 25647.0 48766.0 79721.0 82836.0 NaN + 2004 8706.0 22941.0 41561.0 71204.0 76319.0 NaN NaN + 2005 14464.0 29994.0 61546.0 68984.0 NaN NaN NaN + 2006 20184.0 47368.0 56985.0 NaN NaN NaN NaN + 2007 18480.0 42002.0 NaN NaN NaN NaN NaN 2008 20030.0 NaN NaN NaN NaN NaN NaN diff --git a/docs/friedland/chapter_7.rst b/docs/friedland/chapter_7.rst index d2f1faab2..0bc55bb98 100644 --- a/docs/friedland/chapter_7.rst +++ b/docs/friedland/chapter_7.rst @@ -494,35 +494,35 @@ Exhibit II Sheet 2 p111 >>> paid_devs = dev_exhibit(tri['Paid Claims'],assumptions,'volume_2',1.01) PART 1 - Data Triangle - 12 24 36 48 60 72 84 96 108 120 132 - 1998 NaN NaN 6309.000 8521.000 10082.000 11620.000 13242.000 14419.000 15311.0 15764.0 15822.0 - 1999 NaN 4666.000 9861.000 13971.000 18127.000 22032.000 23511.000 24146.000 24592.0 24817.0 NaN - 2000 1302.000 6513.000 12139.000 17828.000 24030.000 28853.000 33222.000 35902.000 36782.0 NaN NaN - 2001 1539.456 5952.241 12319.336 18608.788 24386.973 31090.065 37069.815 38519.529 NaN NaN NaN - 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 NaN NaN NaN NaN - 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN NaN NaN NaN NaN - 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN NaN NaN NaN NaN - 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN NaN NaN NaN NaN - 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3528.854 11864.720 NaN NaN NaN NaN NaN NaN NaN NaN NaN - 2008 3408.876 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 96 108 120 132 + 1998 NaN NaN 6309.0 8521.0 10082.0 11620.0 13242.0 14419.0 15311.0 15764.0 15822.0 + 1999 NaN 4666.0 9861.0 13971.0 18127.0 22032.0 23511.0 24146.0 24592.0 24817.0 NaN + 2000 1302.0 6513.0 12139.0 17828.0 24030.0 28853.0 33222.0 35902.0 36782.0 NaN NaN + 2001 1539.0 5952.0 12319.0 18609.0 24387.0 31090.0 37070.0 38519.0 NaN NaN NaN + 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 NaN NaN NaN NaN + 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN NaN NaN NaN NaN + 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN NaN NaN NaN NaN + 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN NaN NaN NaN NaN + 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3529.0 11865.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2008 3409.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 2 - Age-to-Age Factors 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 1998 NaN NaN 1.350610 1.183194 1.152549 1.139587 1.088884 1.061863 1.029587 1.003679 1999 NaN 2.113373 1.416793 1.297473 1.215425 1.067130 1.027009 1.018471 1.009149 NaN 2000 5.002304 1.863811 1.468655 1.347880 1.200707 1.151423 1.080669 1.024511 NaN NaN - 2001 3.866457 2.069697 1.510535 1.310508 1.274864 1.192336 1.039108 NaN NaN NaN - 2002 3.422273 1.742716 1.598516 1.445784 1.271817 1.093746 NaN NaN NaN NaN - 2003 3.579801 2.032455 1.804947 1.507259 1.139558 NaN NaN NaN NaN NaN - 2004 4.456304 2.621637 1.673962 1.215740 NaN NaN NaN NaN NaN NaN - 2005 4.015541 2.215647 1.478462 NaN NaN NaN NaN NaN NaN NaN - 2006 3.335533 1.937411 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3.362202 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2001 3.867446 2.069724 1.510593 1.310495 1.274860 1.192345 1.039088 NaN NaN NaN + 2002 3.421915 1.742562 1.598539 1.445802 1.271842 1.093726 NaN NaN NaN NaN + 2003 3.580034 2.032532 1.804936 1.507295 1.139545 NaN NaN NaN NaN NaN + 2004 4.456551 2.621742 1.673950 1.215751 NaN NaN NaN NaN NaN NaN + 2005 4.015445 2.215648 1.478447 NaN NaN NaN NaN NaN NaN NaN + 2006 3.335599 1.937426 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3.362142 NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 3 - Average Age-to-Age Factor 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 simple_5 3.750 2.110 1.613 1.365 1.220 1.129 1.059 1.035 1.019 1.004 simple_3 3.571 2.258 1.652 1.390 1.229 1.146 1.049 1.035 1.019 1.004 - simple_2 3.349 2.077 1.576 1.361 1.206 1.143 1.060 1.021 1.019 1.004 + simple_2 3.349 2.077 1.576 1.362 1.206 1.143 1.060 1.021 1.019 1.004 volume_4 3.713 2.206 1.615 1.342 1.218 1.128 1.056 1.030 1.017 1.004 volume_3 3.550 2.238 1.619 1.349 1.222 1.141 1.051 1.030 1.017 1.004 volume_2 3.349 2.079 1.574 1.316 1.203 1.136 1.059 1.022 1.017 1.004 @@ -543,17 +543,17 @@ Exhibit II Sheet 3 p112 >>> exhibit = rounded_development_summary(reported_devs["Selected"],paid_devs["Selected"]) >>> exhibit Age Reported Claims Paid Claims Reported CDF Paid CDF Reported Ultimate Paid Ultimate - 1998 132 15822.0 15822.000 1.000 1.010 15822.0 15980.0 - 1999 120 25107.0 24817.000 0.999 1.014 25082.0 25164.0 - 2000 108 37246.0 36782.000 0.992 1.031 36948.0 37922.0 - 2001 96 38798.0 38519.529 0.992 1.054 38488.0 40600.0 - 2002 84 48169.0 44437.623 1.003 1.116 48314.0 49592.0 - 2003 72 44373.0 39320.472 1.013 1.268 44950.0 49858.0 - 2004 60 70288.0 52811.083 1.064 1.525 74786.0 80537.0 - 2005 48 70655.0 40026.352 1.085 2.007 76661.0 80333.0 - 2006 36 48804.0 22819.111 1.196 3.160 58370.0 72108.0 - 2007 24 31732.0 11864.720 1.512 6.569 47979.0 77939.0 - 2008 12 18632.0 3408.876 2.551 21.999 47530.0 74992.0 + 1998 132 15822.0 15822.0 1.000 1.010 15822.0 15980.0 + 1999 120 25107.0 24817.0 0.999 1.014 25082.0 25164.0 + 2000 108 37246.0 36782.0 0.992 1.031 36948.0 37922.0 + 2001 96 38798.0 38519.0 0.992 1.054 38488.0 40599.0 + 2002 84 48169.0 44437.0 1.003 1.116 48314.0 49592.0 + 2003 72 44373.0 39320.0 1.013 1.268 44950.0 49858.0 + 2004 60 70288.0 52811.0 1.064 1.525 74786.0 80537.0 + 2005 48 70655.0 40026.0 1.085 2.007 76661.0 80332.0 + 2006 36 48804.0 22819.0 1.196 3.160 58370.0 72108.0 + 2007 24 31732.0 11865.0 1.512 6.569 47979.0 77941.0 + 2008 12 18632.0 3409.0 2.551 21.999 47530.0 74995.0 Exhibit II Sheet 4 p113 ########################## @@ -563,17 +563,17 @@ Exhibit II Sheet 4 p113 >>> unpaid_exhibit = unpaid_summary(exhibit) >>> unpaid_exhibit[['Case Outstanding','Reported Method IBNR','Paid Method IBNR','Reported Method Unpaid','Paid Method Unpaid']] Case Outstanding Reported Method IBNR Paid Method IBNR Reported Method Unpaid Paid Method Unpaid - 1998 0.000 0.0 158.0 0.000 158.000 - 1999 290.000 -25.0 57.0 265.000 347.000 - 2000 464.000 -298.0 676.0 166.000 1140.000 - 2001 278.471 -310.0 1802.0 -31.529 2080.471 - 2002 3731.377 145.0 1423.0 3876.377 5154.377 - 2003 5052.528 577.0 5485.0 5629.528 10537.528 - 2004 17476.917 4498.0 10249.0 21974.917 27725.917 - 2005 30628.648 6006.0 9678.0 36634.648 40306.648 - 2006 25984.889 9566.0 23304.0 35550.889 49288.889 - 2007 19867.280 16247.0 46207.0 36114.280 66074.280 - 2008 15223.124 28898.0 56360.0 44121.124 71583.124 + 1998 0.0 0.0 158.0 0.0 158.0 + 1999 290.0 -25.0 57.0 265.0 347.0 + 2000 464.0 -298.0 676.0 166.0 1140.0 + 2001 279.0 -310.0 1801.0 -31.0 2080.0 + 2002 3732.0 145.0 1423.0 3877.0 5155.0 + 2003 5053.0 577.0 5485.0 5630.0 10538.0 + 2004 17477.0 4498.0 10249.0 21975.0 27726.0 + 2005 30629.0 6006.0 9677.0 36635.0 40306.0 + 2006 25985.0 9566.0 23304.0 35551.0 49289.0 + 2007 19867.0 16247.0 46209.0 36114.0 66076.0 + 2008 15223.0 28898.0 56363.0 44121.0 71586.0 Persisting the Selected Estimators ################################## From 61163d7f7d19719a1ab5ba9731696c6d3e14bd0e Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:39:22 +0000 Subject: [PATCH 11/27] reverting to data given by the text --- chainladder/utils/data/_manifest.py | 2 +- .../utils/data/friedland_xyz_auto_bi.csv | 134 +++++++++--------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/chainladder/utils/data/_manifest.py b/chainladder/utils/data/_manifest.py index aa47dc060..b215bbb74 100644 --- a/chainladder/utils/data/_manifest.py +++ b/chainladder/utils/data/_manifest.py @@ -282,7 +282,7 @@ "origin": "Accident Year", "development": "Calendar Year", "index": None, - "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium"], + "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium","Incremental Paid Severity"], "cumulative": True, }, "genins": { diff --git a/chainladder/utils/data/friedland_xyz_auto_bi.csv b/chainladder/utils/data/friedland_xyz_auto_bi.csv index a98639033..1ff6366f2 100644 --- a/chainladder/utils/data/friedland_xyz_auto_bi.csv +++ b/chainladder/utils/data/friedland_xyz_auto_bi.csv @@ -1,67 +1,67 @@ -Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium -1998,1998,,,,,,,20000 -1998,1999,,,,,,,20000 -1998,2000,6309,11171,,,4861,,20000 -1998,2001,8521,12380,510,634,3859,19526,20000 -1998,2002,10082,13216,547,635,3134,20813,20000 -1998,2003,11620,14067,575,635,2447,22152,20000 -1998,2004,13242,14688,598,637,1446,23058,20000 -1998,2005,14419,16366,612,637,1947,25692,20000 -1998,2006,15311,16163,620,637,853,25374,20000 -1998,2007,15764,15835,635,637,71,24859,20000 -1998,2008,15822,15822,637,637,0,24839,20000 -1999,1999,,,,,,,31500 -1999,2000,4666,13255,,,8589,,31500 -1999,2001,9861,16405,686,1026,6544,15989,31500 -1999,2002,13971,19639,819,1039,5668,18902,31500 -1999,2003,18127,22473,910,1047,4347,21464,31500 -1999,2004,22032,23764,980,1050,1732,22632,31500 -1999,2005,23511,25094,1007,1053,1583,23831,31500 -1999,2006,24146,24795,1036,1047,649,23682,31500 -1999,2007,24592,25071,1039,1047,479,23946,31500 -1999,2008,24817,25107,1044,1047,290,23980,31500 -2000,2000,1302,15676,,,14374,,45000 -2000,2001,6513,18749,650,1354,12237,13847,45000 -2000,2002,12139,21900,932,1397,9760,15676,45000 -2000,2003,17828,27144,1095,1411,9316,19237,45000 -2000,2004,24030,29488,1216,1410,5458,20914,45000 -2000,2005,28853,34458,1292,1408,5605,24473,45000 -2000,2006,33222,36949,1367,1408,3727,26242,45000 -2000,2007,35902,37505,1391,1408,1603,26637,45000 -2000,2008,36782,37246,1402,1408,465,26453,45000 -2001,2001,1539.456,11827,304,1305,10288,9063,50000 -2001,2002,5952.241,16004,681,1421,10052,11262,50000 -2001,2003,12319.336,21022,936,1449,8703,14508,50000 -2001,2004,18608.788,26578,1092,1458,7969,18229,50000 -2001,2005,24386.973,34205,1225,1458,9818,23460,50000 -2001,2006,31090.065,37136,1357,1455,6046,25523,50000 -2001,2007,37069.815,38541,1432,1455,1471,26489,50000 -2001,2008,38519.529,38798,1446,1455,278,26665,50000 -2002,2002,2317.651,12811,203,1342,10494,9546,61183 -2002,2003,7931.635,20370,607,1514,12439,13455,61183 -2002,2004,13822.585,26656,841,1548,12833,17219,61183 -2002,2005,22095.617,37667,1089,1557,15572,24192,61183 -2002,2006,31945.485,44414,1327,1549,12469,28673,61183 -2002,2007,40628.819,48701,1464,1552,8072,31379,61183 -2002,2008,44437.623,48169,1523,1554,3731,30997,61183 -2003,2003,1743.211,9651,181,1373,7908,7029,69175 -2003,2004,6240.349,16995,614,1616,10755,10517,69175 -2003,2005,12683.23,30354,941,1630,17671,18622,69175 -2003,2006,22892.562,40594,1263,1626,17702,24966,69175 -2003,2007,34505.01,44231,1507,1629,9726,27152,69175 -2003,2008,39320.472,44373,1568,1629,5052,27239,69175 -2004,2004,2221.22,16995,235,1932,14774,8796,99322 -2004,2005,9898.432,40180,848,2168,30281,18533,99322 -2004,2006,25950.094,58866,1442,2234,32916,26350,99322 -2004,2007,43439.464,71707,1852,2249,28268,31884,99322 -2004,2008,52811.083,70288,2029,2258,17477,31129,99322 -2005,2005,3042.925,28674,295,2067,25631,13872,138151 -2005,2006,12218.989,47432,1119,2293,35213,20686,138151 -2005,2007,27072.964,70340,1664,2367,43268,29717,138151 -2005,2008,40026.352,70655,1946,2390,30629,29563,138151 -2006,2006,3531.114,27066,307,1473,23535,18375,107578 -2006,2007,11778.146,46783,906,1645,35005,28440,107578 -2006,2008,22819.111,48804,1201,1657,25985,29453,107578 -2007,2007,3528.854,19477,329,1192,15948,16340,62438 -2007,2008,11864.72,31732,791,1264,19867,25104,62438 -2008,2008,3408.876,18632,276,1036,15223,17985,47797 +Accident Year,Calendar Year,Paid Claims,Reported Claims,Closed Claim Counts,Reported Claim Counts,Case Outstanding,Reported Severities,Earned Premium,Incremental Paid Severity +1998,1998,,,,,,,20000, +1998,1999,,,,,,,20000, +1998,2000,6309,11171,,,4861,,20000, +1998,2001,8521,12380,510,634,3859,19526,20000, +1998,2002,10082,13216,547,635,3134,20813,20000, +1998,2003,11620,14067,575,635,2447,22152,20000, +1998,2004,13242,14688,598,637,1446,23058,20000, +1998,2005,14419,16366,612,637,1947,25692,20000, +1998,2006,15311,16163,620,637,853,25374,20000, +1998,2007,15764,15835,635,637,71,24859,20000, +1998,2008,15822,15822,637,637,0,24839,20000, +1999,1999,,,,,,,31500, +1999,2000,4666,13255,,,8589,,31500, +1999,2001,9861,16405,686,1026,6544,15989,31500, +1999,2002,13971,19639,819,1039,5668,18902,31500, +1999,2003,18127,22473,910,1047,4347,21464,31500, +1999,2004,22032,23764,980,1050,1732,22632,31500, +1999,2005,23511,25094,1007,1053,1583,23831,31500, +1999,2006,24146,24795,1036,1047,649,23682,31500, +1999,2007,24592,25071,1039,1047,479,23946,31500, +1999,2008,24817,25107,1044,1047,290,23980,31500, +2000,2000,1302,15676,,,14374,,45000, +2000,2001,6513,18749,650,1354,12237,13847,45000, +2000,2002,12139,21900,932,1397,9760,15676,45000, +2000,2003,17828,27144,1095,1411,9316,19237,45000, +2000,2004,24030,29488,1216,1410,5458,20914,45000, +2000,2005,28853,34458,1292,1408,5605,24473,45000, +2000,2006,33222,36949,1367,1408,3727,26242,45000, +2000,2007,35902,37505,1391,1408,1603,26637,45000, +2000,2008,36782,37246,1402,1408,465,26453,45000, +2001,2001,1539,11827,304,1305,10288,9063,50000,5064 +2001,2002,5952,16004,681,1421,10052,11262,50000,11705 +2001,2003,12319,21022,936,1449,8703,14508,50000,24969 +2001,2004,18609,26578,1092,1458,7969,18229,50000,40317 +2001,2005,24387,34205,1225,1458,9818,23460,50000,43445 +2001,2006,31090,37136,1357,1455,6046,25523,50000,50781 +2001,2007,37070,38541,1432,1455,1471,26489,50000,79730 +2001,2008,38519,38798,1446,1455,278,26665,50000,103551 +2002,2002,2318,12811,203,1342,10494,9546,61183,11417 +2002,2003,7932,20370,607,1514,12439,13455,61183,13896 +2002,2004,13822,26656,841,1548,12833,17219,61183,25175 +2002,2005,22095,37667,1089,1557,15572,24192,61183,33359 +2002,2006,31945,44414,1327,1549,12469,28673,61183,41386 +2002,2007,40629,48701,1464,1552,8072,31379,61183,63382 +2002,2008,44437,48169,1523,1554,3731,30997,61183,64556 +2003,2003,1743,9651,181,1373,7908,7029,69175,9631 +2003,2004,6240,16995,614,1616,10755,10517,69175,10386 +2003,2005,12683,30354,941,1630,17671,18622,69175,19703 +2003,2006,22892,40594,1263,1626,17702,24966,69175,31706 +2003,2007,34505,44231,1507,1629,9726,27152,69175,47592 +2003,2008,39320,44373,1568,1629,5052,27239,69175,78942 +2004,2004,2221,16995,235,1932,14774,8796,99322,9452 +2004,2005,9898,40180,848,2168,30281,18533,99322,12524 +2004,2006,25950,58866,1442,2234,32916,26350,99322,27023 +2004,2007,43439,71707,1852,2249,28268,31884,99322,42657 +2004,2008,52811,70288,2029,2258,17477,31129,99322,52947 +2005,2005,3043,28674,295,2067,25631,13872,138151,10315 +2005,2006,12219,47432,1119,2293,35213,20686,138151,11136 +2005,2007,27073,70340,1664,2367,43268,29717,138151,27255 +2005,2008,40026,70655,1946,2390,30629,29563,138151,45934 +2006,2006,3531,27066,307,1473,23535,18375,107578,11502 +2006,2007,11778,46783,906,1645,35005,28440,107578,13768 +2006,2008,22819,48804,1201,1657,25985,29453,107578,37427 +2007,2007,3529,19477,329,1192,15948,16340,62438,10726 +2007,2008,11865,31732,791,1264,19867,25104,62438,18043 +2008,2008,3409,18632,276,1036,15223,17985,47797,12351 From dd4a4daaa99f0cc4c575f7715f5d2badf0bab01d Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:41:56 +0000 Subject: [PATCH 12/27] updating exhibit --- docs/friedland/chapter_11.ipynb | 504 +++++++++++++++----------------- 1 file changed, 243 insertions(+), 261 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index bf82fb9e3..d63e79ea8 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -4,10 +4,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "24730109-b724-4a80-817b-c11900453da4", "showTitle": false, @@ -46,17 +43,37 @@ "import chainladder as cl\n", "from IPython.display import display as nb_display\n", "\n", + "def format_exh(\n", + " exh_df:pd.DataFrame,\n", + " value_cols: list[str] = [],\n", + " factor_cols: list[str] = [],\n", + " other_formats: dict = {}\n", + "):\n", + " return exh_df \\\n", + " .reset_index() \\\n", + " .rename(columns={'origin':'Accident Year'}) \\\n", + " .style \\\n", + " .hide(axis='index') \\\n", + " .set_properties(**{\"text-align\":\"right\"}) \\\n", + " .format({\n", + " 'Accident Year':\"{:%Y}\",\n", + " **{x:\"{:,.0f}\" for x in value_cols},\n", + " **{x:\"{:,.3f}\" for x in factor_cols},\n", + " **other_formats\n", + " }) \n", + "\n", "#create reported/paid summary exhibit\n", "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", " output = reported_tri.latest_diagonal.to_frame()\n", " output.columns = ['Reported']\n", " output['Paid'] = paid_tri.latest_diagonal.to_frame()\n", " #using floating point offset to achieve standard rounding\n", - " output['Ult Claims'] = (ult.latest_diagonal + 1e-9).round(0).to_frame()\n", + " output['Ult Claims'] = ult.latest_diagonal.to_frame()\n", " output['Case Outstanding'] = output[\"Reported\"] - output[\"Paid\"]\n", " output['IBNR'] = output[\"Ult Claims\"] - output[\"Reported\"]\n", " output['Unpaid'] = output[\"Ult Claims\"] - output[\"Paid\"]\n", - " return output.round(0)\n", + " output.index.name = 'origin'\n", + " return output\n", "\n", "#create a dict of developed triangles for each of the selection assumptions on a given page\n", "def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", @@ -115,10 +132,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "1f3f99fd-cb60-4901-807f-71273d213005", "showTitle": false, @@ -196,10 +210,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "65209fce-b487-4443-92bc-17da6ca31835", "showTitle": false, @@ -248,10 +259,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d91cc78f-5868-4711-a5a0-d49a9b331ec9", "showTitle": false, @@ -300,10 +308,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "10316a34-7ae5-41f6-b266-70fdda592cd6", "showTitle": false, @@ -335,23 +340,37 @@ "source": [ "e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]\n", "e1_s3[\"Selected Ult CC\"] = e1_cc_ult.latest_diagonal.to_frame()\n", - "e1_s3[['Closed CDF','Reported CDF']] = e1_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e1_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e1_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + "format_exh(\n", + " e1_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ") " ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "a8b310a4-5622-4d1a-8217-7fac37ac21c4", "showTitle": false, @@ -388,10 +407,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b876aa8e-280f-4e37-9b2b-0d83ebaf86e1", "showTitle": false, @@ -429,10 +445,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "090cd8df-b99a-438a-b727-80d208b93a6d", "showTitle": false, @@ -478,10 +491,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "16ae37e3-32c6-44c0-9d8f-6a9295b0d113", "showTitle": false, @@ -512,23 +522,32 @@ "outputs": [], "source": [ "e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Reported Severity',\n", + " 'Ultimate':'Ult Severity'\n", + " }\n", + ")\n", "e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']\n", "e1_s7[\"Ult Claims\"] = e1_ult.latest_diagonal.to_frame()\n", - "e1_s7['CDF'] = e1_s7['CDF'].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e1_s7[['Reported Severity','Ult Severity','Ult Claims']] = (e1_s7[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", - "e1_s7" + "format_exh(\n", + " e1_s7,\n", + " [\n", + " 'Reported Severity',\n", + " 'Ult Severity',\n", + " 'Selected Ult CC',\n", + " 'Ult Claims',\n", + " ],\n", + " ['CDF']\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3135d7a1-01f8-46aa-bf62-3cd6e0953696", "showTitle": false, @@ -558,7 +577,8 @@ }, "outputs": [], "source": [ - "summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)" + "e1_s8 = summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)\n", + "format_exh(e1_s8,e1_s8.columns)" ] }, { @@ -619,10 +639,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "dd3aa676-954a-470b-b812-3f9c1492ed66", "showTitle": false, @@ -697,10 +714,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c2e97f2e-16f7-4b58-ab54-ac46fb34f31c", "showTitle": true, @@ -750,10 +764,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5a9022d4-50c6-4f97-bfa3-1ce4d122a2a1", "showTitle": true, @@ -803,10 +814,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "ff41fe54-f723-4bfa-a166-73ff106e63d1", "showTitle": true, @@ -839,23 +847,37 @@ "source": [ "e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]\n", "e2_s3[\"Selected Ult CC\"] = e2_cc_ult.latest_diagonal.to_frame()\n", - "e2_s3[['Closed CDF','Reported CDF']] = e2_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e2_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e2_s3[['development','Closed Claim Counts','Reported Claim Counts','Closed CDF','Reported CDF','Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']]" + "format_exh(\n", + " e2_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ") " ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0f7e73e8-737b-4909-87a6-c68d0f621dbf", "showTitle": true, @@ -894,10 +916,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "be51ed2d-bf1e-4ca4-8b6a-cb891adda7ff", "showTitle": true, @@ -944,10 +963,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d1006396-3dfd-46b2-a0a4-96461ba2d5f2", "showTitle": true, @@ -979,23 +995,34 @@ "outputs": [], "source": [ "e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Reported Severity','Ultimate':'Ult Severity'})\n", + "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Reported Severity',\n", + " 'Ultimate':'Ult Severity'\n", + " }\n", + ")\n", "e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']\n", "e2_s6[\"Ult Claims\"] = e2_ult.latest_diagonal.to_frame()\n", - "e2_s6['CDF'] = e2_s6['CDF'].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e2_s6[['Reported Severity','Ult Severity','Ult Claims']] = (e2_s6[['Reported Severity','Ult Severity','Ult Claims']]+1e-9).round(0)\n", - "e2_s6" + "format_exh(\n", + " e2_s6,\n", + " [\n", + " 'Reported Severity',\n", + " 'Ult Severity',\n", + " 'Selected Ult CC',\n", + " 'Ult Claims',\n", + " ],\n", + " [\n", + " 'CDF',\n", + " ]\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "fb3533d3-36e4-4971-8f9a-49e76ec75d43", "showTitle": true, @@ -1026,7 +1053,8 @@ }, "outputs": [], "source": [ - "summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)" + "e2_s7 = summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)\n", + "format_exh(e2_s7,e2_s7.columns)" ] }, { @@ -1071,10 +1099,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "e9c5436c-4e87-448f-bc10-72e08932caf9", "showTitle": false, @@ -1177,10 +1202,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b8b09c7a-0457-4e83-8be5-30b2082ee425", "showTitle": false, @@ -1229,10 +1251,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "80da84e0-c7d8-4dd9-930c-6956ac1bcb3b", "showTitle": false, @@ -1280,10 +1299,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "efa10e00-52c8-4fd8-87ac-acd7e3358960", "showTitle": false, @@ -1315,23 +1331,37 @@ "source": [ "e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','CDF':'Closed CDF','Ultimate':'Ult Count Using CCC'})\n", + "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Closed Claim Counts',\n", + " 'CDF':'Closed CDF',\n", + " 'Ultimate':'Ult Count Using CCC'\n", + " }\n", + ")\n", "e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]\n", "e3_s3[\"Selected Ult CC\"] = e3_cc_ult.latest_diagonal.to_frame()\n", - "e3_s3[['Closed CDF','Reported CDF']] = e3_s3[['Closed CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] = (e3_s3[['Ult Count Using CCC','Ult Count Using RCC','Selected Ult CC']] + 1e-9).round(0)\n", - "e3_s3" + "format_exh(\n", + " e3_s3,\n", + " [\n", + " 'Closed Claim Counts',\n", + " 'Reported Claim Counts',\n", + " 'Ult Count Using CCC',\n", + " 'Ult Count Using RCC',\n", + " 'Selected Ult CC',\n", + " ],\n", + " [\n", + " 'Closed CDF',\n", + " 'Reported CDF',\n", + " ]\n", + ")" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7a7c251e-b567-464c-bc82-597c2836ccdb", "showTitle": false, @@ -1368,11 +1398,20 @@ "e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()\n", "e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']\n", "e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']\n", - "e3_s4[['CC Trend','Payroll Trend']] = e3_s4[['CC Trend','Payroll Trend']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s4[['Trended Ult CC','Trended Payroll']] = (e3_s4[['Trended Ult CC','Trended Payroll']] + 1e-9).round(0)\n", - "e3_s4['Trended Ultimate Freq'] = e3_s4[\"Trended Ultimate Freq\"].map(\"{:.2%}\".format)\n", - "nb_display(e3_s4)\n", + "nb_display(format_exh(\n", + " e3_s4,\n", + " [\n", + " 'Selected Ult CC',\n", + " 'Trended Ult CC',\n", + " 'Payroll',\n", + " 'Trended Payroll',\n", + " ],\n", + " [\n", + " 'CC Trend',\n", + " 'Payroll Trend',\n", + " ],\n", + " {'Trended Ultimate Freq' : \"{:.2%}\"}\n", + "))\n", "print(f\"Selected Frequency at 2008 level {e3_freq_2008:.2%}\")\n", "print(f\"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}\")" ] @@ -1381,10 +1420,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f3e4e3fe-5297-4079-bb12-103c43e28820", "showTitle": false, @@ -1428,10 +1464,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d28ce8b5-8add-43d5-9609-6dac464d77ac", "showTitle": false, @@ -1478,10 +1511,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f9f3d551-9208-4773-8059-8ee160821d09", "showTitle": false, @@ -1528,10 +1558,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3dda605a-2dd1-472a-bd42-ef193c95c1b1", "showTitle": false, @@ -1563,23 +1590,27 @@ "source": [ "e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", "e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(columns={'Latest':'Paid Sev','CDF':'Paid CDF','Ultimate':'Ult Paid Sev'})\n", + "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(\n", + " columns={\n", + " 'development':'Age',\n", + " 'Latest':'Paid Sev',\n", + " 'CDF':'Paid CDF',\n", + " 'Ultimate':'Ult Paid Sev'\n", + " }\n", + ")\n", "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", "e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)\n", "#using floating point offset to achieve standard rounding\n", "e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)\n", - "e3_s8.iloc[:-2,:][['development','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" + "e3_s8.iloc[:-2,:][['Age','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "781252e1-a9d8-4a65-b502-ba78541d132e", "showTitle": false, @@ -1627,10 +1658,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0d86dcac-eb94-4810-910d-8207388ffff6", "showTitle": false, @@ -1673,6 +1701,27 @@ "e3_s10.T" ] }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s4['Trended Ultimate Freq']" + ] + }, { "cell_type": "code", "execution_count": 0, @@ -1699,10 +1748,14 @@ "assert np.allclose(\n", " e3_s3['Selected Ult CC'].values[...,1:],\n", " np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),\n", - " atol=1\n", + " rtol=0.001\n", ")\n", "#Exhibit III Sheet 4\n", - "assert np.all(e3_s4['Trended Ultimate Freq'].values[...,1:] == np.array(['0.53%', '0.53%', '0.54%', '0.43%', '0.35%', '0.36%', '0.36%']))\n", + "assert np.allclose(\n", + " e3_s4['Trended Ultimate Freq'].values[...,1:],\n", + " np.array([0.0053, 0.0053, 0.0054, 0.0043, 0.0035, 0.0036, 0.0036]),\n", + " atol = 0.0001\n", + " )\n", "#Exhibit III Sheet 6\n", "assert np.allclose(\n", " e3_psev_selected.ldf_.round(3).values,\n", @@ -1729,10 +1782,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "fb822f26-db0f-4035-aa8c-4511509cb6af", "showTitle": false, @@ -1831,10 +1881,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "cca2c635-f303-4d44-9dd3-de27a63faa4b", "showTitle": false, @@ -1881,10 +1928,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "6111c76b-a5be-4a64-ab78-a6bea6a931a9", "showTitle": false, @@ -1930,10 +1974,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f01f14d6-2449-46e0-87e3-2215d2fcf7e7", "showTitle": false, @@ -2014,10 +2055,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5b49ffa0-f939-4d71-917d-1ab1ee88c000", "showTitle": false, @@ -2134,10 +2172,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "905e7cd2-2188-4bb0-a656-d0cec6558dd8", "showTitle": false, @@ -2186,10 +2221,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "f105d40b-944b-4c23-b2aa-b335748d5855", "showTitle": false, @@ -2237,10 +2269,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "6a8888f3-14e0-4fcf-ad12-2531b7e9e244", "showTitle": false, @@ -2282,10 +2311,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7f896e42-0389-4483-878e-2b6e4ede5d4f", "showTitle": false, @@ -2327,10 +2353,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c8d9acc3-9ee7-4390-b017-5e54a8a2d15f", "showTitle": false, @@ -2370,10 +2393,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "5431abfc-5da3-4fe1-9d75-1d33c25e237b", "showTitle": false, @@ -2417,10 +2437,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c783393d-2550-4d5b-8163-0d84f31204cf", "showTitle": false, @@ -2462,10 +2479,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "1aa1d6f7-c11f-4cc5-aa18-6787929851f2", "showTitle": false, @@ -2509,10 +2523,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d1042988-724d-44ea-8d13-9df69dd70f04", "showTitle": false, @@ -2570,10 +2581,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "7473207a-ed4d-441d-869d-9043a338bae2", "showTitle": false, @@ -2615,10 +2623,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "0b84f3d7-71f8-4a5f-8113-186020db4f0f", "showTitle": false, @@ -2662,10 +2667,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "9fb3d9d7-1038-4615-a512-c088f2ac3f34", "showTitle": false, @@ -2794,10 +2796,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "9c887dd3-d12f-4f0d-b9a1-75dbe3f4b79f", "showTitle": false, @@ -2846,7 +2845,11 @@ "e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)\n", "\n", "#calculate and select incremental paid severity\n", - "e6_ipsev = e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", + "#in a real world scenario, it's possible to calculate incremental paid severity like this\n", + "# e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", + "#however, the cumulative paid data in Friedland is only given in thousands\n", + "#therefore, we will directly use the incremental paid severity data as given\n", + "e6_ipsev = e6_tri['Incremental Paid Severity']\n", "e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", "e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)\n", "e6_sevs_sel = e6_sevs['simple_2'].copy()\n", @@ -2884,10 +2887,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "8c663ba3-5416-4bc7-a4bd-c6b6fd456f52", "showTitle": false, @@ -2931,10 +2931,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "3cea4559-9ad7-4d0d-80c9-4275bc3f6749", "showTitle": false, @@ -2974,10 +2971,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d82ad2af-f1b4-4723-8f59-d8c8c8e18651", "showTitle": false, @@ -3021,10 +3015,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "c513183d-95a5-48b6-a956-5d9b5361b1f2", "showTitle": false, @@ -3068,10 +3059,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "a58fc306-7907-45c7-ae2b-9a6ad2b4e5cc", "showTitle": false, @@ -3129,10 +3117,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "cec56e19-6976-43e9-862d-b866a49fbd84", "showTitle": false, @@ -3174,10 +3159,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "b9a1663c-421e-4f69-bedc-b59dd431dda1", "showTitle": false, @@ -3221,10 +3203,7 @@ "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, "nuid": "d60ebfc1-1517-4d32-85e1-fd79fda6785e", "showTitle": false, @@ -3333,6 +3312,9 @@ "dashboards": [], "environmentMetadata": { "base_environment": "", + "dependencies": [ + "-e ./chainladder-python" + ], "environment_version": "5" }, "inputWidgetPreferences": null, @@ -3346,7 +3328,7 @@ }, "pythonIndentUnit": 4 }, - "notebookName": "Chapter 11", + "notebookName": "chapter_11", "widgets": {} }, "language_info": { @@ -3355,4 +3337,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From e79195c17962ed3a6f802a3fecc09340fa52b07f Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:55:03 +0000 Subject: [PATCH 13/27] reverting xyz changes --- docs/friedland/chapter_6.rst | 38 ++++++++-------- docs/friedland/chapter_7.rst | 84 ++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/friedland/chapter_6.rst b/docs/friedland/chapter_6.rst index 95a4f2f04..6af1648a9 100644 --- a/docs/friedland/chapter_6.rst +++ b/docs/friedland/chapter_6.rst @@ -87,14 +87,14 @@ Table 3 - Paid Claim Development Triangle .. doctest:: >>> tri['Paid Claims'] - 12 24 36 48 60 72 84 - 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 - 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN - 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN - 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN - 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN - 2007 3528.854 11864.720 NaN NaN NaN NaN NaN - 2008 3408.876 NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 + 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 + 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN + 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN + 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN + 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN + 2007 3529.0 11865.0 NaN NaN NaN NaN NaN + 2008 3409.0 NaN NaN NaN NaN NaN NaN Table 4 - Ratio of Reported Claims to Earned Premium ####################################################### @@ -188,7 +188,7 @@ Table 6 - Ratio of Paid Claims-to-Reported Claims 2002 0.181 0.389 0.519 0.587 0.719 0.834 0.923 2003 0.181 0.367 0.418 0.564 0.780 0.886 NaN 2004 0.131 0.246 0.441 0.606 0.751 NaN NaN - 2005 0.106 0.258 0.385 0.567 NaN NaN NaN + 2005 0.106 0.258 0.385 0.566 NaN NaN NaN 2006 0.130 0.252 0.468 NaN NaN NaN NaN 2007 0.181 0.374 NaN NaN NaN NaN NaN 2008 0.183 NaN NaN NaN NaN NaN NaN @@ -279,10 +279,10 @@ Table 13 – Average Paid Claim Development Triangle >>> (tri["Paid Claims"] / tri["Closed Claim Counts"] * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 11417.0 13067.0 16436.0 20290.0 24073.0 27752.0 29178.0 - 2003 9631.0 10163.0 13478.0 18126.0 22896.0 25077.0 NaN - 2004 9452.0 11673.0 17996.0 23455.0 26028.0 NaN NaN - 2005 10315.0 10920.0 16270.0 20569.0 NaN NaN NaN + 2002 11419.0 13068.0 16435.0 20289.0 24073.0 27752.0 29177.0 + 2003 9630.0 10163.0 13478.0 18125.0 22896.0 25077.0 NaN + 2004 9451.0 11672.0 17996.0 23455.0 26028.0 NaN NaN + 2005 10315.0 10920.0 16270.0 20568.0 NaN NaN NaN 2006 11502.0 13000.0 19000.0 NaN NaN NaN NaN 2007 10726.0 15000.0 NaN NaN NaN NaN NaN 2008 12351.0 NaN NaN NaN NaN NaN NaN @@ -294,10 +294,10 @@ Table 14 – Average Case Outstanding Development Triangle >>> ((tri["Reported Claims"] - tri["Paid Claims"]) / (tri["Reported Claim Counts"] - tri["Closed Claim Counts"]) * 1000).round(decimals=0) 12 24 36 48 60 72 84 - 2002 9213.0 13714.0 18152.0 33272.0 56164.0 91729.0 120367.0 - 2003 6634.0 10733.0 25647.0 48764.0 79721.0 82828.0 NaN - 2004 8706.0 22941.0 41560.0 71203.0 76318.0 NaN NaN - 2005 14464.0 29994.0 61546.0 68983.0 NaN NaN NaN - 2006 20184.0 47368.0 56984.0 NaN NaN NaN NaN - 2007 18480.0 42003.0 NaN NaN NaN NaN NaN + 2002 9212.0 13713.0 18153.0 33274.0 56167.0 91727.0 120387.0 + 2003 6634.0 10734.0 25647.0 48766.0 79721.0 82836.0 NaN + 2004 8706.0 22941.0 41561.0 71204.0 76319.0 NaN NaN + 2005 14464.0 29994.0 61546.0 68984.0 NaN NaN NaN + 2006 20184.0 47368.0 56985.0 NaN NaN NaN NaN + 2007 18480.0 42002.0 NaN NaN NaN NaN NaN 2008 20030.0 NaN NaN NaN NaN NaN NaN diff --git a/docs/friedland/chapter_7.rst b/docs/friedland/chapter_7.rst index d2f1faab2..0bc55bb98 100644 --- a/docs/friedland/chapter_7.rst +++ b/docs/friedland/chapter_7.rst @@ -494,35 +494,35 @@ Exhibit II Sheet 2 p111 >>> paid_devs = dev_exhibit(tri['Paid Claims'],assumptions,'volume_2',1.01) PART 1 - Data Triangle - 12 24 36 48 60 72 84 96 108 120 132 - 1998 NaN NaN 6309.000 8521.000 10082.000 11620.000 13242.000 14419.000 15311.0 15764.0 15822.0 - 1999 NaN 4666.000 9861.000 13971.000 18127.000 22032.000 23511.000 24146.000 24592.0 24817.0 NaN - 2000 1302.000 6513.000 12139.000 17828.000 24030.000 28853.000 33222.000 35902.000 36782.0 NaN NaN - 2001 1539.456 5952.241 12319.336 18608.788 24386.973 31090.065 37069.815 38519.529 NaN NaN NaN - 2002 2317.651 7931.635 13822.585 22095.617 31945.485 40628.819 44437.623 NaN NaN NaN NaN - 2003 1743.211 6240.349 12683.230 22892.562 34505.010 39320.472 NaN NaN NaN NaN NaN - 2004 2221.220 9898.432 25950.094 43439.464 52811.083 NaN NaN NaN NaN NaN NaN - 2005 3042.925 12218.989 27072.964 40026.352 NaN NaN NaN NaN NaN NaN NaN - 2006 3531.114 11778.146 22819.111 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3528.854 11864.720 NaN NaN NaN NaN NaN NaN NaN NaN NaN - 2008 3408.876 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + 12 24 36 48 60 72 84 96 108 120 132 + 1998 NaN NaN 6309.0 8521.0 10082.0 11620.0 13242.0 14419.0 15311.0 15764.0 15822.0 + 1999 NaN 4666.0 9861.0 13971.0 18127.0 22032.0 23511.0 24146.0 24592.0 24817.0 NaN + 2000 1302.0 6513.0 12139.0 17828.0 24030.0 28853.0 33222.0 35902.0 36782.0 NaN NaN + 2001 1539.0 5952.0 12319.0 18609.0 24387.0 31090.0 37070.0 38519.0 NaN NaN NaN + 2002 2318.0 7932.0 13822.0 22095.0 31945.0 40629.0 44437.0 NaN NaN NaN NaN + 2003 1743.0 6240.0 12683.0 22892.0 34505.0 39320.0 NaN NaN NaN NaN NaN + 2004 2221.0 9898.0 25950.0 43439.0 52811.0 NaN NaN NaN NaN NaN NaN + 2005 3043.0 12219.0 27073.0 40026.0 NaN NaN NaN NaN NaN NaN NaN + 2006 3531.0 11778.0 22819.0 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3529.0 11865.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2008 3409.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 2 - Age-to-Age Factors 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 1998 NaN NaN 1.350610 1.183194 1.152549 1.139587 1.088884 1.061863 1.029587 1.003679 1999 NaN 2.113373 1.416793 1.297473 1.215425 1.067130 1.027009 1.018471 1.009149 NaN 2000 5.002304 1.863811 1.468655 1.347880 1.200707 1.151423 1.080669 1.024511 NaN NaN - 2001 3.866457 2.069697 1.510535 1.310508 1.274864 1.192336 1.039108 NaN NaN NaN - 2002 3.422273 1.742716 1.598516 1.445784 1.271817 1.093746 NaN NaN NaN NaN - 2003 3.579801 2.032455 1.804947 1.507259 1.139558 NaN NaN NaN NaN NaN - 2004 4.456304 2.621637 1.673962 1.215740 NaN NaN NaN NaN NaN NaN - 2005 4.015541 2.215647 1.478462 NaN NaN NaN NaN NaN NaN NaN - 2006 3.335533 1.937411 NaN NaN NaN NaN NaN NaN NaN NaN - 2007 3.362202 NaN NaN NaN NaN NaN NaN NaN NaN NaN + 2001 3.867446 2.069724 1.510593 1.310495 1.274860 1.192345 1.039088 NaN NaN NaN + 2002 3.421915 1.742562 1.598539 1.445802 1.271842 1.093726 NaN NaN NaN NaN + 2003 3.580034 2.032532 1.804936 1.507295 1.139545 NaN NaN NaN NaN NaN + 2004 4.456551 2.621742 1.673950 1.215751 NaN NaN NaN NaN NaN NaN + 2005 4.015445 2.215648 1.478447 NaN NaN NaN NaN NaN NaN NaN + 2006 3.335599 1.937426 NaN NaN NaN NaN NaN NaN NaN NaN + 2007 3.362142 NaN NaN NaN NaN NaN NaN NaN NaN NaN PART 3 - Average Age-to-Age Factor 12-24 24-36 36-48 48-60 60-72 72-84 84-96 96-108 108-120 120-132 simple_5 3.750 2.110 1.613 1.365 1.220 1.129 1.059 1.035 1.019 1.004 simple_3 3.571 2.258 1.652 1.390 1.229 1.146 1.049 1.035 1.019 1.004 - simple_2 3.349 2.077 1.576 1.361 1.206 1.143 1.060 1.021 1.019 1.004 + simple_2 3.349 2.077 1.576 1.362 1.206 1.143 1.060 1.021 1.019 1.004 volume_4 3.713 2.206 1.615 1.342 1.218 1.128 1.056 1.030 1.017 1.004 volume_3 3.550 2.238 1.619 1.349 1.222 1.141 1.051 1.030 1.017 1.004 volume_2 3.349 2.079 1.574 1.316 1.203 1.136 1.059 1.022 1.017 1.004 @@ -543,17 +543,17 @@ Exhibit II Sheet 3 p112 >>> exhibit = rounded_development_summary(reported_devs["Selected"],paid_devs["Selected"]) >>> exhibit Age Reported Claims Paid Claims Reported CDF Paid CDF Reported Ultimate Paid Ultimate - 1998 132 15822.0 15822.000 1.000 1.010 15822.0 15980.0 - 1999 120 25107.0 24817.000 0.999 1.014 25082.0 25164.0 - 2000 108 37246.0 36782.000 0.992 1.031 36948.0 37922.0 - 2001 96 38798.0 38519.529 0.992 1.054 38488.0 40600.0 - 2002 84 48169.0 44437.623 1.003 1.116 48314.0 49592.0 - 2003 72 44373.0 39320.472 1.013 1.268 44950.0 49858.0 - 2004 60 70288.0 52811.083 1.064 1.525 74786.0 80537.0 - 2005 48 70655.0 40026.352 1.085 2.007 76661.0 80333.0 - 2006 36 48804.0 22819.111 1.196 3.160 58370.0 72108.0 - 2007 24 31732.0 11864.720 1.512 6.569 47979.0 77939.0 - 2008 12 18632.0 3408.876 2.551 21.999 47530.0 74992.0 + 1998 132 15822.0 15822.0 1.000 1.010 15822.0 15980.0 + 1999 120 25107.0 24817.0 0.999 1.014 25082.0 25164.0 + 2000 108 37246.0 36782.0 0.992 1.031 36948.0 37922.0 + 2001 96 38798.0 38519.0 0.992 1.054 38488.0 40599.0 + 2002 84 48169.0 44437.0 1.003 1.116 48314.0 49592.0 + 2003 72 44373.0 39320.0 1.013 1.268 44950.0 49858.0 + 2004 60 70288.0 52811.0 1.064 1.525 74786.0 80537.0 + 2005 48 70655.0 40026.0 1.085 2.007 76661.0 80332.0 + 2006 36 48804.0 22819.0 1.196 3.160 58370.0 72108.0 + 2007 24 31732.0 11865.0 1.512 6.569 47979.0 77941.0 + 2008 12 18632.0 3409.0 2.551 21.999 47530.0 74995.0 Exhibit II Sheet 4 p113 ########################## @@ -563,17 +563,17 @@ Exhibit II Sheet 4 p113 >>> unpaid_exhibit = unpaid_summary(exhibit) >>> unpaid_exhibit[['Case Outstanding','Reported Method IBNR','Paid Method IBNR','Reported Method Unpaid','Paid Method Unpaid']] Case Outstanding Reported Method IBNR Paid Method IBNR Reported Method Unpaid Paid Method Unpaid - 1998 0.000 0.0 158.0 0.000 158.000 - 1999 290.000 -25.0 57.0 265.000 347.000 - 2000 464.000 -298.0 676.0 166.000 1140.000 - 2001 278.471 -310.0 1802.0 -31.529 2080.471 - 2002 3731.377 145.0 1423.0 3876.377 5154.377 - 2003 5052.528 577.0 5485.0 5629.528 10537.528 - 2004 17476.917 4498.0 10249.0 21974.917 27725.917 - 2005 30628.648 6006.0 9678.0 36634.648 40306.648 - 2006 25984.889 9566.0 23304.0 35550.889 49288.889 - 2007 19867.280 16247.0 46207.0 36114.280 66074.280 - 2008 15223.124 28898.0 56360.0 44121.124 71583.124 + 1998 0.0 0.0 158.0 0.0 158.0 + 1999 290.0 -25.0 57.0 265.0 347.0 + 2000 464.0 -298.0 676.0 166.0 1140.0 + 2001 279.0 -310.0 1801.0 -31.0 2080.0 + 2002 3732.0 145.0 1423.0 3877.0 5155.0 + 2003 5053.0 577.0 5485.0 5630.0 10538.0 + 2004 17477.0 4498.0 10249.0 21975.0 27726.0 + 2005 30629.0 6006.0 9677.0 36635.0 40306.0 + 2006 25985.0 9566.0 23304.0 35551.0 49289.0 + 2007 19867.0 16247.0 46209.0 36114.0 66076.0 + 2008 15223.0 28898.0 56363.0 44121.0 71586.0 Persisting the Selected Estimators ################################## From ca41421f6d65b26d7ed323ae15084e6fd75f4e05 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:10:38 -0700 Subject: [PATCH 14/27] Update _toc.yml --- docs/_toc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_toc.yml b/docs/_toc.yml index 62d710b9e..e2d30269d 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -36,8 +36,8 @@ parts: - file: friedland/chapter_6.rst - file: friedland/chapter_7.rst - file: friedland/chapter_9.ipynb - - file: friedland/chapter_11.ipynb - file: friedland/chapter_10.ipynb + - file: friedland/chapter_11.ipynb - chapters: - file: gallery/index.md - chapters: From d01c817b4a52f3d12aa14f85731495ae3ad9c00a Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 07:37:57 +0000 Subject: [PATCH 15/27] removing a debugging cell --- docs/friedland/chapter_11.ipynb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index d63e79ea8..722a047d7 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -1701,27 +1701,6 @@ "e3_s10.T" ] }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "e3_s4['Trended Ultimate Freq']" - ] - }, { "cell_type": "code", "execution_count": 0, From cba3b81279ec389b8a7dfe95fc1699dbf189158e Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 07:37:57 +0000 Subject: [PATCH 16/27] removing a debugging cell --- docs/friedland/chapter_11.ipynb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index d63e79ea8..722a047d7 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -1701,27 +1701,6 @@ "e3_s10.T" ] }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "e3_s4['Trended Ultimate Freq']" - ] - }, { "cell_type": "code", "execution_count": 0, From abdd11d45ca4577af86c8e37b545e6e5ff6d2360 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 08:32:25 +0000 Subject: [PATCH 17/27] more formatting --- .../utils/data/friedland_auto_freq_sev.csv | 112 +++++++++--------- docs/friedland/chapter_11.ipynb | 91 ++++++++++---- 2 files changed, 121 insertions(+), 82 deletions(-) diff --git a/chainladder/utils/data/friedland_auto_freq_sev.csv b/chainladder/utils/data/friedland_auto_freq_sev.csv index 6a22a655a..907eb3c0d 100644 --- a/chainladder/utils/data/friedland_auto_freq_sev.csv +++ b/chainladder/utils/data/friedland_auto_freq_sev.csv @@ -1,56 +1,56 @@ -Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims -2003-07,2003-07,2547,3556,14234668,4003, -2003-07,2004-01,3262,3314,14959396,4514, -2003-07,2004-07,3287,3301,14920520,4520, -2003-07,2005-01,3291,3299,14911480,4520, -2003-07,2005-07,3292,3295,14926350,4530, -2003-07,2006-01,3292,3294,14862528,4512, -2003-07,2006-07,3292,3293,14861309,4513, -2003-07,2007-01,3292,3293,14854723,4511, -2003-07,2007-07,3292,3293,14851430,4510, -2003-07,2008-01,3292,3292,14846920,4510,14846000 -2004-01,2004-01,2791,3492,14547672,4166, -2004-01,2004-07,3217,3262,14672476,4498, -2004-01,2005-01,3240,3250,14644500,4506, -2004-01,2005-07,3242,3247,14627735,4505, -2004-01,2006-01,3243,3247,14621241,4503, -2004-01,2006-07,3243,3245,14608990,4502, -2004-01,2007-01,3243,3245,14608990,4502, -2004-01,2007-07,3243,3244,14610976,4504, -2004-01,2008-01,3242,3243,14616201,4507,14614000 -2004-07,2004-07,2099,2980,12128600,4070, -2004-07,2005-01,2677,2712,12575544,4637, -2004-07,2005-07,2695,2704,12541152,4638, -2004-07,2006-01,2697,2702,12531876,4638, -2004-07,2006-07,2697,2700,12522600,4683, -2004-07,2007-01,2698,2700,12522600,4638, -2004-07,2007-07,2698,2699,12509865,4635, -2004-07,2008-01,2698,2699,12501768,4632,12502000 -2005-01,2005-01,2370,2896,11980752,4137, -2005-01,2005-07,2735,2768,11921776,4307, -2005-01,2006-01,2751,2761,11883344,4304, -2005-01,2006-07,2754,2758,11862158,4301, -2005-01,2007-01,2755,2758,11853884,4298, -2005-01,2007-07,2755,2758,11842852,4294, -2005-01,2008-01,2756,2757,11841315,4295,11840000 -2005-07,2005-07,1966,2814,11284140,4010, -2005-07,2006-01,2609,2650,11842850,4469, -2005-07,2006-07,2630,2640,11806080,4472, -2005-07,2007-01,2634,2639,11788413,4467, -2005-07,2007-07,2634,2638,11770756,4462, -2005-07,2008-01,2634,2636,11769740,4465,11765000 -2006-01,2006-01,2261,2808,11945232,4254, -2006-01,2006-07,2671,2712,11856864,4372, -2006-01,2007-01,2694,2704,11819184,4371, -2006-01,2007-07,2696,2701,11773659,4359, -2006-01,2008-01,2697,2700,11761200,4356,11755000 -2006-07,2006-07,1949,2799,12503133,4467, -2006-07,2007-01,2637,2675,12762425,4771, -2006-07,2007-07,2659,2670,12706530,4759, -2006-07,2008-01,2662,2668,12697012,4759,12679000 -2007-01,2007-01,2059,2578,11662872,4524, -2007-01,2007-07,2496,2533,11522617,4549, -2007-01,2008-01,2520,2529,11491776,4544,11406000 -2007-07,2007-07,2083,2791,12646021,4531, -2007-07,2008-01,2732,2778,12853806,4627,12648000 -2008-01,2008-01,2533,3139,14072137,4483,11833000 +Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims +2003-07,2003-07,2547,3556,14234668,4003, +2003-07,2004-01,3262,3314,14959396,4514, +2003-07,2004-07,3287,3301,14920520,4520, +2003-07,2005-01,3291,3299,14911480,4520, +2003-07,2005-07,3292,3295,14926350,4530, +2003-07,2006-01,3292,3294,14862528,4512, +2003-07,2006-07,3292,3293,14861309,4513, +2003-07,2007-01,3292,3293,14854723,4511, +2003-07,2007-07,3292,3293,14851430,4510, +2003-07,2008-01,3292,3292,14846920,4510,14846000 +2004-01,2004-01,2791,3492,14547672,4166, +2004-01,2004-07,3217,3262,14672476,4498, +2004-01,2005-01,3240,3250,14644500,4506, +2004-01,2005-07,3242,3247,14627735,4505, +2004-01,2006-01,3243,3247,14621241,4503, +2004-01,2006-07,3243,3245,14608990,4502, +2004-01,2007-01,3243,3245,14608990,4502, +2004-01,2007-07,3243,3244,14610976,4504, +2004-01,2008-01,3242,3243,14616201,4507,14614000 +2004-07,2004-07,2099,2980,12128600,4070, +2004-07,2005-01,2677,2712,12575544,4637, +2004-07,2005-07,2695,2704,12541152,4638, +2004-07,2006-01,2697,2702,12531876,4638, +2004-07,2006-07,2697,2700,12522600,4638, +2004-07,2007-01,2698,2700,12522600,4638, +2004-07,2007-07,2698,2699,12509865,4635, +2004-07,2008-01,2698,2699,12501768,4632,12502000 +2005-01,2005-01,2370,2896,11980752,4137, +2005-01,2005-07,2735,2768,11921776,4307, +2005-01,2006-01,2751,2761,11883344,4304, +2005-01,2006-07,2754,2758,11862158,4301, +2005-01,2007-01,2755,2758,11853884,4298, +2005-01,2007-07,2755,2758,11842852,4294, +2005-01,2008-01,2756,2757,11841315,4295,11840000 +2005-07,2005-07,1966,2814,11284140,4010, +2005-07,2006-01,2609,2650,11842850,4469, +2005-07,2006-07,2630,2640,11806080,4472, +2005-07,2007-01,2634,2639,11788413,4467, +2005-07,2007-07,2634,2638,11770756,4462, +2005-07,2008-01,2634,2636,11769740,4465,11765000 +2006-01,2006-01,2261,2808,11945232,4254, +2006-01,2006-07,2671,2712,11856864,4372, +2006-01,2007-01,2694,2704,11819184,4371, +2006-01,2007-07,2696,2701,11773659,4359, +2006-01,2008-01,2697,2700,11761200,4356,11755000 +2006-07,2006-07,1949,2799,12503133,4467, +2006-07,2007-01,2637,2675,12762425,4771, +2006-07,2007-07,2659,2670,12706530,4759, +2006-07,2008-01,2662,2668,12697012,4759,12679000 +2007-01,2007-01,2059,2578,11662872,4524, +2007-01,2007-07,2496,2533,11522617,4549, +2007-01,2008-01,2520,2529,11491776,4544,11406000 +2007-07,2007-07,2083,2791,12646021,4531, +2007-07,2008-01,2732,2778,12853806,4627,12648000 +2008-01,2008-01,2533,3139,14072137,4483,11833000 diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 722a047d7..7adecde2b 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -47,7 +47,8 @@ " exh_df:pd.DataFrame,\n", " value_cols: list[str] = [],\n", " factor_cols: list[str] = [],\n", - " other_formats: dict = {}\n", + " other_formats: dict = {},\n", + " date_format: str = \"{:%Y}\"\n", "):\n", " return exh_df \\\n", " .reset_index() \\\n", @@ -56,7 +57,7 @@ " .hide(axis='index') \\\n", " .set_properties(**{\"text-align\":\"right\"}) \\\n", " .format({\n", - " 'Accident Year':\"{:%Y}\",\n", + " 'Accident Year':date_format,\n", " **{x:\"{:,.0f}\" for x in value_cols},\n", " **{x:\"{:,.3f}\" for x in factor_cols},\n", " **other_formats\n", @@ -245,7 +246,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e1_ccc_selected.ldf_)\n", @@ -294,7 +295,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e1_rcc_selected.ldf_)\n", @@ -362,7 +363,9 @@ " [\n", " 'Closed CDF',\n", " 'Reported CDF',\n", - " ]\n", + " ],\n", + " {},\n", + " \"{:%Y%m}\"\n", ") " ] }, @@ -480,7 +483,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factors')\n", - "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e1_rsev_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -539,7 +542,9 @@ " 'Selected Ult CC',\n", " 'Ult Claims',\n", " ],\n", - " ['CDF']\n", + " ['CDF'],\n", + " {},\n", + " \"{:%Y%m}\"\n", ")" ] }, @@ -578,7 +583,7 @@ "outputs": [], "source": [ "e1_s8 = summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)\n", - "format_exh(e1_s8,e1_s8.columns)" + "format_exh(e1_s8,e1_s8.columns,[],{},\"{:%Y%m}\")" ] }, { @@ -750,7 +755,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e2_ccc_selected.ldf_)\n", @@ -800,7 +805,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e2_rcc_selected.ldf_)\n", @@ -952,7 +957,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factors')\n", - "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e2_rsev_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -1237,7 +1242,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_ccc_selected.ldf_)\n", @@ -1286,7 +1291,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e3_rcc_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -1499,7 +1504,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_psev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_psev_selected.ldf_)\n", @@ -1546,7 +1551,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_rsev_selected.ldf_)\n", @@ -1600,10 +1605,20 @@ ")\n", "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", - "e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)\n", - "e3_s8.iloc[:-2,:][['Age','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" + "format_exh(\n", + " e3_s8.iloc[:-2,:],\n", + " [\n", + " 'Paid Sev',\n", + " 'Reported Sev',\n", + " 'Ult Paid Sev',\n", + " 'Ult Reported Sev',\n", + " 'Selected Ult Sev',\n", + " ],\n", + " [\n", + " 'Paid CDF',\n", + " 'Reported CDF',\n", + " ],\n", + ")" ] }, { @@ -1646,12 +1661,21 @@ "e3_s9[['Sev Trend']] = e3_s9[['Sev Trend']].round(3)\n", "#using floating point offset to achieve standard rounding\n", "e3_s9[['Trended Ult Sev']] = (e3_s9[['Trended Ult Sev']] + 1e-9).round(0)\n", - "nb_display(e3_s9.iloc[:-2,:])\n", + "nb_display(format_exh(\n", + " e3_s9.iloc[:-2,:],\n", + " [\n", + " 'Selected Ult Sev',\n", + " 'Trended Ult Sev',\n", + " ],\n", + " [\n", + " 'Sev Trend',\n", + " ],\n", + "))\n", "print('Average Trended Severity at 2008 Cost Level')\n", "for k,v in e3_sevs.items():\n", - " print(f\"\\t{k:<20} \\t{v.round(0)}\")\n", - "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:.0f}\")\n", - "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:.0f}\")" + " print(f\"\\t{k:<20} \\t{v:,.0f}\")\n", + "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:,.0f}\")\n", + "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:,.0f}\")" ] }, { @@ -1698,7 +1722,22 @@ "e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']\n", "e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']\n", "e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']\n", - "e3_s10.T" + "format_exh(\n", + " e3_s10,\n", + " [\n", + " 'Payroll',\n", + " 'Ult CC',\n", + " 'Selected Severity',\n", + " 'Ult Claims',\n", + " 'Reported Claims',\n", + " 'Paid Claims',\n", + " 'Case Outstanding',\n", + " 'IBNR',\n", + " 'Unpaid'\n", + " ],\n", + " [],\n", + " {'Selected Frequency':'{:.2%}'}\n", + ")" ] }, { @@ -2186,7 +2225,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e5_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e5_ccc_selected.ldf_)\n", @@ -2235,7 +2274,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e5_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e5_rcc_selected.ldf_)\n", "print('CDF to Ultimate')\n", From 62743b073db74f6206d9c84dcb08eb0a7d76582f Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 06:41:56 +0000 Subject: [PATCH 18/27] updating exhibit --- docs/friedland/chapter_11.ipynb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 722a047d7..d63e79ea8 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -1701,6 +1701,27 @@ "e3_s10.T" ] }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "e3_s4['Trended Ultimate Freq']" + ] + }, { "cell_type": "code", "execution_count": 0, From 1124554133135491cefb9b7c2f0a7cf80ceb0100 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 07:37:57 +0000 Subject: [PATCH 19/27] removing a debugging cell --- docs/friedland/chapter_11.ipynb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index d63e79ea8..722a047d7 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -1701,27 +1701,6 @@ "e3_s10.T" ] }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "21d21bb5-5e66-4ac4-bdf0-c6535ecceeba", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "e3_s4['Trended Ultimate Freq']" - ] - }, { "cell_type": "code", "execution_count": 0, From fe37feb14816571d931059cc04ea6312d9d9d723 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 08:32:25 +0000 Subject: [PATCH 20/27] more formatting --- .../utils/data/friedland_auto_freq_sev.csv | 112 +++++++++--------- docs/friedland/chapter_11.ipynb | 91 ++++++++++---- 2 files changed, 121 insertions(+), 82 deletions(-) diff --git a/chainladder/utils/data/friedland_auto_freq_sev.csv b/chainladder/utils/data/friedland_auto_freq_sev.csv index 6a22a655a..907eb3c0d 100644 --- a/chainladder/utils/data/friedland_auto_freq_sev.csv +++ b/chainladder/utils/data/friedland_auto_freq_sev.csv @@ -1,56 +1,56 @@ -Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims -2003-07,2003-07,2547,3556,14234668,4003, -2003-07,2004-01,3262,3314,14959396,4514, -2003-07,2004-07,3287,3301,14920520,4520, -2003-07,2005-01,3291,3299,14911480,4520, -2003-07,2005-07,3292,3295,14926350,4530, -2003-07,2006-01,3292,3294,14862528,4512, -2003-07,2006-07,3292,3293,14861309,4513, -2003-07,2007-01,3292,3293,14854723,4511, -2003-07,2007-07,3292,3293,14851430,4510, -2003-07,2008-01,3292,3292,14846920,4510,14846000 -2004-01,2004-01,2791,3492,14547672,4166, -2004-01,2004-07,3217,3262,14672476,4498, -2004-01,2005-01,3240,3250,14644500,4506, -2004-01,2005-07,3242,3247,14627735,4505, -2004-01,2006-01,3243,3247,14621241,4503, -2004-01,2006-07,3243,3245,14608990,4502, -2004-01,2007-01,3243,3245,14608990,4502, -2004-01,2007-07,3243,3244,14610976,4504, -2004-01,2008-01,3242,3243,14616201,4507,14614000 -2004-07,2004-07,2099,2980,12128600,4070, -2004-07,2005-01,2677,2712,12575544,4637, -2004-07,2005-07,2695,2704,12541152,4638, -2004-07,2006-01,2697,2702,12531876,4638, -2004-07,2006-07,2697,2700,12522600,4683, -2004-07,2007-01,2698,2700,12522600,4638, -2004-07,2007-07,2698,2699,12509865,4635, -2004-07,2008-01,2698,2699,12501768,4632,12502000 -2005-01,2005-01,2370,2896,11980752,4137, -2005-01,2005-07,2735,2768,11921776,4307, -2005-01,2006-01,2751,2761,11883344,4304, -2005-01,2006-07,2754,2758,11862158,4301, -2005-01,2007-01,2755,2758,11853884,4298, -2005-01,2007-07,2755,2758,11842852,4294, -2005-01,2008-01,2756,2757,11841315,4295,11840000 -2005-07,2005-07,1966,2814,11284140,4010, -2005-07,2006-01,2609,2650,11842850,4469, -2005-07,2006-07,2630,2640,11806080,4472, -2005-07,2007-01,2634,2639,11788413,4467, -2005-07,2007-07,2634,2638,11770756,4462, -2005-07,2008-01,2634,2636,11769740,4465,11765000 -2006-01,2006-01,2261,2808,11945232,4254, -2006-01,2006-07,2671,2712,11856864,4372, -2006-01,2007-01,2694,2704,11819184,4371, -2006-01,2007-07,2696,2701,11773659,4359, -2006-01,2008-01,2697,2700,11761200,4356,11755000 -2006-07,2006-07,1949,2799,12503133,4467, -2006-07,2007-01,2637,2675,12762425,4771, -2006-07,2007-07,2659,2670,12706530,4759, -2006-07,2008-01,2662,2668,12697012,4759,12679000 -2007-01,2007-01,2059,2578,11662872,4524, -2007-01,2007-07,2496,2533,11522617,4549, -2007-01,2008-01,2520,2529,11491776,4544,11406000 -2007-07,2007-07,2083,2791,12646021,4531, -2007-07,2008-01,2732,2778,12853806,4627,12648000 -2008-01,2008-01,2533,3139,14072137,4483,11833000 +Accident Half-Year,Calendar Half-Year,Closed Claim Counts,Reported Claim Counts,Reported Claims,Reported Severity,Paid Claims +2003-07,2003-07,2547,3556,14234668,4003, +2003-07,2004-01,3262,3314,14959396,4514, +2003-07,2004-07,3287,3301,14920520,4520, +2003-07,2005-01,3291,3299,14911480,4520, +2003-07,2005-07,3292,3295,14926350,4530, +2003-07,2006-01,3292,3294,14862528,4512, +2003-07,2006-07,3292,3293,14861309,4513, +2003-07,2007-01,3292,3293,14854723,4511, +2003-07,2007-07,3292,3293,14851430,4510, +2003-07,2008-01,3292,3292,14846920,4510,14846000 +2004-01,2004-01,2791,3492,14547672,4166, +2004-01,2004-07,3217,3262,14672476,4498, +2004-01,2005-01,3240,3250,14644500,4506, +2004-01,2005-07,3242,3247,14627735,4505, +2004-01,2006-01,3243,3247,14621241,4503, +2004-01,2006-07,3243,3245,14608990,4502, +2004-01,2007-01,3243,3245,14608990,4502, +2004-01,2007-07,3243,3244,14610976,4504, +2004-01,2008-01,3242,3243,14616201,4507,14614000 +2004-07,2004-07,2099,2980,12128600,4070, +2004-07,2005-01,2677,2712,12575544,4637, +2004-07,2005-07,2695,2704,12541152,4638, +2004-07,2006-01,2697,2702,12531876,4638, +2004-07,2006-07,2697,2700,12522600,4638, +2004-07,2007-01,2698,2700,12522600,4638, +2004-07,2007-07,2698,2699,12509865,4635, +2004-07,2008-01,2698,2699,12501768,4632,12502000 +2005-01,2005-01,2370,2896,11980752,4137, +2005-01,2005-07,2735,2768,11921776,4307, +2005-01,2006-01,2751,2761,11883344,4304, +2005-01,2006-07,2754,2758,11862158,4301, +2005-01,2007-01,2755,2758,11853884,4298, +2005-01,2007-07,2755,2758,11842852,4294, +2005-01,2008-01,2756,2757,11841315,4295,11840000 +2005-07,2005-07,1966,2814,11284140,4010, +2005-07,2006-01,2609,2650,11842850,4469, +2005-07,2006-07,2630,2640,11806080,4472, +2005-07,2007-01,2634,2639,11788413,4467, +2005-07,2007-07,2634,2638,11770756,4462, +2005-07,2008-01,2634,2636,11769740,4465,11765000 +2006-01,2006-01,2261,2808,11945232,4254, +2006-01,2006-07,2671,2712,11856864,4372, +2006-01,2007-01,2694,2704,11819184,4371, +2006-01,2007-07,2696,2701,11773659,4359, +2006-01,2008-01,2697,2700,11761200,4356,11755000 +2006-07,2006-07,1949,2799,12503133,4467, +2006-07,2007-01,2637,2675,12762425,4771, +2006-07,2007-07,2659,2670,12706530,4759, +2006-07,2008-01,2662,2668,12697012,4759,12679000 +2007-01,2007-01,2059,2578,11662872,4524, +2007-01,2007-07,2496,2533,11522617,4549, +2007-01,2008-01,2520,2529,11491776,4544,11406000 +2007-07,2007-07,2083,2791,12646021,4531, +2007-07,2008-01,2732,2778,12853806,4627,12648000 +2008-01,2008-01,2533,3139,14072137,4483,11833000 diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 722a047d7..7adecde2b 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -47,7 +47,8 @@ " exh_df:pd.DataFrame,\n", " value_cols: list[str] = [],\n", " factor_cols: list[str] = [],\n", - " other_formats: dict = {}\n", + " other_formats: dict = {},\n", + " date_format: str = \"{:%Y}\"\n", "):\n", " return exh_df \\\n", " .reset_index() \\\n", @@ -56,7 +57,7 @@ " .hide(axis='index') \\\n", " .set_properties(**{\"text-align\":\"right\"}) \\\n", " .format({\n", - " 'Accident Year':\"{:%Y}\",\n", + " 'Accident Year':date_format,\n", " **{x:\"{:,.0f}\" for x in value_cols},\n", " **{x:\"{:,.3f}\" for x in factor_cols},\n", " **other_formats\n", @@ -245,7 +246,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e1_ccc_selected.ldf_)\n", @@ -294,7 +295,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e1_rcc_selected.ldf_)\n", @@ -362,7 +363,9 @@ " [\n", " 'Closed CDF',\n", " 'Reported CDF',\n", - " ]\n", + " ],\n", + " {},\n", + " \"{:%Y%m}\"\n", ") " ] }, @@ -480,7 +483,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e1_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factors')\n", - "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e1_rsev_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -539,7 +542,9 @@ " 'Selected Ult CC',\n", " 'Ult Claims',\n", " ],\n", - " ['CDF']\n", + " ['CDF'],\n", + " {},\n", + " \"{:%Y%m}\"\n", ")" ] }, @@ -578,7 +583,7 @@ "outputs": [], "source": [ "e1_s8 = summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)\n", - "format_exh(e1_s8,e1_s8.columns)" + "format_exh(e1_s8,e1_s8.columns,[],{},\"{:%Y%m}\")" ] }, { @@ -750,7 +755,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e2_ccc_selected.ldf_)\n", @@ -800,7 +805,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e2_rcc_selected.ldf_)\n", @@ -952,7 +957,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e2_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factors')\n", - "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e2_rsev_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -1237,7 +1242,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_ccc_selected.ldf_)\n", @@ -1286,7 +1291,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e3_rcc_selected.ldf_)\n", "print('CDF to Ultimate')\n", @@ -1499,7 +1504,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_psev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_psev_selected.ldf_)\n", @@ -1546,7 +1551,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e3_rsev.age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e3_rsev_selected.ldf_)\n", @@ -1600,10 +1605,20 @@ ")\n", "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", - "e3_s8[['Paid CDF','Reported CDF']] = e3_s8[['Paid CDF','Reported CDF']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] = (e3_s8[['Paid Sev','Reported Sev','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']] + 1e-9).round(0)\n", - "e3_s8.iloc[:-2,:][['Age','Paid Sev','Reported Sev','Paid CDF','Reported CDF','Ult Paid Sev','Ult Reported Sev','Selected Ult Sev']]" + "format_exh(\n", + " e3_s8.iloc[:-2,:],\n", + " [\n", + " 'Paid Sev',\n", + " 'Reported Sev',\n", + " 'Ult Paid Sev',\n", + " 'Ult Reported Sev',\n", + " 'Selected Ult Sev',\n", + " ],\n", + " [\n", + " 'Paid CDF',\n", + " 'Reported CDF',\n", + " ],\n", + ")" ] }, { @@ -1646,12 +1661,21 @@ "e3_s9[['Sev Trend']] = e3_s9[['Sev Trend']].round(3)\n", "#using floating point offset to achieve standard rounding\n", "e3_s9[['Trended Ult Sev']] = (e3_s9[['Trended Ult Sev']] + 1e-9).round(0)\n", - "nb_display(e3_s9.iloc[:-2,:])\n", + "nb_display(format_exh(\n", + " e3_s9.iloc[:-2,:],\n", + " [\n", + " 'Selected Ult Sev',\n", + " 'Trended Ult Sev',\n", + " ],\n", + " [\n", + " 'Sev Trend',\n", + " ],\n", + "))\n", "print('Average Trended Severity at 2008 Cost Level')\n", "for k,v in e3_sevs.items():\n", - " print(f\"\\t{k:<20} \\t{v.round(0)}\")\n", - "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:.0f}\")\n", - "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:.0f}\")" + " print(f\"\\t{k:<20} \\t{v:,.0f}\")\n", + "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:,.0f}\")\n", + "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:,.0f}\")" ] }, { @@ -1698,7 +1722,22 @@ "e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']\n", "e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']\n", "e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']\n", - "e3_s10.T" + "format_exh(\n", + " e3_s10,\n", + " [\n", + " 'Payroll',\n", + " 'Ult CC',\n", + " 'Selected Severity',\n", + " 'Ult Claims',\n", + " 'Reported Claims',\n", + " 'Paid Claims',\n", + " 'Case Outstanding',\n", + " 'IBNR',\n", + " 'Unpaid'\n", + " ],\n", + " [],\n", + " {'Selected Frequency':'{:.2%}'}\n", + ")" ] }, { @@ -2186,7 +2225,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e5_tri['Closed Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "print('Selected')\n", "nb_display(e5_ccc_selected.ldf_)\n", @@ -2235,7 +2274,7 @@ "print('PART 2 - Age-to-Age Factors')\n", "nb_display(e5_tri['Reported Claim Counts'].age_to_age.round(3))\n", "print('PART 3 - Average Age-to-Age Factor')\n", - "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame())\n", + "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", "print('PART 4 - Selected Age-to-Age Factors')\n", "nb_display(e5_rcc_selected.ldf_)\n", "print('CDF to Ultimate')\n", From dd18092032ec817bd6ead760564e2f51ba43c840 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Mon, 3 Aug 2026 16:24:38 +0000 Subject: [PATCH 21/27] fixing doctest --- docs/friedland/chapter_11.ipynb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 7adecde2b..de5953adc 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -1786,7 +1786,7 @@ "assert np.allclose(\n", " e3_s8['Selected Ult Sev'].values[:-2],\n", " np.array([4371, 4587, 4963, 5242, 5635, 6169]),\n", - " atol=1\n", + " rtol=.001\n", ")\n", "#Exhibit III Sheet 9\n", "assert np.allclose(\n", @@ -1852,7 +1852,8 @@ " },\n", " origin='origin',\n", " development='valuation',\n", - " columns='On-Level Adjustment'\n", + " columns='On-Level Adjustment',\n", + " cumulative = True\n", ")\n", "e4_freq_assumptions = {}\n", "e4_freq_assumptions['all_years'] = {}\n", From 51ad44329aefc11c2a5587c44205988f08b0f4c9 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 22:06:02 +0000 Subject: [PATCH 22/27] ruff fix --- chainladder/core/base.py | 2 +- chainladder/utils/data/_manifest.py | 11 ++++++++++- chainladder/utils/tests/test_utilities.py | 14 ++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/chainladder/core/base.py b/chainladder/core/base.py index 40240a3f3..5ef311374 100644 --- a/chainladder/core/base.py +++ b/chainladder/core/base.py @@ -601,7 +601,7 @@ def _drop_subtriangles(self): del self.sigma_ if "std_err_" in sub_tris: del self.std_err_ - #w_ is currently stored as an ndarray + # w_ is currently stored as an ndarray if hasattr(self, "w_"): del self.w_ diff --git a/chainladder/utils/data/_manifest.py b/chainladder/utils/data/_manifest.py index 854551ab6..b16b293c7 100644 --- a/chainladder/utils/data/_manifest.py +++ b/chainladder/utils/data/_manifest.py @@ -268,7 +268,16 @@ "origin": "Accident Year", "development": "Calendar Year", "index": None, - "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium","Incremental Paid Severity"], + "columns": [ + "Paid Claims", + "Reported Claims", + "Closed Claim Counts", + "Reported Claim Counts", + "Case Outstanding", + "Reported Severities", + "Earned Premium", + "Incremental Paid Severity" + ], "cumulative": True, }, "genins": { diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 752af77d9..484c6ae3b 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -12,7 +12,6 @@ from chainladder import ( __dt64_unit__ ) -from chainladder.utils.utility_functions import date_delta_adjustment from chainladder.utils.data._manifest import SAMPLES from chainladder.utils.utility_functions import ( date_delta_adjustment, @@ -59,7 +58,6 @@ def from_sequence(seq): def test_triangle_json_io(clrd): - xp = clrd.get_array_module() clrd2 = cl.read_json(clrd.to_json(), array_backend=clrd.array_backend) assert clrd == clrd2 assert np.all(clrd.kdims == clrd2.kdims) @@ -153,7 +151,7 @@ def test_concat(clrd): ) -def test_model_diagnostics_erorr(raa,atol): +def test_model_diagnostics_erorr(raa, atol): with pytest.raises(ValueError): cl.model_diagnostics(raa) dev = cl.Development().fit_transform(raa) @@ -162,7 +160,7 @@ def test_model_diagnostics_erorr(raa,atol): md = cl.model_diagnostics(est) assert np.allclose( md['Run Off 1'].values, - emerg[emerg.valuation.year==1991].latest_diagonal.values, + emerg[emerg.valuation.year == 1991].latest_diagonal.values, atol=atol, equal_nan=True ) @@ -186,13 +184,13 @@ def test_model_diagnostics_erorr(raa,atol): ) -def test_model_diagnostics_groupby(prism,atol): +def test_model_diagnostics_groupby(prism, atol): dev = cl.Development().fit(prism["Incurred"].sum()) est = cl.Chainladder().fit(dev.transform(prism["Incurred"])) - lhs = cl.model_diagnostics(est,groupby=['Line']) + lhs = cl.model_diagnostics(est, groupby=['Line']) rhs = cl.model_diagnostics(cl.Chainladder().fit(dev.transform(prism["Incurred"].groupby('Line').sum()))) - assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol,equal_nan=True) - assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol,equal_nan=True) + assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol, equal_nan=True) + assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) def test_concat_immutability(raa): From efaec4cb4f0aa8c10e1208d64b75df3dd394a039 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 22:06:02 +0000 Subject: [PATCH 23/27] ruff fix --- chainladder/core/base.py | 2 +- chainladder/utils/data/_manifest.py | 11 ++++++++++- chainladder/utils/tests/test_utilities.py | 14 ++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/chainladder/core/base.py b/chainladder/core/base.py index a1a88c0b2..29f05f4e7 100644 --- a/chainladder/core/base.py +++ b/chainladder/core/base.py @@ -705,7 +705,7 @@ def _drop_subtriangles(self): del self.sigma_ if "std_err_" in sub_tris: del self.std_err_ - #w_ is currently stored as an ndarray + # w_ is currently stored as an ndarray if hasattr(self, "w_"): del self.w_ diff --git a/chainladder/utils/data/_manifest.py b/chainladder/utils/data/_manifest.py index d07f77d31..b4993bfda 100644 --- a/chainladder/utils/data/_manifest.py +++ b/chainladder/utils/data/_manifest.py @@ -268,7 +268,16 @@ "origin": "Accident Year", "development": "Calendar Year", "index": None, - "columns": ["Paid Claims", "Reported Claims", "Closed Claim Counts", "Reported Claim Counts", "Case Outstanding", "Reported Severities", "Earned Premium","Incremental Paid Severity"], + "columns": [ + "Paid Claims", + "Reported Claims", + "Closed Claim Counts", + "Reported Claim Counts", + "Case Outstanding", + "Reported Severities", + "Earned Premium", + "Incremental Paid Severity" + ], "cumulative": True, }, "genins": { diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 752af77d9..484c6ae3b 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -12,7 +12,6 @@ from chainladder import ( __dt64_unit__ ) -from chainladder.utils.utility_functions import date_delta_adjustment from chainladder.utils.data._manifest import SAMPLES from chainladder.utils.utility_functions import ( date_delta_adjustment, @@ -59,7 +58,6 @@ def from_sequence(seq): def test_triangle_json_io(clrd): - xp = clrd.get_array_module() clrd2 = cl.read_json(clrd.to_json(), array_backend=clrd.array_backend) assert clrd == clrd2 assert np.all(clrd.kdims == clrd2.kdims) @@ -153,7 +151,7 @@ def test_concat(clrd): ) -def test_model_diagnostics_erorr(raa,atol): +def test_model_diagnostics_erorr(raa, atol): with pytest.raises(ValueError): cl.model_diagnostics(raa) dev = cl.Development().fit_transform(raa) @@ -162,7 +160,7 @@ def test_model_diagnostics_erorr(raa,atol): md = cl.model_diagnostics(est) assert np.allclose( md['Run Off 1'].values, - emerg[emerg.valuation.year==1991].latest_diagonal.values, + emerg[emerg.valuation.year == 1991].latest_diagonal.values, atol=atol, equal_nan=True ) @@ -186,13 +184,13 @@ def test_model_diagnostics_erorr(raa,atol): ) -def test_model_diagnostics_groupby(prism,atol): +def test_model_diagnostics_groupby(prism, atol): dev = cl.Development().fit(prism["Incurred"].sum()) est = cl.Chainladder().fit(dev.transform(prism["Incurred"])) - lhs = cl.model_diagnostics(est,groupby=['Line']) + lhs = cl.model_diagnostics(est, groupby=['Line']) rhs = cl.model_diagnostics(cl.Chainladder().fit(dev.transform(prism["Incurred"].groupby('Line').sum()))) - assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol,equal_nan=True) - assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol,equal_nan=True) + assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol, equal_nan=True) + assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) def test_concat_immutability(raa): From f7d59a467f4c1b7cae7e7d0da97e1bca3e19e44c Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 22:32:43 +0000 Subject: [PATCH 24/27] ruff fix --- chainladder/utils/tests/test_utilities.py | 18 +- chainladder/utils/triangle_weight.py | 25 +- chainladder/utils/weighted_regression.py | 6 +- docs/friedland/chapter_11.ipynb | 1852 ++++++++++++--------- 4 files changed, 1115 insertions(+), 786 deletions(-) diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 484c6ae3b..5f33c24ee 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -189,16 +189,16 @@ def test_model_diagnostics_groupby(prism, atol): est = cl.Chainladder().fit(dev.transform(prism["Incurred"])) lhs = cl.model_diagnostics(est, groupby=['Line']) rhs = cl.model_diagnostics(cl.Chainladder().fit(dev.transform(prism["Incurred"].groupby('Line').sum()))) - assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol, equal_nan=True) - assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) + assert np.allclose(lhs['Ultimate'].values, rhs['Ultimate'].values,atol=atol, equal_nan=True) + assert np.allclose(np.nan_to_num(lhs['IBNR'].values), np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) def test_concat_immutability(raa): u = cl.Chainladder().fit(raa).ultimate_ - l = raa.latest_diagonal - u.columns = l.columns + latest = raa.latest_diagonal + u.columns = latest.columns u_new = copy.deepcopy(u) - cl.concat((l, u), axis=3) + cl.concat((latest, u), axis=3) assert u == u_new @@ -680,7 +680,7 @@ def test_options_defaults() -> None: """ options = cl.Options() assert options.ARRAY_BACKEND == "numpy" - assert options.AUTO_SPARSE == True + assert options.AUTO_SPARSE assert options.ARRAY_PRIORITY == ["dask", "sparse", "cupy", "numpy"] assert isinstance(options.ULT_VAL, str) @@ -1271,6 +1271,6 @@ def test_triangleweight_full_triangle(raa: Triangle) -> None: Testing new path that allows weights on full triangles ''' ult = cl.Chainladder().fit(raa) - tw = cl.TriangleWeight(n_periods = 4).fit(raa) - tw_full = cl.TriangleWeight(n_periods = 4).fit(ult.full_triangle_) - assert tw.w_.iloc[:,:,:,0] == tw_full.w_.iloc[:,:,:,0] \ No newline at end of file + tw = cl.TriangleWeight(n_periods=4).fit(raa) + tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) + assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] \ No newline at end of file diff --git a/chainladder/utils/triangle_weight.py b/chainladder/utils/triangle_weight.py index d32618409..a122515ce 100644 --- a/chainladder/utils/triangle_weight.py +++ b/chainladder/utils/triangle_weight.py @@ -5,7 +5,6 @@ import numpy as np import pandas as pd -from chainladder.utils.sparse import sp from sklearn.base import BaseEstimator, TransformerMixin import warnings @@ -19,7 +18,7 @@ from chainladder.core.typing import TriangleProtocol -class TriangleWeight(BaseEstimator,TransformerMixin): +class TriangleWeight(BaseEstimator, TransformerMixin): """ Helper class that produces a triangle of weights based on pattern selections @@ -123,7 +122,7 @@ def fit(self, X: TriangleProtocol, y: None = None, sample_weight: None = None): # in practie, there is no realistic use case for a sparse triangle to need weights backend = "numpy" if X.array_backend in ["sparse", "numpy"] else "cupy" obj = X.set_backend(backend) - self.w_ = self._set_weight_func(X=obj,secondary_rank=sample_weight) + self.w_ = self._set_weight_func(X=obj, secondary_rank=sample_weight) return self def transform(self, X: TriangleProtocol) -> Triangle: @@ -146,8 +145,8 @@ def transform(self, X: TriangleProtocol) -> Triangle: def _cascade_param( self, - size:int, - param: bool | int | float | str | None | list[bool|int|float|str|None], + size: int, + param: bool | int | float | str | None | list[bool | int | float | str | None], default_param: bool | int | float | str | None ) -> np.ndarray: """ @@ -237,7 +236,7 @@ def _assign_n_periods_weight_func(self, X: TriangleProtocol) -> np.ndarray: dev_len = X.shape[3] n_periods_param = self._cascade_param(dev_len, self.n_periods, -1) - #helper function that generates the weights for individual n_periods + # helper function that generates the weights for individual n_periods def _assign_n_periods_weight_int(X, n_periods): xp = X.get_array_module() val_offset = { @@ -250,10 +249,10 @@ def _assign_n_periods_weight_int(X, n_periods): return X.values * 0 + 1 else: z = -n_periods * val_offset[X.development_grain][X.origin_grain] - #adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) + # adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) if X.is_full: w = X.copy() - w.values[:,:,:-n_periods,:] = np.nan + w.values[:, :, :-n_periods, :] = np.nan else: val_date_min = X.valuation[X.valuation <= X.valuation_date] val_date_min = val_date_min.drop_duplicates().sort_values() @@ -346,8 +345,8 @@ def _drop_n_func( # getting weights that are within the max and min ranks w = ( - X_ranks < max_rank[:,:,None,:] - ) & (X_ranks > min_rank[:,:,None,:] - 1) + X_ranks < max_rank[:, :, None, :] + ) & (X_ranks > min_rank[:, :, None, :] - 1) # NOTE: The "Some exclusions have been ignored..." UserWarning below is # asserted by the test suite (see chainladder/development/tests/ @@ -484,8 +483,8 @@ def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: w = ~np.isnan(X_val) # weights without considering preserve - index_array_weights = (X_val < drop_above_array[:,:,None,:]) & ( - X_val > drop_below_array[:,:,None,:] + index_array_weights = (X_val < drop_above_array[:, :, None, :]) & ( + X_val > drop_below_array[:, :, None, :] ) # counting remaining factors @@ -494,7 +493,7 @@ def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: # applying preserve warning_flag = np.any(valid_count < preserve_array) w = np.where( - valid_count[:,:,None,:] < preserve_array[:,:,None,:], w, index_array_weights + valid_count[:, :, None, :] < preserve_array[:, :, None, :], w, index_array_weights ) # NOTE: The "Some exclusions have been ignored..." UserWarning below is diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 9e118c946..1b6aec586 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -65,9 +65,9 @@ def infer_x_w(self): def fit( self, - X:BackendArray, - y:BackendArray|None=None, - sample_weight:BackendArray|None=None, + X: BackendArray, + y: BackendArray | None = None, + sample_weight: BackendArray | None = None, average: Literal["volume", "simple", "regression", "geometric"] | None = None ): """ diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index de5953adc..4a270d4f0 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -43,86 +43,106 @@ "import chainladder as cl\n", "from IPython.display import display as nb_display\n", "\n", + "\n", "def format_exh(\n", - " exh_df:pd.DataFrame,\n", - " value_cols: list[str] = [],\n", - " factor_cols: list[str] = [],\n", - " other_formats: dict = {},\n", - " date_format: str = \"{:%Y}\"\n", + " exh_df: pd.DataFrame,\n", + " value_cols: list[str] = [],\n", + " factor_cols: list[str] = [],\n", + " other_formats: dict = {},\n", + " date_format: str = \"{:%Y}\",\n", "):\n", - " return exh_df \\\n", - " .reset_index() \\\n", - " .rename(columns={'origin':'Accident Year'}) \\\n", - " .style \\\n", - " .hide(axis='index') \\\n", - " .set_properties(**{\"text-align\":\"right\"}) \\\n", - " .format({\n", - " 'Accident Year':date_format,\n", - " **{x:\"{:,.0f}\" for x in value_cols},\n", - " **{x:\"{:,.3f}\" for x in factor_cols},\n", - " **other_formats\n", - " }) \n", + " return (\n", + " exh_df.reset_index()\n", + " .rename(columns={\"origin\": \"Accident Year\"})\n", + " .style.hide(axis=\"index\")\n", + " .set_properties(**{\"text-align\": \"right\"})\n", + " .format(\n", + " {\n", + " \"Accident Year\": date_format,\n", + " **{x: \"{:,.0f}\" for x in value_cols},\n", + " **{x: \"{:,.3f}\" for x in factor_cols},\n", + " **other_formats,\n", + " }\n", + " )\n", + " )\n", "\n", - "#create reported/paid summary exhibit\n", - "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", + "\n", + "# create reported/paid summary exhibit\n", + "def summary_exh(\n", + " reported_tri: cl.Triangle, paid_tri: cl.Triangle, ult: cl.Triangle\n", + ") -> pd.DataFrame:\n", " output = reported_tri.latest_diagonal.to_frame()\n", - " output.columns = ['Reported']\n", - " output['Paid'] = paid_tri.latest_diagonal.to_frame()\n", - " #using floating point offset to achieve standard rounding\n", - " output['Ult Claims'] = ult.latest_diagonal.to_frame()\n", - " output['Case Outstanding'] = output[\"Reported\"] - output[\"Paid\"]\n", - " output['IBNR'] = output[\"Ult Claims\"] - output[\"Reported\"]\n", - " output['Unpaid'] = output[\"Ult Claims\"] - output[\"Paid\"]\n", - " output.index.name = 'origin'\n", + " output.columns = [\"Reported\"]\n", + " output[\"Paid\"] = paid_tri.latest_diagonal.to_frame()\n", + " # using floating point offset to achieve standard rounding\n", + " output[\"Ult Claims\"] = ult.latest_diagonal.to_frame()\n", + " output[\"Case Outstanding\"] = output[\"Reported\"] - output[\"Paid\"]\n", + " output[\"IBNR\"] = output[\"Ult Claims\"] - output[\"Reported\"]\n", + " output[\"Unpaid\"] = output[\"Ult Claims\"] - output[\"Paid\"]\n", + " output.index.name = \"origin\"\n", " return output\n", "\n", - "#create a dict of developed triangles for each of the selection assumptions on a given page\n", - "def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", - " return {k:cl.Development(**v).fit_transform(tri) for k,v in avg_params.items()}\n", "\n", - "#combine a dict of triangles into a singla triangle\n", + "# create a dict of developed triangles for each of the selection assumptions on a given page\n", + "def average_dev(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.Triangle]:\n", + " return {k: cl.Development(**v).fit_transform(tri) for k, v in avg_params.items()}\n", + "\n", + "\n", + "# combine a dict of triangles into a singla triangle\n", "def combine_tri(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " avgs = [v.rename('index',k) for k,v in devs.items()]\n", - " return cl.concat(avgs,axis=0)\n", + " avgs = [v.rename(\"index\", k) for k, v in devs.items()]\n", + " return cl.concat(avgs, axis=0)\n", + "\n", "\n", - "#combine the ldf_ of a dict of triangles into a singla triangle\n", + "# combine the ldf_ of a dict of triangles into a singla triangle\n", "def combine_ldf(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " return combine_tri({k:v.ldf_ for k,v in devs.items()})\n", + " return combine_tri({k: v.ldf_ for k, v in devs.items()})\n", "\n", - "#create a dict of DisposalRate estimators for each of the selection assumptions on a given page\n", - "def average_dr(tri: cl.Triangle, w:cl.Triangle, avg_params: dict[str,int]) -> dict[cl.DisposalRate]:\n", - " return {k:cl.DisposalRate(**v).fit(X=tri,sample_weight = w) for k,v in avg_params.items()}\n", "\n", - "#combine the disposal_rate_ of a dict of triangles into a singla triangle\n", + "# create a dict of DisposalRate estimators for each of the selection assumptions on a given page\n", + "def average_dr(\n", + " tri: cl.Triangle, w: cl.Triangle, avg_params: dict[str, int]\n", + ") -> dict[cl.DisposalRate]:\n", + " return {\n", + " k: cl.DisposalRate(**v).fit(X=tri, sample_weight=w)\n", + " for k, v in avg_params.items()\n", + " }\n", + "\n", + "\n", + "# combine the disposal_rate_ of a dict of triangles into a singla triangle\n", "def combine_disposal(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " return combine_tri({k:v.disposal_rate_ for k,v in devs.items()})\n", + " return combine_tri({k: v.disposal_rate_ for k, v in devs.items()})\n", + "\n", + "\n", + "# create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def regs(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.WeightedRegression]:\n", + " return {\n", + " k: cl.WeightedRegression(\n", + " axis=2, thru_orig=False, xp=tri.get_array_module()\n", + " ).fit(\n", + " np.ones(tri.shape) * tri.origin.year.values[None, None, :, None],\n", + " np.log(tri.values),\n", + " cl.TriangleWeight(**v).fit(tri).w_.values,\n", + " )\n", + " for k, v in avg_params.items()\n", + " }\n", "\n", - "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", - "def regs(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.WeightedRegression]:\n", - " return {k:cl.WeightedRegression(\n", - " axis=2,\n", - " thru_orig=False,\n", - " xp=tri.get_array_module()\n", - " ).fit(\n", - " np.ones(tri.shape) * tri.origin.year.values[None,None,:,None],\n", - " np.log(tri.values),\n", - " cl.TriangleWeight(**v).fit(tri).w_.values\n", - " ) for k,v in avg_params.items()}\n", "\n", - "#combine all the trends and r-squared values into pandas DataFrame\n", - "def reg_outputs(regs: dict[cl.WeightedRegression],devs:pd.Series) -> pd.DataFrame:\n", - " trend = {k:(np.exp(v.slope_)-1).flatten() for k,v in regs.items()}\n", - " trend = pd.DataFrame.from_dict(trend,orient='index')\n", + "# combine all the trends and r-squared values into pandas DataFrame\n", + "def reg_outputs(regs: dict[cl.WeightedRegression], devs: pd.Series) -> pd.DataFrame:\n", + " trend = {k: (np.exp(v.slope_) - 1).flatten() for k, v in regs.items()}\n", + " trend = pd.DataFrame.from_dict(trend, orient=\"index\")\n", " trend.columns = devs\n", - " rsq = {k:v.rsq_.flatten() for k,v in regs.items()}\n", - " rsq = pd.DataFrame.from_dict(rsq,orient='index')\n", + " rsq = {k: v.rsq_.flatten() for k, v in regs.items()}\n", + " rsq = pd.DataFrame.from_dict(rsq, orient=\"index\")\n", " rsq.columns = devs\n", - " return trend,rsq\n", + " return trend, rsq\n", + "\n", "\n", - "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", - "def average_sev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", + "# create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def average_sev(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.Triangle]:\n", " output = {}\n", - " for k,v in avg_params.items():\n", + " for k, v in avg_params.items():\n", " w = cl.TriangleWeight(**v).fit(tri)\n", " weighted_tri = tri * w.w_.values\n", " output[k] = weighted_tri.mean()\n", @@ -167,43 +187,59 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e1_tri = cl.load_sample('friedland_auto_freq_sev')\n", + "# loading data and assumptions\n", + "e1_tri = cl.load_sample(\"friedland_auto_freq_sev\")\n", "e1_cnt_assumptions = {}\n", - "e1_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e1_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e1_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e1_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e1_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e1_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e1_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e1_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e1_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e1_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e1_sev_assumptions = {}\n", - "e1_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e1_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e1_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e1_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e1_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e1_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "\n", - "#developing closed claim counts\n", - "e1_ccc_devs = average_dev(e1_tri['Closed Claim Counts'],e1_cnt_assumptions)\n", - "e1_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_ccc_devs['simple_3'])\n", + "# developing closed claim counts\n", + "e1_ccc_devs = average_dev(e1_tri[\"Closed Claim Counts\"], e1_cnt_assumptions)\n", + "e1_ccc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_ccc_devs[\"simple_3\"]\n", + ")\n", "e1_ccc_selected.ldf_ = e1_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e1_rcc_devs = average_dev(e1_tri['Reported Claim Counts'],e1_cnt_assumptions)\n", - "e1_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rcc_devs['simple_3'])\n", + "# developing reported claim counts\n", + "e1_rcc_devs = average_dev(e1_tri[\"Reported Claim Counts\"], e1_cnt_assumptions)\n", + "e1_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_rcc_devs[\"simple_3\"]\n", + ")\n", "e1_rcc_selected.ldf_ = e1_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e1_ccc_cl = cl.Chainladder().fit(e1_ccc_selected)\n", "e1_rcc_cl = cl.Chainladder().fit(e1_rcc_selected)\n", - "e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_)/2\n", - "e1_cc_ult.iloc[:,:,-1,:] = e1_rcc_cl.ultimate_.iloc[:,:,-1,:]\n", + "e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_) / 2\n", + "e1_cc_ult.iloc[:, :, -1, :] = e1_rcc_cl.ultimate_.iloc[:, :, -1, :]\n", "\n", - "#calculating and developing reported severity\n", - "e1_rsev = e1_tri['Reported Claims'] / e1_tri['Reported Claim Counts']\n", - "e1_rsev_devs = average_dev(e1_rsev,e1_sev_assumptions)\n", - "e1_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rsev_devs['medial_5x1'])\n", + "# calculating and developing reported severity\n", + "e1_rsev = e1_tri[\"Reported Claims\"] / e1_tri[\"Reported Claim Counts\"]\n", + "e1_rsev_devs = average_dev(e1_rsev, e1_sev_assumptions)\n", + "e1_rsev_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_rsev_devs[\"medial_5x1\"]\n", + ")\n", "e1_rsev_selected.ldf_ = e1_rsev_selected.ldf_.round(3)\n", "e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e1_ult = e1_rsev_cl.ultimate_ * e1_cc_ult / 1000" ] }, @@ -241,19 +277,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e1_tri['Closed Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e1_tri[\"Closed Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e1_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e1_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display(1/e1_ccc_selected.cdf_.round(3))" + "print(\"Percent Closed\")\n", + "nb_display(1 / e1_ccc_selected.cdf_.round(3))" ] }, { @@ -290,19 +326,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e1_tri['Reported Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e1_tri[\"Reported Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e1_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e1_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display(1/e1_rcc_selected.cdf_.round(3))" + "print(\"Percent Reported\")\n", + "nb_display(1 / e1_rcc_selected.cdf_.round(3))" ] }, { @@ -339,34 +375,44 @@ }, "outputs": [], "source": [ - "e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e1_ccc_df = (\n", + " cl.model_diagnostics(e1_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_rcc_df = (\n", + " cl.model_diagnostics(e1_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_s3 = e1_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]\n", + "e1_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e1_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e1_s3[\"Selected Ult CC\"] = e1_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e1_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", " ],\n", " {},\n", - " \"{:%Y%m}\"\n", - ") " + " \"{:%Y%m}\",\n", + ")" ] }, { @@ -403,7 +449,7 @@ }, "outputs": [], "source": [ - "(e1_tri['Closed Claim Counts']/e1_tri['Reported Claim Counts']).round(3)" + "(e1_tri[\"Closed Claim Counts\"] / e1_tri[\"Reported Claim Counts\"]).round(3)" ] }, { @@ -440,7 +486,7 @@ }, "outputs": [], "source": [ - "nb_display(e1_tri['Reported Claims']/1000)\n", + "nb_display(e1_tri[\"Reported Claims\"] / 1000)\n", "nb_display(e1_rsev)" ] }, @@ -478,15 +524,15 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e1_rsev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e1_rsev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factors')\n", + "print(\"PART 3 - Average Age-to-Age Factors\")\n", "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e1_rsev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_rsev_selected.cdf_.round(3))" ] }, @@ -524,27 +570,31 @@ }, "outputs": [], "source": [ - "e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e1_rsev_ult_df = (\n", + " cl.model_diagnostics(e1_rsev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_s7 = e1_rsev_ult_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Reported Severity',\n", - " 'Ultimate':'Ult Severity'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Reported Severity\",\n", + " \"Ultimate\": \"Ult Severity\",\n", " }\n", ")\n", - "e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']\n", + "e1_s7[\"Selected Ult CC\"] = e1_s3[\"Selected Ult CC\"]\n", "e1_s7[\"Ult Claims\"] = e1_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e1_s7,\n", " [\n", - " 'Reported Severity',\n", - " 'Ult Severity',\n", - " 'Selected Ult CC',\n", - " 'Ult Claims',\n", + " \"Reported Severity\",\n", + " \"Ult Severity\",\n", + " \"Selected Ult CC\",\n", + " \"Ult Claims\",\n", " ],\n", - " ['CDF'],\n", + " [\"CDF\"],\n", " {},\n", - " \"{:%Y%m}\"\n", + " \"{:%Y%m}\",\n", ")" ] }, @@ -620,23 +670,32 @@ }, "outputs": [], "source": [ - "#Exhibit I Sheet 1\n", - "assert np.all(e1_ccc_selected.cdf_.round(3).values == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 2\n", - "assert np.all(e1_rcc_selected.cdf_.round(3).values == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 3\n", + "# Exhibit I Sheet 1\n", + "assert np.all(\n", + " e1_ccc_selected.cdf_.round(3).values\n", + " == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 2\n", + "assert np.all(\n", + " e1_rcc_selected.cdf_.round(3).values\n", + " == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 3\n", "assert np.allclose(\n", - " e1_s3['Selected Ult CC'].values,\n", + " e1_s3[\"Selected Ult CC\"].values,\n", " np.array([3292, 3243, 2699, 2757, 2635, 2699, 2665, 2524, 2764, 3061]),\n", - " atol=1\n", + " atol=1,\n", ")\n", - "#Exhibit I Sheet 6\n", - "assert np.all(e1_rsev_selected.cdf_.round(3).values == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 7\n", + "# Exhibit I Sheet 6\n", + "assert np.all(\n", + " e1_rsev_selected.cdf_.round(3).values\n", + " == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 7\n", "assert np.allclose(\n", - " e1_s7['Ult Severity'].values,\n", + " e1_s7[\"Ult Severity\"].values,\n", " np.array([4510, 4507, 4632, 4295, 4465, 4356, 4754, 4535, 4613, 4644]),\n", - " atol=1\n", + " atol=1,\n", ")" ] }, @@ -676,42 +735,58 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e2_tri = cl.load_sample('friedland_xyz_auto_bi')\n", + "# loading data and assumptions\n", + "e2_tri = cl.load_sample(\"friedland_xyz_auto_bi\")\n", "e2_cnt_assumptions = {}\n", - "e2_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e2_cnt_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e2_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e2_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", - "e2_cnt_assumptions['volume_2'] = {'n_periods':2, 'average':'volume'}\n", + "e2_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e2_cnt_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e2_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e2_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", + "e2_cnt_assumptions[\"volume_2\"] = {\"n_periods\": 2, \"average\": \"volume\"}\n", "e2_sev_assumptions = {}\n", - "e2_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e2_sev_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e2_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e2_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e2_sev_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e2_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "\n", - "#developing closed claim counts\n", - "e2_ccc_devs = average_dev(e2_tri['Closed Claim Counts'],e2_cnt_assumptions)\n", - "e2_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_ccc_devs['volume_2'])\n", + "# developing closed claim counts\n", + "e2_ccc_devs = average_dev(e2_tri[\"Closed Claim Counts\"], e2_cnt_assumptions)\n", + "e2_ccc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_ccc_devs[\"volume_2\"]\n", + ")\n", "e2_ccc_selected.ldf_ = e2_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e2_rcc_devs = average_dev(e2_tri['Reported Claim Counts'],e2_cnt_assumptions)\n", - "e2_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rcc_devs['volume_2'])\n", + "# developing reported claim counts\n", + "e2_rcc_devs = average_dev(e2_tri[\"Reported Claim Counts\"], e2_cnt_assumptions)\n", + "e2_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_rcc_devs[\"volume_2\"]\n", + ")\n", "e2_rcc_selected.ldf_ = e2_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e2_ccc_cl = cl.Chainladder().fit(e2_ccc_selected)\n", "e2_rcc_cl = cl.Chainladder().fit(e2_rcc_selected)\n", - "e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_)/2\n", + "e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating and developing reported severity\n", - "e2_rsev = e2_tri['Reported Claims'] / e2_tri['Reported Claim Counts'] * 1000\n", - "e2_rsev_devs = average_dev(e2_rsev,e2_sev_assumptions)\n", - "e2_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rsev_devs['simple_2'])\n", + "# calculating and developing reported severity\n", + "e2_rsev = e2_tri[\"Reported Claims\"] / e2_tri[\"Reported Claim Counts\"] * 1000\n", + "e2_rsev_devs = average_dev(e2_rsev, e2_sev_assumptions)\n", + "e2_rsev_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_rsev_devs[\"simple_2\"]\n", + ")\n", "e2_rsev_selected.ldf_ = e2_rsev_selected.ldf_.round(3)\n", "e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e2_ult = e2_rsev_cl.ultimate_ * e2_cc_ult / 1000" ] }, @@ -800,19 +875,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e2_tri['Reported Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e2_tri[\"Reported Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e2_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e2_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e2_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display(1/e2_rcc_selected.cdf_.round(3))" + "print(\"Percent Reported\")\n", + "nb_display(1 / e2_rcc_selected.cdf_.round(3))" ] }, { @@ -850,32 +925,42 @@ }, "outputs": [], "source": [ - "e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e2_ccc_df = (\n", + " cl.model_diagnostics(e2_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_rcc_df = (\n", + " cl.model_diagnostics(e2_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_s3 = e2_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]\n", + "e2_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e2_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e2_s3[\"Selected Ult CC\"] = e2_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e2_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", - " ]\n", - ") " + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", + " ],\n", + ")" ] }, { @@ -913,7 +998,7 @@ }, "outputs": [], "source": [ - "nb_display(e2_tri['Reported Claims'])\n", + "nb_display(e2_tri[\"Reported Claims\"])\n", "nb_display(e2_rsev)" ] }, @@ -952,15 +1037,15 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e2_rsev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e2_rsev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factors')\n", + "print(\"PART 3 - Average Age-to-Age Factors\")\n", "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e2_rsev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e2_rsev_selected.cdf_.round(3))" ] }, @@ -999,27 +1084,31 @@ }, "outputs": [], "source": [ - "e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e2_rsev_df = (\n", + " cl.model_diagnostics(e2_rsev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_s6 = e2_rsev_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Reported Severity',\n", - " 'Ultimate':'Ult Severity'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Reported Severity\",\n", + " \"Ultimate\": \"Ult Severity\",\n", " }\n", ")\n", - "e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']\n", + "e2_s6[\"Selected Ult CC\"] = e2_s3[\"Selected Ult CC\"]\n", "e2_s6[\"Ult Claims\"] = e2_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e2_s6,\n", " [\n", - " 'Reported Severity',\n", - " 'Ult Severity',\n", - " 'Selected Ult CC',\n", - " 'Ult Claims',\n", + " \"Reported Severity\",\n", + " \"Ult Severity\",\n", + " \"Selected Ult CC\",\n", + " \"Ult Claims\",\n", " ],\n", " [\n", - " 'CDF',\n", - " ]\n", + " \"CDF\",\n", + " ],\n", ")" ] }, @@ -1058,8 +1147,8 @@ }, "outputs": [], "source": [ - "e2_s7 = summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)\n", - "format_exh(e2_s7,e2_s7.columns)" + "e2_s7 = summary_exh(e2_tri[\"Reported Claims\"], e2_tri[\"Paid Claims\"], e2_ult)\n", + "format_exh(e2_s7, e2_s7.columns)" ] }, { @@ -1080,23 +1169,40 @@ }, "outputs": [], "source": [ - "#Exhibit II Sheet 1\n", - "assert np.all(e2_ccc_selected.cdf_.round(3).values == np.array([6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]))\n", - "#Exhibit II Sheet 2\n", - "assert np.all(e2_rcc_selected.cdf_.round(3).values == np.array([1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit II Sheet 3\n", + "# Exhibit II Sheet 1\n", + "assert np.all(\n", + " e2_ccc_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 2\n", + "assert np.all(\n", + " e2_rcc_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 3\n", "assert np.allclose(\n", - " e2_s3['Selected Ult CC'].values,\n", + " e2_s3[\"Selected Ult CC\"].values,\n", " np.array([637, 1047, 1416, 1466, 1565, 1666, 2309, 2483, 1807, 1556, 1426]),\n", - " atol=1\n", + " atol=1,\n", ")\n", - "#Exhibit II Sheet 5\n", - "assert np.all(e2_rsev_selected.cdf_.round(3).values == np.array([2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]))\n", - "#Exhibit II Sheet 6\n", + "# Exhibit II Sheet 5\n", + "assert np.all(\n", + " e2_rsev_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 6\n", "assert np.allclose(\n", - " e2_s6['Ult Severity'].values,\n", - " np.array([24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]),\n", - " rtol=0.001\n", + " e2_s6[\"Ult Severity\"].values,\n", + " np.array(\n", + " [24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]\n", + " ),\n", + " rtol=0.001,\n", ")" ] }, @@ -1136,71 +1242,93 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e3_tri = cl.load_sample('friedland_wc_self_insurer')\n", + "# loading data and assumptions\n", + "e3_tri = cl.load_sample(\"friedland_wc_self_insurer\")\n", "e3_cnt_assumptions = {}\n", - "e3_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e3_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e3_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e3_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e3_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e3_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e3_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e3_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e3_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e3_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e3_sev_assumptions = {}\n", - "e3_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e3_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e3_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e3_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e3_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e3_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e3_sevavg_assumptions = {}\n", - "e3_sevavg_assumptions['all_years'] = {}\n", - "e3_sevavg_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", - "e3_sevavg_assumptions['latest_3'] = {'n_periods':3}\n", + "e3_sevavg_assumptions[\"all_years\"] = {}\n", + "e3_sevavg_assumptions[\"all_years_excl_hilo\"] = {\"drop_high\": 1, \"drop_low\": 1}\n", + "e3_sevavg_assumptions[\"latest_3\"] = {\"n_periods\": 3}\n", "\n", - "#developing closed claim counts\n", - "#There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", - "e3_ccc_devs = average_dev(e3_tri['Closed Claim Counts'],e3_cnt_assumptions)\n", - "e3_ccc_selected = cl.TailConstant(tail = 1.002, projection_period = 0).fit_transform(e3_ccc_devs['volume_5'])\n", + "# developing closed claim counts\n", + "# There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", + "e3_ccc_devs = average_dev(e3_tri[\"Closed Claim Counts\"], e3_cnt_assumptions)\n", + "e3_ccc_selected = cl.TailConstant(tail=1.002, projection_period=0).fit_transform(\n", + " e3_ccc_devs[\"volume_5\"]\n", + ")\n", "e3_ccc_selected.ldf_ = e3_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e3_rcc_devs = average_dev(e3_tri['Reported Claim Counts'],e3_cnt_assumptions)\n", - "e3_rcc_selected = cl.TailConstant(tail = 1.000, projection_period = 0).fit_transform(e3_rcc_devs['volume_5'])\n", + "# developing reported claim counts\n", + "e3_rcc_devs = average_dev(e3_tri[\"Reported Claim Counts\"], e3_cnt_assumptions)\n", + "e3_rcc_selected = cl.TailConstant(tail=1.000, projection_period=0).fit_transform(\n", + " e3_rcc_devs[\"volume_5\"]\n", + ")\n", "e3_rcc_selected.ldf_ = e3_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e3_ccc_cl = cl.Chainladder().fit(e3_ccc_selected)\n", "e3_rcc_cl = cl.Chainladder().fit(e3_rcc_selected)\n", - "e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_)/2\n", + "e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating trended frequency\n", - "e3_freq_trend = cl.Trend(-.01).fit(e3_tri['Reported Claim Counts']).trend_\n", - "e3_payroll_trend = cl.Trend(.025).fit(e3_tri['Payroll']).trend_\n", - "e3_freq_2008 = .0036\n", - "e3_freq_2007 = e3_freq_2008 / e3_freq_trend.values[0,0,-2,0] * e3_payroll_trend.latest_diagonal.values[0,0,-2,0]\n", + "# calculating trended frequency\n", + "e3_freq_trend = cl.Trend(-0.01).fit(e3_tri[\"Reported Claim Counts\"]).trend_\n", + "e3_payroll_trend = cl.Trend(0.025).fit(e3_tri[\"Payroll\"]).trend_\n", + "e3_freq_2008 = 0.0036\n", + "e3_freq_2007 = (\n", + " e3_freq_2008\n", + " / e3_freq_trend.values[0, 0, -2, 0]\n", + " * e3_payroll_trend.latest_diagonal.values[0, 0, -2, 0]\n", + ")\n", "\n", - "#calculating and developing paid severity\n", - "e3_psev = e3_tri[\"Paid Claims\"] / e3_tri['Closed Claim Counts']\n", - "e3_psev_devs = average_dev(e3_psev,e3_sev_assumptions)\n", - "e3_psev_selected = cl.TailConstant(tail = 1.150, projection_period = 0).fit_transform(e3_psev_devs['medial_5x1'])\n", + "# calculating and developing paid severity\n", + "e3_psev = e3_tri[\"Paid Claims\"] / e3_tri[\"Closed Claim Counts\"]\n", + "e3_psev_devs = average_dev(e3_psev, e3_sev_assumptions)\n", + "e3_psev_selected = cl.TailConstant(tail=1.150, projection_period=0).fit_transform(\n", + " e3_psev_devs[\"medial_5x1\"]\n", + ")\n", "e3_psev_selected.ldf_ = e3_psev_selected.ldf_.round(3)\n", "\n", - "#calculating and developing reported severity\n", - "e3_rsev = e3_tri[\"Reported Claims\"] / e3_tri['Reported Claim Counts']\n", - "e3_rsev_devs = average_dev(e3_rsev,e3_sev_assumptions)\n", - "e3_rsev_selected = cl.TailConstant(tail = 1.025, projection_period = 0).fit_transform(e3_rsev_devs['medial_5x1'])\n", + "# calculating and developing reported severity\n", + "e3_rsev = e3_tri[\"Reported Claims\"] / e3_tri[\"Reported Claim Counts\"]\n", + "e3_rsev_devs = average_dev(e3_rsev, e3_sev_assumptions)\n", + "e3_rsev_selected = cl.TailConstant(tail=1.025, projection_period=0).fit_transform(\n", + " e3_rsev_devs[\"medial_5x1\"]\n", + ")\n", "e3_rsev_selected.ldf_ = e3_rsev_selected.ldf_.round(3)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e3_psev_cl = cl.Chainladder().fit(e3_psev_selected)\n", "e3_resv_cl = cl.Chainladder().fit(e3_rsev_selected)\n", - "e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_)/2\n", + "e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_) / 2\n", "\n", - "#calculated trended severity\n", - "e3_sev_trend = cl.Trend(.075).fit(e3_tri['Reported Claims']).trend_\n", + "# calculated trended severity\n", + "e3_sev_trend = cl.Trend(0.075).fit(e3_tri[\"Reported Claims\"]).trend_\n", "e3_sev_ult_trended = e3_sev_ult * e3_sev_trend.latest_diagonal.values\n", - "#prep ult severity for average calculations\n", - "e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin<='2006']\n", - "e3_sevs = average_sev(e3_sev_ult_trended_2006,e3_sevavg_assumptions)\n", - "#calculate selected severities\n", - "e3_sev_2008 = 7100.\n", - "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0,0,-2,0]\n" + "# prep ult severity for average calculations\n", + "e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin <= \"2006\"]\n", + "e3_sevs = average_sev(e3_sev_ult_trended_2006, e3_sevavg_assumptions)\n", + "# calculate selected severities\n", + "e3_sev_2008 = 7100.0\n", + "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0, 0, -2, 0]" ] }, { @@ -1237,19 +1365,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_tri[\"Closed Claim Counts\"])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e3_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e3_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display((1/e3_ccc_selected.cdf_).round(3))" + "print(\"Percent Closed\")\n", + "nb_display((1 / e3_ccc_selected.cdf_).round(3))" ] }, { @@ -1286,18 +1414,18 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_tri[\"Reported Claim Counts\"])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e3_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e3_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display((1/e3_rcc_selected.cdf_).round(3))" + "print(\"Percent Reported\")\n", + "nb_display((1 / e3_rcc_selected.cdf_).round(3))" ] }, { @@ -1334,31 +1462,41 @@ }, "outputs": [], "source": [ - "e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e3_ccc_df = (\n", + " cl.model_diagnostics(e3_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_rcc_df = (\n", + " cl.model_diagnostics(e3_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_s3 = e3_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]\n", + "e3_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e3_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e3_s3[\"Selected Ult CC\"] = e3_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e3_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", - " ]\n", + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", + " ],\n", ")" ] }, @@ -1396,27 +1534,29 @@ }, "outputs": [], "source": [ - "e3_s4 = e3_s3[['Selected Ult CC']].copy()\n", - "e3_s4['CC Trend'] = e3_freq_trend.latest_diagonal.to_frame()\n", - "e3_s4['Trended Ult CC'] = e3_s4['Selected Ult CC'] * e3_s4['CC Trend']\n", - "e3_s4['Payroll'] = e3_tri['Payroll'].latest_diagonal.to_frame()\n", - "e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()\n", - "e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']\n", - "e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']\n", - "nb_display(format_exh(\n", - " e3_s4,\n", - " [\n", - " 'Selected Ult CC',\n", - " 'Trended Ult CC',\n", - " 'Payroll',\n", - " 'Trended Payroll',\n", - " ],\n", - " [\n", - " 'CC Trend',\n", - " 'Payroll Trend',\n", - " ],\n", - " {'Trended Ultimate Freq' : \"{:.2%}\"}\n", - "))\n", + "e3_s4 = e3_s3[[\"Selected Ult CC\"]].copy()\n", + "e3_s4[\"CC Trend\"] = e3_freq_trend.latest_diagonal.to_frame()\n", + "e3_s4[\"Trended Ult CC\"] = e3_s4[\"Selected Ult CC\"] * e3_s4[\"CC Trend\"]\n", + "e3_s4[\"Payroll\"] = e3_tri[\"Payroll\"].latest_diagonal.to_frame()\n", + "e3_s4[\"Payroll Trend\"] = e3_payroll_trend.latest_diagonal.to_frame()\n", + "e3_s4[\"Trended Payroll\"] = e3_s4[\"Payroll\"] * e3_s4[\"Payroll Trend\"]\n", + "e3_s4[\"Trended Ultimate Freq\"] = e3_s4[\"Trended Ult CC\"] / e3_s4[\"Trended Payroll\"]\n", + "nb_display(\n", + " format_exh(\n", + " e3_s4,\n", + " [\n", + " \"Selected Ult CC\",\n", + " \"Trended Ult CC\",\n", + " \"Payroll\",\n", + " \"Trended Payroll\",\n", + " ],\n", + " [\n", + " \"CC Trend\",\n", + " \"Payroll Trend\",\n", + " ],\n", + " {\"Trended Ultimate Freq\": \"{:.2%}\"},\n", + " )\n", + ")\n", "print(f\"Selected Frequency at 2008 level {e3_freq_2008:.2%}\")\n", "print(f\"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}\")" ] @@ -1455,13 +1595,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e3_tri[\"Paid Claims\"])\n", - "print('Paid Severities')\n", + "print(\"Paid Severities\")\n", "nb_display(e3_psev)\n", - "print('Reported Claims')\n", + "print(\"Reported Claims\")\n", "nb_display(e3_tri[\"Reported Claims\"])\n", - "print('Reported Severities')\n", + "print(\"Reported Severities\")\n", "nb_display(e3_rsev)" ] }, @@ -1499,16 +1639,16 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_psev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e3_psev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e3_psev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_psev_selected.cdf_.round(3))" ] }, @@ -1593,30 +1733,40 @@ }, "outputs": [], "source": [ - "e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e3_psev_df = (\n", + " cl.model_diagnostics(e3_psev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_resv_df = (\n", + " cl.model_diagnostics(e3_resv_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_s8 = e3_psev_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Paid Sev',\n", - " 'CDF':'Paid CDF',\n", - " 'Ultimate':'Ult Paid Sev'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Paid Sev\",\n", + " \"CDF\": \"Paid CDF\",\n", + " \"Ultimate\": \"Ult Paid Sev\",\n", " }\n", ")\n", - "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", + "e3_s8[[\"Reported Sev\", \"Reported CDF\", \"Ult Reported Sev\"]] = e3_resv_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", "format_exh(\n", - " e3_s8.iloc[:-2,:],\n", + " e3_s8.iloc[:-2, :],\n", " [\n", - " 'Paid Sev',\n", - " 'Reported Sev',\n", - " 'Ult Paid Sev',\n", - " 'Ult Reported Sev',\n", - " 'Selected Ult Sev',\n", + " \"Paid Sev\",\n", + " \"Reported Sev\",\n", + " \"Ult Paid Sev\",\n", + " \"Ult Reported Sev\",\n", + " \"Selected Ult Sev\",\n", " ],\n", " [\n", - " 'Paid CDF',\n", - " 'Reported CDF',\n", + " \"Paid CDF\",\n", + " \"Reported CDF\",\n", " ],\n", ")" ] @@ -1712,31 +1862,31 @@ }, "outputs": [], "source": [ - "e3_s10 = e3_s4[['Payroll']].copy()[-2:]\n", - "e3_s10['Selected Frequency'] = [e3_freq_2007,e3_freq_2008]\n", - "e3_s10['Ult CC'] = e3_s10['Payroll'] * e3_s10['Selected Frequency']\n", - "e3_s10['Selected Severity'] = [e3_sev_2007,e3_sev_2008]\n", - "e3_s10['Ult Claims'] = e3_s10['Ult CC'] * e3_s10['Selected Severity']\n", - "e3_s10['Reported Claims'] = e3_tri['Reported Claims'].latest_diagonal.to_frame()\n", - "e3_s10['Paid Claims'] = e3_tri['Paid Claims'].latest_diagonal.to_frame()\n", - "e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']\n", - "e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']\n", - "e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']\n", + "e3_s10 = e3_s4[[\"Payroll\"]].copy()[-2:]\n", + "e3_s10[\"Selected Frequency\"] = [e3_freq_2007, e3_freq_2008]\n", + "e3_s10[\"Ult CC\"] = e3_s10[\"Payroll\"] * e3_s10[\"Selected Frequency\"]\n", + "e3_s10[\"Selected Severity\"] = [e3_sev_2007, e3_sev_2008]\n", + "e3_s10[\"Ult Claims\"] = e3_s10[\"Ult CC\"] * e3_s10[\"Selected Severity\"]\n", + "e3_s10[\"Reported Claims\"] = e3_tri[\"Reported Claims\"].latest_diagonal.to_frame()\n", + "e3_s10[\"Paid Claims\"] = e3_tri[\"Paid Claims\"].latest_diagonal.to_frame()\n", + "e3_s10[\"Case Outstanding\"] = e3_s10[\"Reported Claims\"] - e3_s10[\"Paid Claims\"]\n", + "e3_s10[\"IBNR\"] = e3_s10[\"Ult Claims\"] - e3_s10[\"Reported Claims\"]\n", + "e3_s10[\"Unpaid\"] = e3_s10[\"IBNR\"] + e3_s10[\"Case Outstanding\"]\n", "format_exh(\n", " e3_s10,\n", " [\n", - " 'Payroll',\n", - " 'Ult CC',\n", - " 'Selected Severity',\n", - " 'Ult Claims',\n", - " 'Reported Claims',\n", - " 'Paid Claims',\n", - " 'Case Outstanding',\n", - " 'IBNR',\n", - " 'Unpaid'\n", + " \"Payroll\",\n", + " \"Ult CC\",\n", + " \"Selected Severity\",\n", + " \"Ult Claims\",\n", + " \"Reported Claims\",\n", + " \"Paid Claims\",\n", + " \"Case Outstanding\",\n", + " \"IBNR\",\n", + " \"Unpaid\",\n", " ],\n", " [],\n", - " {'Selected Frequency':'{:.2%}'}\n", + " {\"Selected Frequency\": \"{:.2%}\"},\n", ")" ] }, @@ -1758,41 +1908,50 @@ }, "outputs": [], "source": [ - "#Exhibit III Sheet 1\n", - "assert np.all(e3_ccc_selected.cdf_.round(3).values[...,:7] == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010]))\n", - "#Exhibit III Sheet 2\n", - "assert np.all(e3_rcc_selected.cdf_.round(3).values == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit III Sheet 3\n", + "# Exhibit III Sheet 1\n", + "assert np.all(\n", + " e3_ccc_selected.cdf_.round(3).values[..., :7]\n", + " == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010])\n", + ")\n", + "# Exhibit III Sheet 2\n", + "assert np.all(\n", + " e3_rcc_selected.cdf_.round(3).values\n", + " == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit III Sheet 3\n", "assert np.allclose(\n", - " e3_s3['Selected Ult CC'].values[...,1:],\n", + " e3_s3[\"Selected Ult CC\"].values[..., 1:],\n", " np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),\n", - " rtol=0.001\n", + " rtol=0.001,\n", ")\n", - "#Exhibit III Sheet 4\n", + "# Exhibit III Sheet 4\n", "assert np.allclose(\n", - " e3_s4['Trended Ultimate Freq'].values[...,1:],\n", + " e3_s4[\"Trended Ultimate Freq\"].values[..., 1:],\n", " np.array([0.0053, 0.0053, 0.0054, 0.0043, 0.0035, 0.0036, 0.0036]),\n", - " atol = 0.0001\n", - " )\n", - "#Exhibit III Sheet 6\n", + " atol=0.0001,\n", + ")\n", + "# Exhibit III Sheet 6\n", "assert np.allclose(\n", " e3_psev_selected.ldf_.round(3).values,\n", " np.array([1.447, 1.249, 1.108, 1.066, 1.038, 1.037, 1.021, 1.150]),\n", - " atol=.001\n", + " atol=0.001,\n", + ")\n", + "# Exhibit III Sheet 7\n", + "assert np.all(\n", + " e3_rsev_selected.cdf_.round(3).values\n", + " == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025])\n", ")\n", - "#Exhibit III Sheet 7\n", - "assert np.all(e3_rsev_selected.cdf_.round(3).values == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025]))\n", - "#Exhibit III Sheet 8\n", + "# Exhibit III Sheet 8\n", "assert np.allclose(\n", - " e3_s8['Selected Ult Sev'].values[:-2],\n", + " e3_s8[\"Selected Ult Sev\"].values[:-2],\n", " np.array([4371, 4587, 4963, 5242, 5635, 6169]),\n", - " rtol=.001\n", + " rtol=0.001,\n", ")\n", - "#Exhibit III Sheet 9\n", + "# Exhibit III Sheet 9\n", "assert np.allclose(\n", - " e3_s9['Trended Ult Sev'].values[:-2],\n", + " e3_s9[\"Trended Ult Sev\"].values[:-2],\n", " np.array([7251, 7079, 7125, 7000, 7001, 7129]),\n", - " rtol=0.001\n", + " rtol=0.001,\n", ")" ] }, @@ -1832,67 +1991,91 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "xyz_tort_data = pd.DataFrame({\n", - " \"EffDate\": [\"2006-01-01\", \"2007-01-01\"],\n", - " \"RateChange\": [(0.67/0.75) - 1, 0.75-1]\n", - "})\n", + "# loading data and assumptions\n", + "xyz_tort_data = pd.DataFrame(\n", + " {\n", + " \"EffDate\": [\"2006-01-01\", \"2007-01-01\"],\n", + " \"RateChange\": [(0.67 / 0.75) - 1, 0.75 - 1],\n", + " }\n", + ")\n", "xyz_tort_adjustment = cl.ParallelogramOLF(\n", " rate_history=xyz_tort_data,\n", " change_col=\"RateChange\",\n", " date_col=\"EffDate\",\n", - " vertical_line=True\n", + " vertical_line=True,\n", ")\n", - "#Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given\n", + "# Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given\n", "olf = cl.Triangle(\n", - " data = {\n", - " 'origin':[1998,2002,2003,2004,2005,2006,2007,2008],\n", - " 'valuation':[2008,2008,2008,2008,2008,2008,2008,2008],\n", - " 'On-Level Adjustment':[0,.914,.87,.81,.704,.64,.8,1],\n", + " data={\n", + " \"origin\": [1998, 2002, 2003, 2004, 2005, 2006, 2007, 2008],\n", + " \"valuation\": [2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008],\n", + " \"On-Level Adjustment\": [0, 0.914, 0.87, 0.81, 0.704, 0.64, 0.8, 1],\n", " },\n", - " origin='origin',\n", - " development='valuation',\n", - " columns='On-Level Adjustment',\n", - " cumulative = True\n", + " origin=\"origin\",\n", + " development=\"valuation\",\n", + " columns=\"On-Level Adjustment\",\n", + " cumulative=True,\n", ")\n", "e4_freq_assumptions = {}\n", - "e4_freq_assumptions['all_years'] = {}\n", - "e4_freq_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", - "e4_freq_assumptions['latest_2'] = {'n_periods':2}\n", + "e4_freq_assumptions[\"all_years\"] = {}\n", + "e4_freq_assumptions[\"all_years_excl_hilo\"] = {\"drop_high\": 1, \"drop_low\": 1}\n", + "e4_freq_assumptions[\"latest_2\"] = {\"n_periods\": 2}\n", "e4_sev_assumptions = {}\n", - "e4_sev_assumptions['latest_5_years'] = {'n_periods':5}\n", - "e4_sev_assumptions['latest_5_years_excl_hilo'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", - "e4_sev_assumptions['latest_3'] = {'n_periods':3}\n", + "e4_sev_assumptions[\"latest_5_years\"] = {\"n_periods\": 5}\n", + "e4_sev_assumptions[\"latest_5_years_excl_hilo\"] = {\n", + " \"n_periods\": 5,\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e4_sev_assumptions[\"latest_3\"] = {\"n_periods\": 3}\n", "\n", "# In Exhibit II, the selected ultimate was actually the average of reported and closed ultimates.\n", "e4_s1_tri = e2_rcc_cl.ultimate_.copy()\n", - "e4_s1_tri = e4_s1_tri.rename(axis='columns',value='Ult CC')\n", - "e4_s1_tri['CC Trend'] = cl.Trend(-.015,dates=('2008-12-31','2002-01-01')).fit(e4_s1_tri).trend_\n", - "e4_s1_tri['Trended Ult CC'] = e4_s1_tri['CC Trend'] * e4_s1_tri['Ult CC']\n", - "e4_s1_tri['Earned Premium'] = e2_tri['Earned Premium'].latest_diagonal\n", - "e4_s1_tri['On-Level Adjustment'] = olf['On-Level Adjustment']\n", - "e4_s1_tri['On-Level Premium'] = e4_s1_tri['On-Level Adjustment'] * e4_s1_tri['Earned Premium']\n", - "e4_s1_tri['Trended Ult Freq'] = e4_s1_tri['Trended Ult CC'] / e4_s1_tri['On-Level Premium']\n", - "e4_s1_tri_2006 = e4_s1_tri.iloc[:,:,4:9,:]\n", - "#calculate average frequencies\n", - "e4_freq = average_sev(e4_s1_tri_2006['Trended Ult Freq'],e4_freq_assumptions)\n", - "#calculate selected frequencies\n", - "e4_freq_2008 = e4_freq['latest_2'].round(4)\n", - "e4_freq_2007 = e4_freq_2008 / e4_s1_tri['CC Trend'].values[0,0,-2,0] * e4_s1_tri['On-Level Adjustment'].values[0,0,-2,0]\n", + "e4_s1_tri = e4_s1_tri.rename(axis=\"columns\", value=\"Ult CC\")\n", + "e4_s1_tri[\"CC Trend\"] = (\n", + " cl.Trend(-0.015, dates=(\"2008-12-31\", \"2002-01-01\")).fit(e4_s1_tri).trend_\n", + ")\n", + "e4_s1_tri[\"Trended Ult CC\"] = e4_s1_tri[\"CC Trend\"] * e4_s1_tri[\"Ult CC\"]\n", + "e4_s1_tri[\"Earned Premium\"] = e2_tri[\"Earned Premium\"].latest_diagonal\n", + "e4_s1_tri[\"On-Level Adjustment\"] = olf[\"On-Level Adjustment\"]\n", + "e4_s1_tri[\"On-Level Premium\"] = (\n", + " e4_s1_tri[\"On-Level Adjustment\"] * e4_s1_tri[\"Earned Premium\"]\n", + ")\n", + "e4_s1_tri[\"Trended Ult Freq\"] = (\n", + " e4_s1_tri[\"Trended Ult CC\"] / e4_s1_tri[\"On-Level Premium\"]\n", + ")\n", + "e4_s1_tri_2006 = e4_s1_tri.iloc[:, :, 4:9, :]\n", + "# calculate average frequencies\n", + "e4_freq = average_sev(e4_s1_tri_2006[\"Trended Ult Freq\"], e4_freq_assumptions)\n", + "# calculate selected frequencies\n", + "e4_freq_2008 = e4_freq[\"latest_2\"].round(4)\n", + "e4_freq_2007 = (\n", + " e4_freq_2008\n", + " / e4_s1_tri[\"CC Trend\"].values[0, 0, -2, 0]\n", + " * e4_s1_tri[\"On-Level Adjustment\"].values[0, 0, -2, 0]\n", + ")\n", "e4_freq_2007 = e4_freq_2007.round(4)\n", "\n", "e4_s2_tri = e2_rsev_cl.ultimate_.copy()\n", - "e4_s2_tri = e4_s2_tri.rename(axis='columns',value='Ult Sev')\n", - "e4_s2_tri['Sev Trend'] = cl.Trend(.05,dates=('2008-12-31','1998-01-01')).fit(e4_s2_tri).trend_.round(3)\n", - "#from Chapter 8 Exhibit III sheet 1\n", - "e4_s2_tri['Tort Reform Factors'] = xyz_tort_adjustment.fit(e4_s2_tri['Ult Sev']).olf_\n", - "e4_s2_tri['Trended Ult Sev'] = e4_s2_tri['Ult Sev'] * e4_s2_tri['Sev Trend'] * e4_s2_tri['Tort Reform Factors']\n", - "e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= '2006']\n", - "#calculate average severities\n", - "e4_sev = average_sev(e4_s2_tri_2006['Trended Ult Sev'],e4_sev_assumptions)\n", - "#calculate selected severities\n", - "e4_sev_2008 = e4_sev['latest_5_years_excl_hilo'].round(0)\n", - "e4_sev_2007 = e4_sev_2008 / e4_s2_tri['Sev Trend'].values[0,0,-2,0] / e4_s2_tri['Tort Reform Factors'].values[0,0,-2,0]\n", + "e4_s2_tri = e4_s2_tri.rename(axis=\"columns\", value=\"Ult Sev\")\n", + "e4_s2_tri[\"Sev Trend\"] = (\n", + " cl.Trend(0.05, dates=(\"2008-12-31\", \"1998-01-01\")).fit(e4_s2_tri).trend_.round(3)\n", + ")\n", + "# from Chapter 8 Exhibit III sheet 1\n", + "e4_s2_tri[\"Tort Reform Factors\"] = xyz_tort_adjustment.fit(e4_s2_tri[\"Ult Sev\"]).olf_\n", + "e4_s2_tri[\"Trended Ult Sev\"] = (\n", + " e4_s2_tri[\"Ult Sev\"] * e4_s2_tri[\"Sev Trend\"] * e4_s2_tri[\"Tort Reform Factors\"]\n", + ")\n", + "e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= \"2006\"]\n", + "# calculate average severities\n", + "e4_sev = average_sev(e4_s2_tri_2006[\"Trended Ult Sev\"], e4_sev_assumptions)\n", + "# calculate selected severities\n", + "e4_sev_2008 = e4_sev[\"latest_5_years_excl_hilo\"].round(0)\n", + "e4_sev_2007 = (\n", + " e4_sev_2008\n", + " / e4_s2_tri[\"Sev Trend\"].values[0, 0, -2, 0]\n", + " / e4_s2_tri[\"Tort Reform Factors\"].values[0, 0, -2, 0]\n", + ")\n", "e4_sev_2007 = e4_sev_2007.round(0)" ] }, @@ -1931,13 +2114,17 @@ "outputs": [], "source": [ "e4_s1 = e4_s1_tri.to_frame().T\n", - "e4_s1[['CC Trend','On-Level Adjustment']] = e4_s1[['CC Trend','On-Level Adjustment']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e4_s1[['Trended Ult CC','On-Level Premium']] = (e4_s1[['Trended Ult CC','On-Level Premium']] + 1e-9).round(0)\n", - "e4_s1['Trended Ult Freq'] = e4_s1[\"Trended Ult Freq\"].map(\"{:.2%}\".format)\n", + "e4_s1[[\"CC Trend\", \"On-Level Adjustment\"]] = e4_s1[\n", + " [\"CC Trend\", \"On-Level Adjustment\"]\n", + "].round(3)\n", + "# using floating point offset to achieve standard rounding\n", + "e4_s1[[\"Trended Ult CC\", \"On-Level Premium\"]] = (\n", + " e4_s1[[\"Trended Ult CC\", \"On-Level Premium\"]] + 1e-9\n", + ").round(0)\n", + "e4_s1[\"Trended Ult Freq\"] = e4_s1[\"Trended Ult Freq\"].map(\"{:.2%}\".format)\n", "nb_display(e4_s1.iloc[4:9])\n", - "print('Average Trended Frequency at 2008 Cost Level')\n", - "for k,v in e4_freq.items():\n", + "print(\"Average Trended Frequency at 2008 Cost Level\")\n", + "for k, v in e4_freq.items():\n", " print(f\"\\t{k:<20} \\t{v:.2%}\")\n", "print(f\"Selected 2008 Frequency \\t{e4_freq_2008:.2%}\")\n", "print(f\"Estimated 2007 Frequency \\t{e4_freq_2007:.2%}\")" @@ -1978,12 +2165,16 @@ "outputs": [], "source": [ "e4_s2 = e4_s2_tri.to_frame().T\n", - "e4_s2[['Sev Trend','Tort Reform Factors']] = e4_s2[['Sev Trend','Tort Reform Factors']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e4_s2[['Trended Ult Sev','Ult Sev']] = (e4_s2[['Trended Ult Sev','Ult Sev']] + 1e-9).round(0)\n", + "e4_s2[[\"Sev Trend\", \"Tort Reform Factors\"]] = e4_s2[\n", + " [\"Sev Trend\", \"Tort Reform Factors\"]\n", + "].round(3)\n", + "# using floating point offset to achieve standard rounding\n", + "e4_s2[[\"Trended Ult Sev\", \"Ult Sev\"]] = (\n", + " e4_s2[[\"Trended Ult Sev\", \"Ult Sev\"]] + 1e-9\n", + ").round(0)\n", "nb_display(e4_s2.iloc[:9])\n", - "print('Average Trended Severity at 2008 Cost Level')\n", - "for k,v in e4_sev.items():\n", + "print(\"Average Trended Severity at 2008 Cost Level\")\n", + "for k, v in e4_sev.items():\n", " print(f\"\\t{k:<25} \\t{v.round(0)}\")\n", "print(f\"Selected 2008 Severity \\t\\t\\t{e4_sev_2008}\")\n", "print(f\"Estimated 2007 Severity \\t\\t{e4_sev_2007}\")" @@ -2023,16 +2214,21 @@ }, "outputs": [], "source": [ - "e4_s3 = e4_s1_tri['Earned Premium'].to_frame(keepdims=True).set_index('origin').iloc[-2:][['Earned Premium']]\n", - "e4_s3['Selected Frequency'] = [e4_freq_2007,e4_freq_2008]\n", - "e4_s3['Ult CC'] = e4_s3['Earned Premium'] * e4_s3['Selected Frequency']\n", - "e4_s3['Selected Severity'] = [e4_sev_2007,e4_sev_2008]\n", - "e4_s3['Ult Claims'] = e4_s3['Ult CC'] * e4_s3['Selected Severity']\n", - "e4_s3['Reported Claims'] = (e2_tri['Reported Claims']*1000).latest_diagonal.to_frame()\n", - "e4_s3['Paid Claims'] = (e2_tri['Paid Claims']*1000).latest_diagonal.to_frame()\n", - "e4_s3['Case Outstanding'] = e4_s3['Reported Claims'] - e4_s3['Paid Claims']\n", - "e4_s3['IBNR'] = e4_s3['Ult Claims'] - e4_s3['Reported Claims']\n", - "e4_s3['Unpaid'] = e4_s3['IBNR'] + e4_s3['Case Outstanding']\n", + "e4_s3 = (\n", + " e4_s1_tri[\"Earned Premium\"]\n", + " .to_frame(keepdims=True)\n", + " .set_index(\"origin\")\n", + " .iloc[-2:][[\"Earned Premium\"]]\n", + ")\n", + "e4_s3[\"Selected Frequency\"] = [e4_freq_2007, e4_freq_2008]\n", + "e4_s3[\"Ult CC\"] = e4_s3[\"Earned Premium\"] * e4_s3[\"Selected Frequency\"]\n", + "e4_s3[\"Selected Severity\"] = [e4_sev_2007, e4_sev_2008]\n", + "e4_s3[\"Ult Claims\"] = e4_s3[\"Ult CC\"] * e4_s3[\"Selected Severity\"]\n", + "e4_s3[\"Reported Claims\"] = (e2_tri[\"Reported Claims\"] * 1000).latest_diagonal.to_frame()\n", + "e4_s3[\"Paid Claims\"] = (e2_tri[\"Paid Claims\"] * 1000).latest_diagonal.to_frame()\n", + "e4_s3[\"Case Outstanding\"] = e4_s3[\"Reported Claims\"] - e4_s3[\"Paid Claims\"]\n", + "e4_s3[\"IBNR\"] = e4_s3[\"Ult Claims\"] - e4_s3[\"Reported Claims\"]\n", + "e4_s3[\"Unpaid\"] = e4_s3[\"IBNR\"] + e4_s3[\"Case Outstanding\"]\n", "e4_s3.T" ] }, @@ -2054,19 +2250,18 @@ }, "outputs": [], "source": [ - "#Exhibit IV Sheet 1\n", - "assert np.all(e4_s1.iloc[4:9]['Trended Ult Freq'].values == np.array(['2.54%', '2.51%', '2.65%', '2.36%', '2.37%']))\n", - "#Exhibit IV Sheet 2\n", + "# Exhibit IV Sheet 1\n", + "assert np.all(\n", + " e4_s1.iloc[4:9][\"Trended Ult Freq\"].values\n", + " == np.array([\"2.54%\", \"2.51%\", \"2.65%\", \"2.36%\", \"2.37%\"])\n", + ")\n", + "# Exhibit IV Sheet 2\n", "assert np.allclose(\n", - " list(e4_sev.values()),\n", - " np.array([26669., 26720., 27254.]),\n", - " rtol=0.001\n", + " list(e4_sev.values()), np.array([26669.0, 26720.0, 27254.0]), rtol=0.001\n", ")\n", - "#Exhibit IV Sheet 3\n", + "# Exhibit IV Sheet 3\n", "assert np.allclose(\n", - " e4_s3.iloc[:,4].values,\n", - " np.array([30512152., 30140260.]),\n", - " rtol=0.001\n", + " e4_s3.iloc[:, 4].values, np.array([30512152.0, 30140260.0]), rtol=0.001\n", ")" ] }, @@ -2106,85 +2301,111 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e5_tri = cl.load_sample('friedland_gl_insurer')\n", + "# loading data and assumptions\n", + "e5_tri = cl.load_sample(\"friedland_gl_insurer\")\n", "e5_cnt_assumptions = {}\n", - "e5_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e5_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e5_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e5_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e5_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e5_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e5_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e5_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e5_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e5_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e5_disp_assumptions = {}\n", - "e5_disp_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e5_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e5_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e5_disp_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e5_disp_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e5_disp_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e5_trend_assumptions = {}\n", - "e5_trend_assumptions['all_years'] = {}\n", - "e5_trend_assumptions['latest_6'] = {'n_periods':6}\n", - "e5_trend_assumptions['latest_4'] = {'n_periods':4}\n", + "e5_trend_assumptions[\"all_years\"] = {}\n", + "e5_trend_assumptions[\"latest_6\"] = {\"n_periods\": 6}\n", + "e5_trend_assumptions[\"latest_4\"] = {\"n_periods\": 4}\n", "e5_sev_assumptions = {}\n", - "e5_sev_assumptions['simple_5'] = {'n_periods':5}\n", - "e5_sev_assumptions['simple_3'] = {'n_periods':3}\n", - "e5_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "e5_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5}\n", + "e5_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3}\n", + "e5_sev_assumptions[\"medial_5x1\"] = {\"n_periods\": 5, \"drop_high\": 1, \"drop_low\": 1}\n", "\n", - "#developing closed claim counts\n", - "e5_ccc_devs = average_dev(e5_tri['Closed Claim Counts'],e5_cnt_assumptions)\n", - "e5_ccc_selected = cl.TailConstant(tail = 1.100, projection_period = 0).fit_transform(e5_ccc_devs['volume_3'])\n", + "# developing closed claim counts\n", + "e5_ccc_devs = average_dev(e5_tri[\"Closed Claim Counts\"], e5_cnt_assumptions)\n", + "e5_ccc_selected = cl.TailConstant(tail=1.100, projection_period=0).fit_transform(\n", + " e5_ccc_devs[\"volume_3\"]\n", + ")\n", "e5_ccc_selected.ldf_ = e5_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e5_rcc_devs = average_dev(e5_tri['Reported Claim Counts'],e5_cnt_assumptions)\n", - "e5_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e5_rcc_devs['volume_3'])\n", + "# developing reported claim counts\n", + "e5_rcc_devs = average_dev(e5_tri[\"Reported Claim Counts\"], e5_cnt_assumptions)\n", + "e5_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e5_rcc_devs[\"volume_3\"]\n", + ")\n", "e5_rcc_selected.ldf_ = e5_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e5_ccc_cl = cl.Chainladder().fit(e5_ccc_selected)\n", "e5_rcc_cl = cl.Chainladder().fit(e5_rcc_selected)\n", - "e5_cc_ult = ((e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_)/2)\n", + "e5_cc_ult = (e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating disposal rate and complete the closed count triangle\n", - "e5_drs = average_dr(e5_tri['Closed Claim Counts'],e5_cc_ult,e5_disp_assumptions)\n", - "e5_dr_selected = e5_drs['medial_5x1']\n", + "# calculating disposal rate and complete the closed count triangle\n", + "e5_drs = average_dr(e5_tri[\"Closed Claim Counts\"], e5_cc_ult, e5_disp_assumptions)\n", + "e5_dr_selected = e5_drs[\"medial_5x1\"]\n", "e5_dr_selected.disposal_rate_ = e5_dr_selected.disposal_rate_.round(3)\n", - "e5_dr_tri = e5_dr_selected.transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult)\n", + "e5_dr_tri = e5_dr_selected.transform(\n", + " e5_tri[\"Closed Claim Counts\"], sample_weight=e5_cc_ult\n", + ")\n", "\n", - "#calculate and select incremental paid severity\n", - "e5_ipsev = e5_tri[\"Paid Claims\"].cum_to_incr() / e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", - "e5_regs = regs(e5_ipsev,e5_trend_assumptions)\n", - "e5_trend,e5_rsq = reg_outputs(e5_regs,e5_ipsev.development)\n", - "e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin>'2001'],{'all_years_ex_2001':{}})\n", - "e5_trend_ex2001,e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001,e5_ipsev.development)\n", - "e5_sevs = average_sev(e5_ipsev.trend(0.05),e5_sev_assumptions)\n", - "e5_sevs_sel = e5_sevs['simple_3'].copy()\n", - "e5_sevs_sel.origin = ['2008']\n", + "# calculate and select incremental paid severity\n", + "e5_ipsev = (\n", + " e5_tri[\"Paid Claims\"].cum_to_incr() / e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ")\n", + "e5_regs = regs(e5_ipsev, e5_trend_assumptions)\n", + "e5_trend, e5_rsq = reg_outputs(e5_regs, e5_ipsev.development)\n", + "e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin > \"2001\"], {\"all_years_ex_2001\": {}})\n", + "e5_trend_ex2001, e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001, e5_ipsev.development)\n", + "e5_sevs = average_sev(e5_ipsev.trend(0.05), e5_sev_assumptions)\n", + "e5_sevs_sel = e5_sevs[\"simple_3\"].copy()\n", + "e5_sevs_sel.origin = [\"2008\"]\n", "\n", - "#calculate and select tail paid severity\n", + "# calculate and select tail paid severity\n", "e5_incr_ccc = e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", "e5_incr_paid = e5_tri[\"Paid Claims\"].cum_to_incr()\n", "e5_incr_paid_trended = e5_incr_paid.trend(0.05)\n", "\n", - "#complete the incremental paid severity triangle\n", + "# complete the incremental paid severity triangle\n", "e5_ipsev_full = e5_ipsev.copy()\n", - "#extending the severity triangle to 108 months\n", - "e5_ipsev_full = cl.concat((e5_ipsev_full,e5_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", - "#setting the before 60 months to selected incremental\n", - "e5_ipsev_full.iloc[:,:,:,:5] = e5_sevs_sel.iloc[:,:,:,:5]\n", - "#setting the after 72 months to selected tail\n", - "e5_ipsev_full.iloc[:,:,:,5:] = (\n", + "# extending the severity triangle to 108 months\n", + "e5_ipsev_full = cl.concat(\n", + " (e5_ipsev_full, e5_ipsev_full.latest_diagonal.rename(\"development\", [9999])), axis=3\n", + ")\n", + "# setting the before 60 months to selected incremental\n", + "e5_ipsev_full.iloc[:, :, :, :5] = e5_sevs_sel.iloc[:, :, :, :5]\n", + "# setting the after 72 months to selected tail\n", + "e5_ipsev_full.iloc[:, :, :, 5:] = (\n", " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", - " /\n", - " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum() \n", + " / e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()\n", ")\n", - "#setting the valuation_date to the future\n", + "# setting the valuation_date to the future\n", "e5_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", - "#trending back from latest accident year\n", - "e5_ipsev_full_detrended = e5_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01')\n", - "#compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle\n", - "e5_ipsev_full_complete = e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date] + e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]\n", + "# trending back from latest accident year\n", + "e5_ipsev_full_detrended = e5_ipsev_full.trend(\n", + " 1 / 1.05 - 1, start=\"2008-12-31\", end=\"2001-01-01\"\n", + ")\n", + "# compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle\n", + "e5_ipsev_full_complete = (\n", + " e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date]\n", + " + e5_ipsev_full_detrended[\n", + " e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date\n", + " ]\n", + ")\n", "\n", - "#complete the incremental paid triangle\n", + "# complete the incremental paid triangle\n", "e5_s11_ccc = e5_dr_tri.full_triangle_.cum_to_incr()\n", - "e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete\n" + "e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete" ] }, { @@ -2318,10 +2539,22 @@ }, "outputs": [], "source": [ - "e5_ccc_df = cl.model_diagnostics(e5_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e5_rcc_df = cl.model_diagnostics(e5_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e5_s3 = e5_ccc_df[['development','Latest','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','Ultimate':'Ult Count Using CCC'})\n", - "e5_s3[['Reported Claim Counts','Ult Count Using RCC']] = e5_rcc_df[['Latest','Ultimate']]\n", + "e5_ccc_df = (\n", + " cl.model_diagnostics(e5_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e5_rcc_df = (\n", + " cl.model_diagnostics(e5_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e5_s3 = e5_ccc_df[[\"development\", \"Latest\", \"Ultimate\"]].rename(\n", + " columns={\"Latest\": \"Closed Claim Counts\", \"Ultimate\": \"Ult Count Using CCC\"}\n", + ")\n", + "e5_s3[[\"Reported Claim Counts\", \"Ult Count Using RCC\"]] = e5_rcc_df[\n", + " [\"Latest\", \"Ultimate\"]\n", + "]\n", "e5_s3[\"Selected Ult CC\"] = e5_cc_ult.round(0).latest_diagonal.to_frame()\n", "e5_s3" ] @@ -2360,11 +2593,15 @@ }, "outputs": [], "source": [ - "print('Part 1 - Disposal Rate Triangle')\n", - "nb_display(cl.DisposalRate().fit_transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult).disposal_rate_tri.round(3))\n", - "print('PART 2 - Average Disposal Rate Factors')\n", + "print(\"Part 1 - Disposal Rate Triangle\")\n", + "nb_display(\n", + " cl.DisposalRate()\n", + " .fit_transform(e5_tri[\"Closed Claim Counts\"], sample_weight=e5_cc_ult)\n", + " .disposal_rate_tri.round(3)\n", + ")\n", + "print(\"PART 2 - Average Disposal Rate Factors\")\n", "nb_display(combine_disposal(e5_drs).round(3).to_frame())\n", - "print('PART 3 - Selected Disposal Rate Factors')\n", + "print(\"PART 3 - Selected Disposal Rate Factors\")\n", "nb_display(e5_dr_selected.disposal_rate_)" ] }, @@ -2402,9 +2639,9 @@ }, "outputs": [], "source": [ - "print('Closed Claim Counts')\n", - "nb_display(e5_tri['Closed Claim Counts'])\n", - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Closed Claim Counts\")\n", + "nb_display(e5_tri[\"Closed Claim Counts\"])\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e5_dr_tri.full_triangle_.cum_to_incr().round(0))" ] }, @@ -2442,13 +2679,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e5_tri[\"Paid Claims\"])\n", - "print('Incremental Paid CLaims')\n", + "print(\"Incremental Paid CLaims\")\n", "nb_display(e5_tri[\"Paid Claims\"].cum_to_incr())\n", - "print('Incremental Closed Claim Counts')\n", + "print(\"Incremental Closed Claim Counts\")\n", "nb_display(e5_tri[\"Closed Claim Counts\"].cum_to_incr())\n", - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)" ] }, @@ -2486,12 +2723,12 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Annual Change based on Exponential Regression')\n", - "nb_display(pd.concat([e5_trend.round(3),e5_trend_ex2001.round(3)]))\n", - "print('Goodness of Fit Test of Exponential Regression (R-Squared)')\n", - "nb_display(pd.concat([e5_rsq.round(3),e5_rsq_ex2001.round(3)]))" + "print(\"Annual Change based on Exponential Regression\")\n", + "nb_display(pd.concat([e5_trend.round(3), e5_trend_ex2001.round(3)]))\n", + "print(\"Goodness of Fit Test of Exponential Regression (R-Squared)\")\n", + "nb_display(pd.concat([e5_rsq.round(3), e5_rsq_ex2001.round(3)]))" ] }, { @@ -2528,14 +2765,14 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Trended Incremental Paid Severities Assuming 5% Annual Trend')\n", + "print(\"Trended Incremental Paid Severities Assuming 5% Annual Trend\")\n", "nb_display(e5_ipsev.trend(0.05))\n", - "print('Average Trended Incremental Paid Severities')\n", + "print(\"Average Trended Incremental Paid Severities\")\n", "nb_display(combine_tri(e5_sevs).round(0).to_frame())\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e5_sevs_sel[e5_sevs_sel.development<=60])" + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e5_sevs_sel[e5_sevs_sel.development <= 60])" ] }, { @@ -2573,26 +2810,34 @@ "outputs": [], "source": [ "e5_s9 = pd.DataFrame(\n", - " data = [[\n", - " e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(), \n", - " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()\n", - " ]],\n", - " index = ['Total Closed Claim Counts'],\n", - " columns = ['Age 60 & Older','Age 72 & Older']\n", - ")\n", - "e5_s9.loc['Total Trended Paid Claims'] = [\n", - " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(), \n", - " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", + " data=[\n", + " [\n", + " e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(),\n", + " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum(),\n", + " ]\n", + " ],\n", + " index=[\"Total Closed Claim Counts\"],\n", + " columns=[\"Age 60 & Older\", \"Age 72 & Older\"],\n", + ")\n", + "e5_s9.loc[\"Total Trended Paid Claims\"] = [\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(),\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum(),\n", + "]\n", + "e5_s9.loc[\"Estimated Trended Tail Severity\"] = e5_s9.iloc[1] / e5_s9.iloc[0]\n", + "e5_s9.loc[\"Estimated Incremental Trended Tail Severity\"] = e5_sevs_sel.iloc[\n", + " :, :, :, 4:6\n", + "].values.flatten()\n", + "e5_s9.loc[\"Selected Trended Paid Severity\"] = e5_ipsev_full.values[0, 0, -1, 4:6]\n", + "print(\"Incremental Closed Claim Counts\")\n", + "nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= \"2004\"])\n", + "print(\"Incremental Paid CLaims\")\n", + "nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= \"2004\"])\n", + "print(\"Trended Incremental Paid Severities\")\n", + "nb_display(\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][\n", + " e5_incr_paid_trended.origin <= \"2004\"\n", " ]\n", - "e5_s9.loc['Estimated Trended Tail Severity'] = e5_s9.iloc[1] / e5_s9.iloc[0]\n", - "e5_s9.loc['Estimated Incremental Trended Tail Severity'] = e5_sevs_sel.iloc[:,:,:,4:6].values.flatten()\n", - "e5_s9.loc['Selected Trended Paid Severity'] = e5_ipsev_full.values[0,0,-1,4:6]\n", - "print('Incremental Closed Claim Counts')\n", - "nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= '2004'])\n", - "print('Incremental Paid CLaims')\n", - "nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= '2004'])\n", - "print('Trended Incremental Paid Severities')\n", - "nb_display(e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][e5_incr_paid_trended.origin <= '2004'])\n", + ")\n", "nb_display(e5_s9)" ] }, @@ -2630,11 +2875,13 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e5_ipsev_full[e5_ipsev_full.origin == '2008'])\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e5_ipsev_full[e5_ipsev_full.origin == \"2008\"])\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate\"\n", + ")\n", "nb_display(e5_ipsev_full_complete)" ] }, @@ -2672,13 +2919,15 @@ }, "outputs": [], "source": [ - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e5_s11_ccc.round(0))\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate\"\n", + ")\n", "nb_display(e5_ipsev_full_complete)\n", - "print('Projected Incremental Paid Claims')\n", + "print(\"Projected Incremental Paid Claims\")\n", "nb_display(e5_s11_ip)\n", - "print('Projected Cumulative Paid Claims')\n", + "print(\"Projected Cumulative Paid Claims\")\n", "nb_display(e5_s11_ip.incr_to_cum())" ] }, @@ -2717,9 +2966,9 @@ "outputs": [], "source": [ "summary_exh(\n", - " e5_tri['Reported Claims']/1000,\n", - " e5_tri['Paid Claims']/1000,\n", - " e5_s11_ip.incr_to_cum()/1000\n", + " e5_tri[\"Reported Claims\"] / 1000,\n", + " e5_tri[\"Paid Claims\"] / 1000,\n", + " e5_s11_ip.incr_to_cum() / 1000,\n", ")" ] }, @@ -2749,12 +2998,9 @@ "execution_count": 0, "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, - "nuid": "2a46eef8-f279-4172-b5e1-a23f079ad80e", + "nuid": "520b1f99-ec88-4331-8cdf-56f3bb0d36e2", "showTitle": false, "tableResultSettingsMap": {}, "title": "" @@ -2762,52 +3008,95 @@ }, "outputs": [], "source": [ - "#Exhibit V Sheet 1\n", - "assert np.all(e5_ccc_selected.cdf_.round(3).values == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100]))\n", - "#Exhibit V Sheet 2\n", - "assert np.all(e5_rcc_selected.cdf_.round(3).values == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000]))\n", - "#Exhibit V Sheet 3\n", - "assert np.all(e5_s3['Selected Ult CC'].values==np.array([873., 720., 626., 629., 588., 553, 438., 609.]))\n", - "#Exhibit V Sheet 4\n", - "assert np.all(e5_dr_selected.disposal_rate_.values == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000]))\n", - "#Exhibit V Sheet 5\n", - "lhs = (e5_dr_tri.full_triangle_.cum_to_incr()-e5_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", - "rhs = np.array([\n", - " 77., \n", - " 24., 70., \n", - " 12., 18., 54., \n", - " 46., 13., 19., 57., \n", - " 52., 45., 13., 19., 56., \n", - " 76., 49., 43., 12., 18., 54., \n", - " 67., 55., 36., 31., 9., 13., 39., \n", - "140., 91., 75., 49., 43., 12., 18., 53.\n", - "])\n", + "# ruff: noqa: E241\n", + "# Exhibit V Sheet 1\n", + "assert np.all(\n", + " e5_ccc_selected.cdf_.round(3).values\n", + " == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100])\n", + ")\n", + "# Exhibit V Sheet 2\n", + "assert np.all(\n", + " e5_rcc_selected.cdf_.round(3).values\n", + " == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000])\n", + ")\n", + "# Exhibit V Sheet 3\n", + "assert np.all(\n", + " e5_s3[\"Selected Ult CC\"].values\n", + " == np.array([873.0, 720.0, 626.0, 629.0, 588.0, 553, 438.0, 609.0])\n", + ")\n", + "# Exhibit V Sheet 4\n", + "assert np.all(\n", + " e5_dr_selected.disposal_rate_.values\n", + " == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000])\n", + ")\n", + "# Exhibit V Sheet 5\n", + "lhs = (\n", + " e5_dr_tri.full_triangle_.cum_to_incr() - e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ").values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 77., \n", + " 24., 70., \n", + " 12., 18., 54., \n", + " 46., 13., 19., 57., \n", + " 52., 45., 13., 19., 56., \n", + " 76., 49., 43., 12., 18., 54., \n", + " 67., 55., 36., 31., 9., 13., 39., \n", + " 140., 91., 75., 49., 43., 12., 18., 53.,\n", + " ]\n", + ")\n", "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", - "#Exhibit V Sheet 7\n", - "assert np.all(e5_trend.loc['latest_4'].values[:7].round(3) == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331]))\n", - "assert np.all(e5_rsq.loc['latest_6'].values[:7].round(3) == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000]))\n", - "#Exhibit V Sheet 8\n", - "assert np.all(e5_sevs_sel.values[...,:5].round(0) == np.array([11259., 32980., 65523., 80544., 140802.]))\n", - "#Exhibit V Sheet 9\n", - "assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160., 175816.]))\n", - "#Exhibit V Sheet 10\n", - "lhs = e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date].round(0).values.flatten()\n", - "rhs = np.array([\n", - " 124949.,\n", - " 131196.,131196.,\n", - " 137756.,137756.,137756.,\n", - " 144644.,144644.,144644.,144644.,\n", - " 121630.,151876.,151876.,151876.,151876.,\n", - " 73056.,\t127711.,159470.,159470.,159470.,159470.,\n", - " 62403.,\t76709.,\t134097.,167443.,167443.,167443.,167443.,\n", - "32980., 65523.,\t80544.,\t140802.,175816.,175816.,175816.,175816.,\n", - "])\n", + "# Exhibit V Sheet 7\n", + "assert np.all(\n", + " e5_trend.loc[\"latest_4\"].values[:7].round(3)\n", + " == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331])\n", + ")\n", + "assert np.all(\n", + " e5_rsq.loc[\"latest_6\"].values[:7].round(3)\n", + " == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000])\n", + ")\n", + "# Exhibit V Sheet 8\n", + "assert np.all(\n", + " e5_sevs_sel.values[..., :5].round(0)\n", + " == np.array([11259.0, 32980.0, 65523.0, 80544.0, 140802.0])\n", + ")\n", + "# Exhibit V Sheet 9\n", + "assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160.0, 175816.0]))\n", + "# Exhibit V Sheet 10\n", + "lhs = (\n", + " e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]\n", + " .round(0)\n", + " .values.flatten()\n", + ")\n", + "rhs = np.array(\n", + " [\n", + " 124949.,\n", + " 131196.,131196.,\n", + " 137756.,137756.,137756.,\n", + " 144644.,144644.,144644.,144644.,\n", + " 121630.,151876.,151876.,151876.,151876.,\n", + " 73056.,\t127711.,159470.,159470.,159470.,159470.,\n", + " 62403.,\t76709.,\t134097.,167443.,167443.,167443.,167443.,\n", + " 32980., 65523.,\t80544.,\t140802.,175816.,175816.,175816.,175816.,\n", + " ]\n", + ")\n", "assert np.all(lhs[~np.isnan(lhs)] == rhs)\n", - "#Exhibit V Sheet 11\n", + "# Exhibit V Sheet 11\n", "assert np.allclose(\n", - " e5_s11_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", - " np.array([39497433., 44743308., 35510981., 37766027., 42007442., 41113459., 32859080., 47109641.]),\n", - " rtol = .001\n", + " e5_s11_ip.incr_to_cum().iloc[:, :, :, -1].values.round(0).flatten(),\n", + " np.array(\n", + " [\n", + " 39497433.0,\n", + " 44743308.0,\n", + " 35510981.0,\n", + " 37766027.0,\n", + " 42007442.0,\n", + " 41113459.0,\n", + " 32859080.0,\n", + " 47109641.0,\n", + " ]\n", + " ),\n", + " rtol=0.001,\n", ")" ] }, @@ -2845,61 +3134,77 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e6_tri = e2_tri[e2_tri.origin >= '2001'][e2_tri.development <= 96]\n", + "# loading data and assumptions\n", + "e6_tri = e2_tri[e2_tri.origin >= \"2001\"][e2_tri.development <= 96]\n", "e6_disp_assumptions = {}\n", - "e6_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e6_disp_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e6_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e6_disp_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e6_disp_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e6_disp_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e6_sev_assumptions = {}\n", - "e6_sev_assumptions['simple_3'] = {'n_periods':3}\n", - "e6_sev_assumptions['simple_2'] = {'n_periods':2}\n", - "e6_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "e6_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3}\n", + "e6_sev_assumptions[\"simple_2\"] = {\"n_periods\": 2}\n", + "e6_sev_assumptions[\"medial_5x1\"] = {\"n_periods\": 5, \"drop_high\": 1, \"drop_low\": 1}\n", "\n", - "#calculating disposal rate and complete the closed count triangle\n", - "e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= '2001']\n", - "e6_drs = average_dr(e6_tri['Closed Claim Counts'],e6_rcc_ult,e6_disp_assumptions)\n", - "e6_dr_selected = e6_drs['simple_2']\n", + "# calculating disposal rate and complete the closed count triangle\n", + "e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= \"2001\"]\n", + "e6_drs = average_dr(e6_tri[\"Closed Claim Counts\"], e6_rcc_ult, e6_disp_assumptions)\n", + "e6_dr_selected = e6_drs[\"simple_2\"]\n", "e6_dr_selected.disposal_rate_ = e6_dr_selected.disposal_rate_.round(3)\n", - "e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)\n", + "e6_dr_tri = e6_dr_selected.transform(\n", + " e6_tri[\"Closed Claim Counts\"], sample_weight=e6_rcc_ult\n", + ")\n", "\n", - "#calculate and select incremental paid severity\n", - "#in a real world scenario, it's possible to calculate incremental paid severity like this\n", + "# calculate and select incremental paid severity\n", + "# in a real world scenario, it's possible to calculate incremental paid severity like this\n", "# e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", - "#however, the cumulative paid data in Friedland is only given in thousands\n", - "#therefore, we will directly use the incremental paid severity data as given\n", - "e6_ipsev = e6_tri['Incremental Paid Severity']\n", + "# however, the cumulative paid data in Friedland is only given in thousands\n", + "# therefore, we will directly use the incremental paid severity data as given\n", + "e6_ipsev = e6_tri[\"Incremental Paid Severity\"]\n", "e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", - "e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)\n", - "e6_sevs_sel = e6_sevs['simple_2'].copy()\n", - "e6_sevs_sel.origin = ['2008']\n", + "e6_sevs = average_sev(e6_ipsev_adj, e6_sev_assumptions)\n", + "e6_sevs_sel = e6_sevs[\"simple_2\"].copy()\n", + "e6_sevs_sel.origin = [\"2008\"]\n", "\n", - "#calculate and select tail paid severity\n", + "# calculate and select tail paid severity\n", "e6_incr_ccc = e6_tri[\"Closed Claim Counts\"].cum_to_incr()\n", "e6_incr_paid = e6_tri[\"Paid Claims\"].cum_to_incr() * 1000\n", "e6_incr_paid_trended = e6_ipsev_adj * e6_incr_ccc\n", "\n", "e6_ipsev_full = e6_ipsev_adj.copy()\n", - "#extending the severity triangle to 108 months\n", - "e6_ipsev_full = cl.concat((e6_ipsev_full,e6_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", - "#setting the before 72 months to selected incremental\n", - "e6_ipsev_full.iloc[:,:,:,:6] = e6_sevs_sel.iloc[:,:,:,:6]\n", - "#setting the after 86 months to selected tail\n", - "e6_ipsev_full.iloc[:,:,:,6:] = (\n", + "# extending the severity triangle to 108 months\n", + "e6_ipsev_full = cl.concat(\n", + " (e6_ipsev_full, e6_ipsev_full.latest_diagonal.rename(\"development\", [9999])), axis=3\n", + ")\n", + "# setting the before 72 months to selected incremental\n", + "e6_ipsev_full.iloc[:, :, :, :6] = e6_sevs_sel.iloc[:, :, :, :6]\n", + "# setting the after 86 months to selected tail\n", + "e6_ipsev_full.iloc[:, :, :, 6:] = (\n", " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", - " /\n", - " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum() \n", + " / e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()\n", ")\n", - "#setting the valuation_date to the future\n", + "# setting the valuation_date to the future\n", "e6_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", - "#trending back from latest accident year\n", - "e6_ipsev_full_detrended = e6_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01') / xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", - "#compositing the actual incremental severities with dtrended selected\n", - "e6_ipsev_full_complete = e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date] + e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date]\n", + "# trending back from latest accident year\n", + "e6_ipsev_full_detrended = (\n", + " e6_ipsev_full.trend(1 / 1.05 - 1, start=\"2008-12-31\", end=\"2001-01-01\")\n", + " / xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", + ")\n", + "# compositing the actual incremental severities with dtrended selected\n", + "e6_ipsev_full_complete = (\n", + " e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date]\n", + " + e6_ipsev_full_detrended[\n", + " e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date\n", + " ]\n", + ")\n", "\n", - "#complete the incremental paid triangle\n", + "# complete the incremental paid triangle\n", "e6_s7_ccc = e6_dr_tri.full_triangle_.cum_to_incr()\n", - "e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000\n" + "e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000" ] }, { @@ -2936,13 +3241,17 @@ }, "outputs": [], "source": [ - "print('Part 1 - Disposal Rate Triangle')\n", - "nb_display(cl.DisposalRate().fit_transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult).disposal_rate_tri.round(3))\n", + "print(\"Part 1 - Disposal Rate Triangle\")\n", + "nb_display(\n", + " cl.DisposalRate()\n", + " .fit_transform(e6_tri[\"Closed Claim Counts\"], sample_weight=e6_rcc_ult)\n", + " .disposal_rate_tri.round(3)\n", + ")\n", "\n", - "print('PART 2 - Average Disposal Rate Factors')\n", + "print(\"PART 2 - Average Disposal Rate Factors\")\n", "nb_display(combine_disposal(e6_drs).round(3).to_frame())\n", "\n", - "print('PART 3 - Selected Disposal Rate Factors')\n", + "print(\"PART 3 - Selected Disposal Rate Factors\")\n", "nb_display(e6_dr_selected.disposal_rate_)" ] }, @@ -2980,9 +3289,9 @@ }, "outputs": [], "source": [ - "print('Closed Claim Counts')\n", - "nb_display(e6_tri['Closed Claim Counts'])\n", - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Closed Claim Counts\")\n", + "nb_display(e6_tri[\"Closed Claim Counts\"])\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e6_dr_tri.full_triangle_.cum_to_incr().round(0))" ] }, @@ -3020,13 +3329,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e6_tri[\"Paid Claims\"])\n", - "print('Incremental Paid CLaims')\n", + "print(\"Incremental Paid CLaims\")\n", "nb_display(e6_tri[\"Paid Claims\"].cum_to_incr())\n", - "print('Incremental Closed Claim Counts')\n", + "print(\"Incremental Closed Claim Counts\")\n", "nb_display(e6_tri[\"Closed Claim Counts\"].cum_to_incr())\n", - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)" ] }, @@ -3064,14 +3373,16 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)\n", - "print('Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform')\n", + "print(\n", + " \"Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform\"\n", + ")\n", "nb_display(e6_ipsev_adj)\n", - "print('Average Trended Incremental Paid Severities')\n", + "print(\"Average Trended Incremental Paid Severities\")\n", "nb_display(combine_tri(e6_sevs).round(0).to_frame())\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e6_sevs_sel[e6_sevs_sel.development<=72])" + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e6_sevs_sel[e6_sevs_sel.development <= 72])" ] }, { @@ -3108,27 +3419,35 @@ }, "outputs": [], "source": [ - "print('Incremental Closed Claim Counts')\n", - "nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= '2003'])\n", - "print('Incremental Paid CLaims')\n", - "nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= '2003'])\n", - "print('Trended Incremental Paid Severities')\n", - "nb_display(e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][e6_incr_paid_trended.origin <= '2003'])\n", - "e6_s5 = pd.DataFrame(\n", - " data = [[\n", - " e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(), \n", - " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()\n", - " ]],\n", - " index = ['Total Closed Claim Counts'],\n", - " columns = ['Age 72 & Older','Age 84 & Older']\n", - ")\n", - "e6_s5.loc['Total Trended Paid Claims'] = [\n", - " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(), \n", - " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", + "print(\"Incremental Closed Claim Counts\")\n", + "nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= \"2003\"])\n", + "print(\"Incremental Paid CLaims\")\n", + "nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= \"2003\"])\n", + "print(\"Trended Incremental Paid Severities\")\n", + "nb_display(\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][\n", + " e6_incr_paid_trended.origin <= \"2003\"\n", " ]\n", - "e6_s5.loc['Estimated Trended Tail Severity'] = e6_s5.iloc[1] / e6_s5.iloc[0]\n", - "e6_s5.loc['Estimated Incremental Trended Tail Severity'] = e6_sevs_sel.iloc[:,:,:,5:7].values.flatten()\n", - "e6_s5.loc['Selected Trended Paid Severity'] = e6_ipsev_full.values[0,0,-1,5:7]\n", + ")\n", + "e6_s5 = pd.DataFrame(\n", + " data=[\n", + " [\n", + " e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(),\n", + " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum(),\n", + " ]\n", + " ],\n", + " index=[\"Total Closed Claim Counts\"],\n", + " columns=[\"Age 72 & Older\", \"Age 84 & Older\"],\n", + ")\n", + "e6_s5.loc[\"Total Trended Paid Claims\"] = [\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(),\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum(),\n", + "]\n", + "e6_s5.loc[\"Estimated Trended Tail Severity\"] = e6_s5.iloc[1] / e6_s5.iloc[0]\n", + "e6_s5.loc[\"Estimated Incremental Trended Tail Severity\"] = e6_sevs_sel.iloc[\n", + " :, :, :, 5:7\n", + "].values.flatten()\n", + "e6_s5.loc[\"Selected Trended Paid Severity\"] = e6_ipsev_full.values[0, 0, -1, 5:7]\n", "nb_display(e6_s5)" ] }, @@ -3166,11 +3485,13 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e6_ipsev_full[e6_ipsev_full.origin == '2008'])\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e6_ipsev_full[e6_ipsev_full.origin == \"2008\"])\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment\"\n", + ")\n", "nb_display(e6_ipsev_full_complete)" ] }, @@ -3208,13 +3529,15 @@ }, "outputs": [], "source": [ - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e6_s7_ccc.round(0))\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment\"\n", + ")\n", "nb_display(e6_ipsev_full_complete)\n", - "print('Projected Incremental Paid Claims')\n", + "print(\"Projected Incremental Paid Claims\")\n", "nb_display(e6_s7_ip)\n", - "print('Projected Cumulative Paid Claims')\n", + "print(\"Projected Cumulative Paid Claims\")\n", "nb_display(e6_s7_ip.incr_to_cum())" ] }, @@ -3252,11 +3575,7 @@ }, "outputs": [], "source": [ - "summary_exh(\n", - " e6_tri['Reported Claims'],\n", - " e6_tri['Paid Claims'],\n", - " e6_s7_ip.incr_to_cum()\n", - ")" + "summary_exh(e6_tri[\"Reported Claims\"], e6_tri[\"Paid Claims\"], e6_s7_ip.incr_to_cum())" ] }, { @@ -3264,12 +3583,9 @@ "execution_count": 0, "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, - "nuid": "e03f91fe-ff00-47ee-9994-3caf56a38ab1", + "nuid": "d192b52a-32a1-4b88-895c-7e6747278d7f", "showTitle": false, "tableResultSettingsMap": {}, "title": "" @@ -3277,44 +3593,58 @@ }, "outputs": [], "source": [ - "#Exhibit VI Sheet 1\n", - "assert np.all(e6_dr_selected.disposal_rate_.values == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000]))\n", - "#Exhibit VI Sheet 2\n", - "lhs = (e6_dr_tri.full_triangle_.cum_to_incr()-e6_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", - "rhs = np.array([\n", - " 9., \n", - " 21., 10., \n", - " 39., 16., 8., \n", - " 109., 78., 31., 16., \n", - " 235., 103., 74., 29., 15., \n", - " 177., 155., 68., 48., 19., 10., \n", - " 160., 133., 116., 51., 36., 15., 7., \n", - "389., 156., 130., 114., 50., 36., 14., 7.\n", - "])\n", + "# Exhibit VI Sheet 1\n", + "assert np.all(\n", + " e6_dr_selected.disposal_rate_.values\n", + " == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000])\n", + ")\n", + "# Exhibit VI Sheet 2\n", + "lhs = (\n", + " e6_dr_tri.full_triangle_.cum_to_incr() - e6_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ").values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 9., \n", + " 21., 10., \n", + " 39., 16., 8., \n", + " 109., 78., 31., 16., \n", + " 235., 103., 74., 29., 15., \n", + " 177., 155., 68., 48., 19., 10., \n", + " 160., 133., 116., 51., 36., 15., 7., \n", + " 389., 156., 130., 114., 50., 36., 14., 7.,\n", + " ]\n", + ")\n", "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", - "#Exhibit VI Sheet 4\n", - "assert np.all(e6_sevs_sel.values[...,:6].round(0) == np.array([11807.,15165.,26043.,35183.,41908.,62206.]))\n", - "#Exhibit VI Sheet 5\n", - "assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.,70432.]))\n", - "#Exhibit VI Sheet 6\n", - "lhs = e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date].values.flatten()\n", - "rhs = np.array([\n", - " 74709., \n", - " 78444., 78444., \n", - " 82367., 82367., 82367., \n", - " 76384., 86485., 86485., 86485., \n", - " 54032., 80203., 90809., 90809., 90809., \n", - " 42549., 50682., 75230., 85179., 85179., 85179., \n", - " 24803., 33508., 39912., 59244., 67079., 67079., 67079., \n", - "15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., \n", - "])\n", + "# Exhibit VI Sheet 4\n", + "assert np.all(\n", + " e6_sevs_sel.values[..., :6].round(0)\n", + " == np.array([11807.0, 15165.0, 26043.0, 35183.0, 41908.0, 62206.0])\n", + ")\n", + "# Exhibit VI Sheet 5\n", + "assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.0, 70432.0]))\n", + "# Exhibit VI Sheet 6\n", + "lhs = e6_ipsev_full_detrended[\n", + " e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date\n", + "].values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 74709., \n", + " 78444., 78444., \n", + " 82367., 82367., 82367., \n", + " 76384., 86485., 86485., 86485., \n", + " 54032., 80203., 90809., 90809., 90809., \n", + " 42549., 50682., 75230., 85179., 85179., 85179., \n", + " 24803., 33508., 39912., 59244., 67079., 67079., 67079., \n", + " 15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., \n", + " ]\n", + ")\n", "assert np.allclose(lhs[~np.isnan(lhs)], rhs, atol=1)\n", - "#Exhibit VI Sheet 7\n", + "# Exhibit VI Sheet 7\n", "assert np.allclose(\n", - " e6_s7_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", - " np.array([39192.,46869.,44479.,71906.,71684.,49913.,31805.,29828.]),\n", - " rtol = .001\n", - ")\n" + " e6_s7_ip.incr_to_cum().iloc[:, :, :, -1].values.round(0).flatten(),\n", + " np.array([39192.0, 46869.0, 44479.0, 71906.0, 71684.0, 49913.0, 31805.0, 29828.0]),\n", + " rtol=0.001,\n", + ")" ] } ], From 9e981292cd281c4786a9e458f59b1f63f7c28584 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 22:32:43 +0000 Subject: [PATCH 25/27] ruff fix --- chainladder/utils/tests/test_utilities.py | 18 +- chainladder/utils/triangle_weight.py | 25 +- chainladder/utils/weighted_regression.py | 6 +- docs/friedland/chapter_11.ipynb | 1852 ++++++++++++--------- 4 files changed, 1115 insertions(+), 786 deletions(-) diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 484c6ae3b..5f33c24ee 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -189,16 +189,16 @@ def test_model_diagnostics_groupby(prism, atol): est = cl.Chainladder().fit(dev.transform(prism["Incurred"])) lhs = cl.model_diagnostics(est, groupby=['Line']) rhs = cl.model_diagnostics(cl.Chainladder().fit(dev.transform(prism["Incurred"].groupby('Line').sum()))) - assert np.allclose(lhs['Ultimate'].values,rhs['Ultimate'].values,atol=atol, equal_nan=True) - assert np.allclose(np.nan_to_num(lhs['IBNR'].values),np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) + assert np.allclose(lhs['Ultimate'].values, rhs['Ultimate'].values,atol=atol, equal_nan=True) + assert np.allclose(np.nan_to_num(lhs['IBNR'].values), np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) def test_concat_immutability(raa): u = cl.Chainladder().fit(raa).ultimate_ - l = raa.latest_diagonal - u.columns = l.columns + latest = raa.latest_diagonal + u.columns = latest.columns u_new = copy.deepcopy(u) - cl.concat((l, u), axis=3) + cl.concat((latest, u), axis=3) assert u == u_new @@ -680,7 +680,7 @@ def test_options_defaults() -> None: """ options = cl.Options() assert options.ARRAY_BACKEND == "numpy" - assert options.AUTO_SPARSE == True + assert options.AUTO_SPARSE assert options.ARRAY_PRIORITY == ["dask", "sparse", "cupy", "numpy"] assert isinstance(options.ULT_VAL, str) @@ -1271,6 +1271,6 @@ def test_triangleweight_full_triangle(raa: Triangle) -> None: Testing new path that allows weights on full triangles ''' ult = cl.Chainladder().fit(raa) - tw = cl.TriangleWeight(n_periods = 4).fit(raa) - tw_full = cl.TriangleWeight(n_periods = 4).fit(ult.full_triangle_) - assert tw.w_.iloc[:,:,:,0] == tw_full.w_.iloc[:,:,:,0] \ No newline at end of file + tw = cl.TriangleWeight(n_periods=4).fit(raa) + tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) + assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] \ No newline at end of file diff --git a/chainladder/utils/triangle_weight.py b/chainladder/utils/triangle_weight.py index d32618409..a122515ce 100644 --- a/chainladder/utils/triangle_weight.py +++ b/chainladder/utils/triangle_weight.py @@ -5,7 +5,6 @@ import numpy as np import pandas as pd -from chainladder.utils.sparse import sp from sklearn.base import BaseEstimator, TransformerMixin import warnings @@ -19,7 +18,7 @@ from chainladder.core.typing import TriangleProtocol -class TriangleWeight(BaseEstimator,TransformerMixin): +class TriangleWeight(BaseEstimator, TransformerMixin): """ Helper class that produces a triangle of weights based on pattern selections @@ -123,7 +122,7 @@ def fit(self, X: TriangleProtocol, y: None = None, sample_weight: None = None): # in practie, there is no realistic use case for a sparse triangle to need weights backend = "numpy" if X.array_backend in ["sparse", "numpy"] else "cupy" obj = X.set_backend(backend) - self.w_ = self._set_weight_func(X=obj,secondary_rank=sample_weight) + self.w_ = self._set_weight_func(X=obj, secondary_rank=sample_weight) return self def transform(self, X: TriangleProtocol) -> Triangle: @@ -146,8 +145,8 @@ def transform(self, X: TriangleProtocol) -> Triangle: def _cascade_param( self, - size:int, - param: bool | int | float | str | None | list[bool|int|float|str|None], + size: int, + param: bool | int | float | str | None | list[bool | int | float | str | None], default_param: bool | int | float | str | None ) -> np.ndarray: """ @@ -237,7 +236,7 @@ def _assign_n_periods_weight_func(self, X: TriangleProtocol) -> np.ndarray: dev_len = X.shape[3] n_periods_param = self._cascade_param(dev_len, self.n_periods, -1) - #helper function that generates the weights for individual n_periods + # helper function that generates the weights for individual n_periods def _assign_n_periods_weight_int(X, n_periods): xp = X.get_array_module() val_offset = { @@ -250,10 +249,10 @@ def _assign_n_periods_weight_int(X, n_periods): return X.values * 0 + 1 else: z = -n_periods * val_offset[X.development_grain][X.origin_grain] - #adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) + # adding new path to handle full triangle (e.g. full_triangle_, ultimate_, et.c) if X.is_full: w = X.copy() - w.values[:,:,:-n_periods,:] = np.nan + w.values[:, :, :-n_periods, :] = np.nan else: val_date_min = X.valuation[X.valuation <= X.valuation_date] val_date_min = val_date_min.drop_duplicates().sort_values() @@ -346,8 +345,8 @@ def _drop_n_func( # getting weights that are within the max and min ranks w = ( - X_ranks < max_rank[:,:,None,:] - ) & (X_ranks > min_rank[:,:,None,:] - 1) + X_ranks < max_rank[:, :, None, :] + ) & (X_ranks > min_rank[:, :, None, :] - 1) # NOTE: The "Some exclusions have been ignored..." UserWarning below is # asserted by the test suite (see chainladder/development/tests/ @@ -484,8 +483,8 @@ def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: w = ~np.isnan(X_val) # weights without considering preserve - index_array_weights = (X_val < drop_above_array[:,:,None,:]) & ( - X_val > drop_below_array[:,:,None,:] + index_array_weights = (X_val < drop_above_array[:, :, None, :]) & ( + X_val > drop_below_array[:, :, None, :] ) # counting remaining factors @@ -494,7 +493,7 @@ def _drop_x_func(self, X: TriangleProtocol) -> np.ndarray: # applying preserve warning_flag = np.any(valid_count < preserve_array) w = np.where( - valid_count[:,:,None,:] < preserve_array[:,:,None,:], w, index_array_weights + valid_count[:, :, None, :] < preserve_array[:, :, None, :], w, index_array_weights ) # NOTE: The "Some exclusions have been ignored..." UserWarning below is diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 9e118c946..1b6aec586 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -65,9 +65,9 @@ def infer_x_w(self): def fit( self, - X:BackendArray, - y:BackendArray|None=None, - sample_weight:BackendArray|None=None, + X: BackendArray, + y: BackendArray | None = None, + sample_weight: BackendArray | None = None, average: Literal["volume", "simple", "regression", "geometric"] | None = None ): """ diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index de5953adc..4a270d4f0 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -43,86 +43,106 @@ "import chainladder as cl\n", "from IPython.display import display as nb_display\n", "\n", + "\n", "def format_exh(\n", - " exh_df:pd.DataFrame,\n", - " value_cols: list[str] = [],\n", - " factor_cols: list[str] = [],\n", - " other_formats: dict = {},\n", - " date_format: str = \"{:%Y}\"\n", + " exh_df: pd.DataFrame,\n", + " value_cols: list[str] = [],\n", + " factor_cols: list[str] = [],\n", + " other_formats: dict = {},\n", + " date_format: str = \"{:%Y}\",\n", "):\n", - " return exh_df \\\n", - " .reset_index() \\\n", - " .rename(columns={'origin':'Accident Year'}) \\\n", - " .style \\\n", - " .hide(axis='index') \\\n", - " .set_properties(**{\"text-align\":\"right\"}) \\\n", - " .format({\n", - " 'Accident Year':date_format,\n", - " **{x:\"{:,.0f}\" for x in value_cols},\n", - " **{x:\"{:,.3f}\" for x in factor_cols},\n", - " **other_formats\n", - " }) \n", + " return (\n", + " exh_df.reset_index()\n", + " .rename(columns={\"origin\": \"Accident Year\"})\n", + " .style.hide(axis=\"index\")\n", + " .set_properties(**{\"text-align\": \"right\"})\n", + " .format(\n", + " {\n", + " \"Accident Year\": date_format,\n", + " **{x: \"{:,.0f}\" for x in value_cols},\n", + " **{x: \"{:,.3f}\" for x in factor_cols},\n", + " **other_formats,\n", + " }\n", + " )\n", + " )\n", "\n", - "#create reported/paid summary exhibit\n", - "def summary_exh(reported_tri:cl.Triangle,paid_tri:cl.Triangle,ult:cl.Triangle) -> pd.DataFrame:\n", + "\n", + "# create reported/paid summary exhibit\n", + "def summary_exh(\n", + " reported_tri: cl.Triangle, paid_tri: cl.Triangle, ult: cl.Triangle\n", + ") -> pd.DataFrame:\n", " output = reported_tri.latest_diagonal.to_frame()\n", - " output.columns = ['Reported']\n", - " output['Paid'] = paid_tri.latest_diagonal.to_frame()\n", - " #using floating point offset to achieve standard rounding\n", - " output['Ult Claims'] = ult.latest_diagonal.to_frame()\n", - " output['Case Outstanding'] = output[\"Reported\"] - output[\"Paid\"]\n", - " output['IBNR'] = output[\"Ult Claims\"] - output[\"Reported\"]\n", - " output['Unpaid'] = output[\"Ult Claims\"] - output[\"Paid\"]\n", - " output.index.name = 'origin'\n", + " output.columns = [\"Reported\"]\n", + " output[\"Paid\"] = paid_tri.latest_diagonal.to_frame()\n", + " # using floating point offset to achieve standard rounding\n", + " output[\"Ult Claims\"] = ult.latest_diagonal.to_frame()\n", + " output[\"Case Outstanding\"] = output[\"Reported\"] - output[\"Paid\"]\n", + " output[\"IBNR\"] = output[\"Ult Claims\"] - output[\"Reported\"]\n", + " output[\"Unpaid\"] = output[\"Ult Claims\"] - output[\"Paid\"]\n", + " output.index.name = \"origin\"\n", " return output\n", "\n", - "#create a dict of developed triangles for each of the selection assumptions on a given page\n", - "def average_dev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", - " return {k:cl.Development(**v).fit_transform(tri) for k,v in avg_params.items()}\n", "\n", - "#combine a dict of triangles into a singla triangle\n", + "# create a dict of developed triangles for each of the selection assumptions on a given page\n", + "def average_dev(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.Triangle]:\n", + " return {k: cl.Development(**v).fit_transform(tri) for k, v in avg_params.items()}\n", + "\n", + "\n", + "# combine a dict of triangles into a singla triangle\n", "def combine_tri(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " avgs = [v.rename('index',k) for k,v in devs.items()]\n", - " return cl.concat(avgs,axis=0)\n", + " avgs = [v.rename(\"index\", k) for k, v in devs.items()]\n", + " return cl.concat(avgs, axis=0)\n", + "\n", "\n", - "#combine the ldf_ of a dict of triangles into a singla triangle\n", + "# combine the ldf_ of a dict of triangles into a singla triangle\n", "def combine_ldf(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " return combine_tri({k:v.ldf_ for k,v in devs.items()})\n", + " return combine_tri({k: v.ldf_ for k, v in devs.items()})\n", "\n", - "#create a dict of DisposalRate estimators for each of the selection assumptions on a given page\n", - "def average_dr(tri: cl.Triangle, w:cl.Triangle, avg_params: dict[str,int]) -> dict[cl.DisposalRate]:\n", - " return {k:cl.DisposalRate(**v).fit(X=tri,sample_weight = w) for k,v in avg_params.items()}\n", "\n", - "#combine the disposal_rate_ of a dict of triangles into a singla triangle\n", + "# create a dict of DisposalRate estimators for each of the selection assumptions on a given page\n", + "def average_dr(\n", + " tri: cl.Triangle, w: cl.Triangle, avg_params: dict[str, int]\n", + ") -> dict[cl.DisposalRate]:\n", + " return {\n", + " k: cl.DisposalRate(**v).fit(X=tri, sample_weight=w)\n", + " for k, v in avg_params.items()\n", + " }\n", + "\n", + "\n", + "# combine the disposal_rate_ of a dict of triangles into a singla triangle\n", "def combine_disposal(devs: dict[cl.Triangle]) -> cl.Triangle:\n", - " return combine_tri({k:v.disposal_rate_ for k,v in devs.items()})\n", + " return combine_tri({k: v.disposal_rate_ for k, v in devs.items()})\n", + "\n", + "\n", + "# create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def regs(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.WeightedRegression]:\n", + " return {\n", + " k: cl.WeightedRegression(\n", + " axis=2, thru_orig=False, xp=tri.get_array_module()\n", + " ).fit(\n", + " np.ones(tri.shape) * tri.origin.year.values[None, None, :, None],\n", + " np.log(tri.values),\n", + " cl.TriangleWeight(**v).fit(tri).w_.values,\n", + " )\n", + " for k, v in avg_params.items()\n", + " }\n", "\n", - "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", - "def regs(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.WeightedRegression]:\n", - " return {k:cl.WeightedRegression(\n", - " axis=2,\n", - " thru_orig=False,\n", - " xp=tri.get_array_module()\n", - " ).fit(\n", - " np.ones(tri.shape) * tri.origin.year.values[None,None,:,None],\n", - " np.log(tri.values),\n", - " cl.TriangleWeight(**v).fit(tri).w_.values\n", - " ) for k,v in avg_params.items()}\n", "\n", - "#combine all the trends and r-squared values into pandas DataFrame\n", - "def reg_outputs(regs: dict[cl.WeightedRegression],devs:pd.Series) -> pd.DataFrame:\n", - " trend = {k:(np.exp(v.slope_)-1).flatten() for k,v in regs.items()}\n", - " trend = pd.DataFrame.from_dict(trend,orient='index')\n", + "# combine all the trends and r-squared values into pandas DataFrame\n", + "def reg_outputs(regs: dict[cl.WeightedRegression], devs: pd.Series) -> pd.DataFrame:\n", + " trend = {k: (np.exp(v.slope_) - 1).flatten() for k, v in regs.items()}\n", + " trend = pd.DataFrame.from_dict(trend, orient=\"index\")\n", " trend.columns = devs\n", - " rsq = {k:v.rsq_.flatten() for k,v in regs.items()}\n", - " rsq = pd.DataFrame.from_dict(rsq,orient='index')\n", + " rsq = {k: v.rsq_.flatten() for k, v in regs.items()}\n", + " rsq = pd.DataFrame.from_dict(rsq, orient=\"index\")\n", " rsq.columns = devs\n", - " return trend,rsq\n", + " return trend, rsq\n", + "\n", "\n", - "#create a dict of weighted regression objects for each of the selection assumptions on a given page\n", - "def average_sev(tri: cl.Triangle, avg_params: dict[str,int]) -> dict[cl.Triangle]:\n", + "# create a dict of weighted regression objects for each of the selection assumptions on a given page\n", + "def average_sev(tri: cl.Triangle, avg_params: dict[str, int]) -> dict[cl.Triangle]:\n", " output = {}\n", - " for k,v in avg_params.items():\n", + " for k, v in avg_params.items():\n", " w = cl.TriangleWeight(**v).fit(tri)\n", " weighted_tri = tri * w.w_.values\n", " output[k] = weighted_tri.mean()\n", @@ -167,43 +187,59 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e1_tri = cl.load_sample('friedland_auto_freq_sev')\n", + "# loading data and assumptions\n", + "e1_tri = cl.load_sample(\"friedland_auto_freq_sev\")\n", "e1_cnt_assumptions = {}\n", - "e1_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e1_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e1_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e1_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e1_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e1_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e1_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e1_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e1_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e1_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e1_sev_assumptions = {}\n", - "e1_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e1_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e1_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e1_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e1_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e1_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "\n", - "#developing closed claim counts\n", - "e1_ccc_devs = average_dev(e1_tri['Closed Claim Counts'],e1_cnt_assumptions)\n", - "e1_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_ccc_devs['simple_3'])\n", + "# developing closed claim counts\n", + "e1_ccc_devs = average_dev(e1_tri[\"Closed Claim Counts\"], e1_cnt_assumptions)\n", + "e1_ccc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_ccc_devs[\"simple_3\"]\n", + ")\n", "e1_ccc_selected.ldf_ = e1_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e1_rcc_devs = average_dev(e1_tri['Reported Claim Counts'],e1_cnt_assumptions)\n", - "e1_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rcc_devs['simple_3'])\n", + "# developing reported claim counts\n", + "e1_rcc_devs = average_dev(e1_tri[\"Reported Claim Counts\"], e1_cnt_assumptions)\n", + "e1_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_rcc_devs[\"simple_3\"]\n", + ")\n", "e1_rcc_selected.ldf_ = e1_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e1_ccc_cl = cl.Chainladder().fit(e1_ccc_selected)\n", "e1_rcc_cl = cl.Chainladder().fit(e1_rcc_selected)\n", - "e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_)/2\n", - "e1_cc_ult.iloc[:,:,-1,:] = e1_rcc_cl.ultimate_.iloc[:,:,-1,:]\n", + "e1_cc_ult = (e1_ccc_cl.ultimate_ + e1_rcc_cl.ultimate_) / 2\n", + "e1_cc_ult.iloc[:, :, -1, :] = e1_rcc_cl.ultimate_.iloc[:, :, -1, :]\n", "\n", - "#calculating and developing reported severity\n", - "e1_rsev = e1_tri['Reported Claims'] / e1_tri['Reported Claim Counts']\n", - "e1_rsev_devs = average_dev(e1_rsev,e1_sev_assumptions)\n", - "e1_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e1_rsev_devs['medial_5x1'])\n", + "# calculating and developing reported severity\n", + "e1_rsev = e1_tri[\"Reported Claims\"] / e1_tri[\"Reported Claim Counts\"]\n", + "e1_rsev_devs = average_dev(e1_rsev, e1_sev_assumptions)\n", + "e1_rsev_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e1_rsev_devs[\"medial_5x1\"]\n", + ")\n", "e1_rsev_selected.ldf_ = e1_rsev_selected.ldf_.round(3)\n", "e1_rsev_cl = cl.Chainladder().fit(e1_rsev_selected)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e1_ult = e1_rsev_cl.ultimate_ * e1_cc_ult / 1000" ] }, @@ -241,19 +277,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e1_tri['Closed Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e1_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e1_tri[\"Closed Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e1_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e1_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e1_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display(1/e1_ccc_selected.cdf_.round(3))" + "print(\"Percent Closed\")\n", + "nb_display(1 / e1_ccc_selected.cdf_.round(3))" ] }, { @@ -290,19 +326,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e1_tri['Reported Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e1_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e1_tri[\"Reported Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e1_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e1_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e1_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display(1/e1_rcc_selected.cdf_.round(3))" + "print(\"Percent Reported\")\n", + "nb_display(1 / e1_rcc_selected.cdf_.round(3))" ] }, { @@ -339,34 +375,44 @@ }, "outputs": [], "source": [ - "e1_ccc_df = cl.model_diagnostics(e1_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_rcc_df = cl.model_diagnostics(e1_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s3 = e1_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e1_ccc_df = (\n", + " cl.model_diagnostics(e1_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_rcc_df = (\n", + " cl.model_diagnostics(e1_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_s3 = e1_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e1_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e1_rcc_df[['Latest','CDF','Ultimate']]\n", + "e1_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e1_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e1_s3[\"Selected Ult CC\"] = e1_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e1_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", " ],\n", " {},\n", - " \"{:%Y%m}\"\n", - ") " + " \"{:%Y%m}\",\n", + ")" ] }, { @@ -403,7 +449,7 @@ }, "outputs": [], "source": [ - "(e1_tri['Closed Claim Counts']/e1_tri['Reported Claim Counts']).round(3)" + "(e1_tri[\"Closed Claim Counts\"] / e1_tri[\"Reported Claim Counts\"]).round(3)" ] }, { @@ -440,7 +486,7 @@ }, "outputs": [], "source": [ - "nb_display(e1_tri['Reported Claims']/1000)\n", + "nb_display(e1_tri[\"Reported Claims\"] / 1000)\n", "nb_display(e1_rsev)" ] }, @@ -478,15 +524,15 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e1_rsev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e1_rsev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factors')\n", + "print(\"PART 3 - Average Age-to-Age Factors\")\n", "nb_display(combine_ldf(e1_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e1_rsev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e1_rsev_selected.cdf_.round(3))" ] }, @@ -524,27 +570,31 @@ }, "outputs": [], "source": [ - "e1_rsev_ult_df = cl.model_diagnostics(e1_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e1_s7 = e1_rsev_ult_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e1_rsev_ult_df = (\n", + " cl.model_diagnostics(e1_rsev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e1_s7 = e1_rsev_ult_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Reported Severity',\n", - " 'Ultimate':'Ult Severity'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Reported Severity\",\n", + " \"Ultimate\": \"Ult Severity\",\n", " }\n", ")\n", - "e1_s7['Selected Ult CC'] = e1_s3['Selected Ult CC']\n", + "e1_s7[\"Selected Ult CC\"] = e1_s3[\"Selected Ult CC\"]\n", "e1_s7[\"Ult Claims\"] = e1_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e1_s7,\n", " [\n", - " 'Reported Severity',\n", - " 'Ult Severity',\n", - " 'Selected Ult CC',\n", - " 'Ult Claims',\n", + " \"Reported Severity\",\n", + " \"Ult Severity\",\n", + " \"Selected Ult CC\",\n", + " \"Ult Claims\",\n", " ],\n", - " ['CDF'],\n", + " [\"CDF\"],\n", " {},\n", - " \"{:%Y%m}\"\n", + " \"{:%Y%m}\",\n", ")" ] }, @@ -620,23 +670,32 @@ }, "outputs": [], "source": [ - "#Exhibit I Sheet 1\n", - "assert np.all(e1_ccc_selected.cdf_.round(3).values == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 2\n", - "assert np.all(e1_rcc_selected.cdf_.round(3).values == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 3\n", + "# Exhibit I Sheet 1\n", + "assert np.all(\n", + " e1_ccc_selected.cdf_.round(3).values\n", + " == np.array([1.305, 1.010, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 2\n", + "assert np.all(\n", + " e1_rcc_selected.cdf_.round(3).values\n", + " == np.array([0.975, 0.997, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 3\n", "assert np.allclose(\n", - " e1_s3['Selected Ult CC'].values,\n", + " e1_s3[\"Selected Ult CC\"].values,\n", " np.array([3292, 3243, 2699, 2757, 2635, 2699, 2665, 2524, 2764, 3061]),\n", - " atol=1\n", + " atol=1,\n", ")\n", - "#Exhibit I Sheet 6\n", - "assert np.all(e1_rsev_selected.cdf_.round(3).values == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit I Sheet 7\n", + "# Exhibit I Sheet 6\n", + "assert np.all(\n", + " e1_rsev_selected.cdf_.round(3).values\n", + " == np.array([1.036, 0.997, 0.998, 0.999, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit I Sheet 7\n", "assert np.allclose(\n", - " e1_s7['Ult Severity'].values,\n", + " e1_s7[\"Ult Severity\"].values,\n", " np.array([4510, 4507, 4632, 4295, 4465, 4356, 4754, 4535, 4613, 4644]),\n", - " atol=1\n", + " atol=1,\n", ")" ] }, @@ -676,42 +735,58 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e2_tri = cl.load_sample('friedland_xyz_auto_bi')\n", + "# loading data and assumptions\n", + "e2_tri = cl.load_sample(\"friedland_xyz_auto_bi\")\n", "e2_cnt_assumptions = {}\n", - "e2_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e2_cnt_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e2_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e2_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", - "e2_cnt_assumptions['volume_2'] = {'n_periods':2, 'average':'volume'}\n", + "e2_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e2_cnt_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e2_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e2_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", + "e2_cnt_assumptions[\"volume_2\"] = {\"n_periods\": 2, \"average\": \"volume\"}\n", "e2_sev_assumptions = {}\n", - "e2_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e2_sev_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e2_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e2_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e2_sev_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e2_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "\n", - "#developing closed claim counts\n", - "e2_ccc_devs = average_dev(e2_tri['Closed Claim Counts'],e2_cnt_assumptions)\n", - "e2_ccc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_ccc_devs['volume_2'])\n", + "# developing closed claim counts\n", + "e2_ccc_devs = average_dev(e2_tri[\"Closed Claim Counts\"], e2_cnt_assumptions)\n", + "e2_ccc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_ccc_devs[\"volume_2\"]\n", + ")\n", "e2_ccc_selected.ldf_ = e2_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e2_rcc_devs = average_dev(e2_tri['Reported Claim Counts'],e2_cnt_assumptions)\n", - "e2_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rcc_devs['volume_2'])\n", + "# developing reported claim counts\n", + "e2_rcc_devs = average_dev(e2_tri[\"Reported Claim Counts\"], e2_cnt_assumptions)\n", + "e2_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_rcc_devs[\"volume_2\"]\n", + ")\n", "e2_rcc_selected.ldf_ = e2_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e2_ccc_cl = cl.Chainladder().fit(e2_ccc_selected)\n", "e2_rcc_cl = cl.Chainladder().fit(e2_rcc_selected)\n", - "e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_)/2\n", + "e2_cc_ult = (e2_ccc_cl.ultimate_ + e2_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating and developing reported severity\n", - "e2_rsev = e2_tri['Reported Claims'] / e2_tri['Reported Claim Counts'] * 1000\n", - "e2_rsev_devs = average_dev(e2_rsev,e2_sev_assumptions)\n", - "e2_rsev_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e2_rsev_devs['simple_2'])\n", + "# calculating and developing reported severity\n", + "e2_rsev = e2_tri[\"Reported Claims\"] / e2_tri[\"Reported Claim Counts\"] * 1000\n", + "e2_rsev_devs = average_dev(e2_rsev, e2_sev_assumptions)\n", + "e2_rsev_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e2_rsev_devs[\"simple_2\"]\n", + ")\n", "e2_rsev_selected.ldf_ = e2_rsev_selected.ldf_.round(3)\n", "e2_rsev_cl = cl.Chainladder().fit(e2_rsev_selected)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e2_ult = e2_rsev_cl.ultimate_ * e2_cc_ult / 1000" ] }, @@ -800,19 +875,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e2_tri['Reported Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e2_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e2_tri[\"Reported Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e2_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e2_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e2_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e2_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display(1/e2_rcc_selected.cdf_.round(3))" + "print(\"Percent Reported\")\n", + "nb_display(1 / e2_rcc_selected.cdf_.round(3))" ] }, { @@ -850,32 +925,42 @@ }, "outputs": [], "source": [ - "e2_ccc_df = cl.model_diagnostics(e2_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_rcc_df = cl.model_diagnostics(e2_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s3 = e2_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e2_ccc_df = (\n", + " cl.model_diagnostics(e2_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_rcc_df = (\n", + " cl.model_diagnostics(e2_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_s3 = e2_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e2_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e2_rcc_df[['Latest','CDF','Ultimate']]\n", + "e2_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e2_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e2_s3[\"Selected Ult CC\"] = e2_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e2_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", - " ]\n", - ") " + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", + " ],\n", + ")" ] }, { @@ -913,7 +998,7 @@ }, "outputs": [], "source": [ - "nb_display(e2_tri['Reported Claims'])\n", + "nb_display(e2_tri[\"Reported Claims\"])\n", "nb_display(e2_rsev)" ] }, @@ -952,15 +1037,15 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e2_rsev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e2_rsev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factors')\n", + "print(\"PART 3 - Average Age-to-Age Factors\")\n", "nb_display(combine_ldf(e2_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e2_rsev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e2_rsev_selected.cdf_.round(3))" ] }, @@ -999,27 +1084,31 @@ }, "outputs": [], "source": [ - "e2_rsev_df = cl.model_diagnostics(e2_rsev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e2_s6 = e2_rsev_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e2_rsev_df = (\n", + " cl.model_diagnostics(e2_rsev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e2_s6 = e2_rsev_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Reported Severity',\n", - " 'Ultimate':'Ult Severity'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Reported Severity\",\n", + " \"Ultimate\": \"Ult Severity\",\n", " }\n", ")\n", - "e2_s6['Selected Ult CC'] = e2_s3['Selected Ult CC']\n", + "e2_s6[\"Selected Ult CC\"] = e2_s3[\"Selected Ult CC\"]\n", "e2_s6[\"Ult Claims\"] = e2_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e2_s6,\n", " [\n", - " 'Reported Severity',\n", - " 'Ult Severity',\n", - " 'Selected Ult CC',\n", - " 'Ult Claims',\n", + " \"Reported Severity\",\n", + " \"Ult Severity\",\n", + " \"Selected Ult CC\",\n", + " \"Ult Claims\",\n", " ],\n", " [\n", - " 'CDF',\n", - " ]\n", + " \"CDF\",\n", + " ],\n", ")" ] }, @@ -1058,8 +1147,8 @@ }, "outputs": [], "source": [ - "e2_s7 = summary_exh(e2_tri['Reported Claims'],e2_tri['Paid Claims'],e2_ult)\n", - "format_exh(e2_s7,e2_s7.columns)" + "e2_s7 = summary_exh(e2_tri[\"Reported Claims\"], e2_tri[\"Paid Claims\"], e2_ult)\n", + "format_exh(e2_s7, e2_s7.columns)" ] }, { @@ -1080,23 +1169,40 @@ }, "outputs": [], "source": [ - "#Exhibit II Sheet 1\n", - "assert np.all(e2_ccc_selected.cdf_.round(3).values == np.array([6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]))\n", - "#Exhibit II Sheet 2\n", - "assert np.all(e2_rcc_selected.cdf_.round(3).values == np.array([1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit II Sheet 3\n", + "# Exhibit II Sheet 1\n", + "assert np.all(\n", + " e2_ccc_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [6.085, 2.281, 1.612, 1.318, 1.161, 1.085, 1.035, 1.021, 1.015, 1.003, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 2\n", + "assert np.all(\n", + " e2_rcc_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [1.131, 1.035, 1.013, 1.005, 1.002, 1.001, 1.000, 1.000, 1.000, 1.000, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 3\n", "assert np.allclose(\n", - " e2_s3['Selected Ult CC'].values,\n", + " e2_s3[\"Selected Ult CC\"].values,\n", " np.array([637, 1047, 1416, 1466, 1565, 1666, 2309, 2483, 1807, 1556, 1426]),\n", - " atol=1\n", + " atol=1,\n", ")\n", - "#Exhibit II Sheet 5\n", - "assert np.all(e2_rsev_selected.cdf_.round(3).values == np.array([2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]))\n", - "#Exhibit II Sheet 6\n", + "# Exhibit II Sheet 5\n", + "assert np.all(\n", + " e2_rsev_selected.cdf_.round(3).values\n", + " == np.array(\n", + " [2.310, 1.498, 1.212, 1.100, 1.066, 1.016, 1.003, 0.992, 0.990, 0.999, 1.000]\n", + " )\n", + ")\n", + "# Exhibit II Sheet 6\n", "assert np.allclose(\n", - " e2_s6['Ult Severity'].values,\n", - " np.array([24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]),\n", - " rtol=0.001\n", + " e2_s6[\"Ult Severity\"].values,\n", + " np.array(\n", + " [24839, 23956, 26189, 26452, 31090, 27675, 33183, 32519, 35697, 37606, 41544]\n", + " ),\n", + " rtol=0.001,\n", ")" ] }, @@ -1136,71 +1242,93 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e3_tri = cl.load_sample('friedland_wc_self_insurer')\n", + "# loading data and assumptions\n", + "e3_tri = cl.load_sample(\"friedland_wc_self_insurer\")\n", "e3_cnt_assumptions = {}\n", - "e3_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e3_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e3_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e3_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e3_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e3_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e3_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e3_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e3_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e3_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e3_sev_assumptions = {}\n", - "e3_sev_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e3_sev_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e3_sev_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e3_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e3_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e3_sev_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e3_sevavg_assumptions = {}\n", - "e3_sevavg_assumptions['all_years'] = {}\n", - "e3_sevavg_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", - "e3_sevavg_assumptions['latest_3'] = {'n_periods':3}\n", + "e3_sevavg_assumptions[\"all_years\"] = {}\n", + "e3_sevavg_assumptions[\"all_years_excl_hilo\"] = {\"drop_high\": 1, \"drop_low\": 1}\n", + "e3_sevavg_assumptions[\"latest_3\"] = {\"n_periods\": 3}\n", "\n", - "#developing closed claim counts\n", - "#There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", - "e3_ccc_devs = average_dev(e3_tri['Closed Claim Counts'],e3_cnt_assumptions)\n", - "e3_ccc_selected = cl.TailConstant(tail = 1.002, projection_period = 0).fit_transform(e3_ccc_devs['volume_5'])\n", + "# developing closed claim counts\n", + "# There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", + "e3_ccc_devs = average_dev(e3_tri[\"Closed Claim Counts\"], e3_cnt_assumptions)\n", + "e3_ccc_selected = cl.TailConstant(tail=1.002, projection_period=0).fit_transform(\n", + " e3_ccc_devs[\"volume_5\"]\n", + ")\n", "e3_ccc_selected.ldf_ = e3_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e3_rcc_devs = average_dev(e3_tri['Reported Claim Counts'],e3_cnt_assumptions)\n", - "e3_rcc_selected = cl.TailConstant(tail = 1.000, projection_period = 0).fit_transform(e3_rcc_devs['volume_5'])\n", + "# developing reported claim counts\n", + "e3_rcc_devs = average_dev(e3_tri[\"Reported Claim Counts\"], e3_cnt_assumptions)\n", + "e3_rcc_selected = cl.TailConstant(tail=1.000, projection_period=0).fit_transform(\n", + " e3_rcc_devs[\"volume_5\"]\n", + ")\n", "e3_rcc_selected.ldf_ = e3_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e3_ccc_cl = cl.Chainladder().fit(e3_ccc_selected)\n", "e3_rcc_cl = cl.Chainladder().fit(e3_rcc_selected)\n", - "e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_)/2\n", + "e3_cc_ult = (e3_ccc_cl.ultimate_ + e3_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating trended frequency\n", - "e3_freq_trend = cl.Trend(-.01).fit(e3_tri['Reported Claim Counts']).trend_\n", - "e3_payroll_trend = cl.Trend(.025).fit(e3_tri['Payroll']).trend_\n", - "e3_freq_2008 = .0036\n", - "e3_freq_2007 = e3_freq_2008 / e3_freq_trend.values[0,0,-2,0] * e3_payroll_trend.latest_diagonal.values[0,0,-2,0]\n", + "# calculating trended frequency\n", + "e3_freq_trend = cl.Trend(-0.01).fit(e3_tri[\"Reported Claim Counts\"]).trend_\n", + "e3_payroll_trend = cl.Trend(0.025).fit(e3_tri[\"Payroll\"]).trend_\n", + "e3_freq_2008 = 0.0036\n", + "e3_freq_2007 = (\n", + " e3_freq_2008\n", + " / e3_freq_trend.values[0, 0, -2, 0]\n", + " * e3_payroll_trend.latest_diagonal.values[0, 0, -2, 0]\n", + ")\n", "\n", - "#calculating and developing paid severity\n", - "e3_psev = e3_tri[\"Paid Claims\"] / e3_tri['Closed Claim Counts']\n", - "e3_psev_devs = average_dev(e3_psev,e3_sev_assumptions)\n", - "e3_psev_selected = cl.TailConstant(tail = 1.150, projection_period = 0).fit_transform(e3_psev_devs['medial_5x1'])\n", + "# calculating and developing paid severity\n", + "e3_psev = e3_tri[\"Paid Claims\"] / e3_tri[\"Closed Claim Counts\"]\n", + "e3_psev_devs = average_dev(e3_psev, e3_sev_assumptions)\n", + "e3_psev_selected = cl.TailConstant(tail=1.150, projection_period=0).fit_transform(\n", + " e3_psev_devs[\"medial_5x1\"]\n", + ")\n", "e3_psev_selected.ldf_ = e3_psev_selected.ldf_.round(3)\n", "\n", - "#calculating and developing reported severity\n", - "e3_rsev = e3_tri[\"Reported Claims\"] / e3_tri['Reported Claim Counts']\n", - "e3_rsev_devs = average_dev(e3_rsev,e3_sev_assumptions)\n", - "e3_rsev_selected = cl.TailConstant(tail = 1.025, projection_period = 0).fit_transform(e3_rsev_devs['medial_5x1'])\n", + "# calculating and developing reported severity\n", + "e3_rsev = e3_tri[\"Reported Claims\"] / e3_tri[\"Reported Claim Counts\"]\n", + "e3_rsev_devs = average_dev(e3_rsev, e3_sev_assumptions)\n", + "e3_rsev_selected = cl.TailConstant(tail=1.025, projection_period=0).fit_transform(\n", + " e3_rsev_devs[\"medial_5x1\"]\n", + ")\n", "e3_rsev_selected.ldf_ = e3_rsev_selected.ldf_.round(3)\n", "\n", - "#combining developed count and severity\n", + "# combining developed count and severity\n", "e3_psev_cl = cl.Chainladder().fit(e3_psev_selected)\n", "e3_resv_cl = cl.Chainladder().fit(e3_rsev_selected)\n", - "e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_)/2\n", + "e3_sev_ult = (e3_psev_cl.ultimate_ + e3_resv_cl.ultimate_) / 2\n", "\n", - "#calculated trended severity\n", - "e3_sev_trend = cl.Trend(.075).fit(e3_tri['Reported Claims']).trend_\n", + "# calculated trended severity\n", + "e3_sev_trend = cl.Trend(0.075).fit(e3_tri[\"Reported Claims\"]).trend_\n", "e3_sev_ult_trended = e3_sev_ult * e3_sev_trend.latest_diagonal.values\n", - "#prep ult severity for average calculations\n", - "e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin<='2006']\n", - "e3_sevs = average_sev(e3_sev_ult_trended_2006,e3_sevavg_assumptions)\n", - "#calculate selected severities\n", - "e3_sev_2008 = 7100.\n", - "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0,0,-2,0]\n" + "# prep ult severity for average calculations\n", + "e3_sev_ult_trended_2006 = e3_sev_ult_trended[e3_sev_ult_trended.origin <= \"2006\"]\n", + "e3_sevs = average_sev(e3_sev_ult_trended_2006, e3_sevavg_assumptions)\n", + "# calculate selected severities\n", + "e3_sev_2008 = 7100.0\n", + "e3_sev_2007 = e3_sev_2008 / e3_sev_trend.values[0, 0, -2, 0]" ] }, { @@ -1237,19 +1365,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_tri[\"Closed Claim Counts\"])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e3_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e3_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e3_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display((1/e3_ccc_selected.cdf_).round(3))" + "print(\"Percent Closed\")\n", + "nb_display((1 / e3_ccc_selected.cdf_).round(3))" ] }, { @@ -1286,18 +1414,18 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_tri[\"Reported Claim Counts\"])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e3_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e3_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e3_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display((1/e3_rcc_selected.cdf_).round(3))" + "print(\"Percent Reported\")\n", + "nb_display((1 / e3_rcc_selected.cdf_).round(3))" ] }, { @@ -1334,31 +1462,41 @@ }, "outputs": [], "source": [ - "e3_ccc_df = cl.model_diagnostics(e3_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_rcc_df = cl.model_diagnostics(e3_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s3 = e3_ccc_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e3_ccc_df = (\n", + " cl.model_diagnostics(e3_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_rcc_df = (\n", + " cl.model_diagnostics(e3_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_s3 = e3_ccc_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Closed Claim Counts',\n", - " 'CDF':'Closed CDF',\n", - " 'Ultimate':'Ult Count Using CCC'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Closed Claim Counts\",\n", + " \"CDF\": \"Closed CDF\",\n", + " \"Ultimate\": \"Ult Count Using CCC\",\n", " }\n", ")\n", - "e3_s3[['Reported Claim Counts','Reported CDF','Ult Count Using RCC']] = e3_rcc_df[['Latest','CDF','Ultimate']]\n", + "e3_s3[[\"Reported Claim Counts\", \"Reported CDF\", \"Ult Count Using RCC\"]] = e3_rcc_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e3_s3[\"Selected Ult CC\"] = e3_cc_ult.latest_diagonal.to_frame()\n", "format_exh(\n", " e3_s3,\n", " [\n", - " 'Closed Claim Counts',\n", - " 'Reported Claim Counts',\n", - " 'Ult Count Using CCC',\n", - " 'Ult Count Using RCC',\n", - " 'Selected Ult CC',\n", + " \"Closed Claim Counts\",\n", + " \"Reported Claim Counts\",\n", + " \"Ult Count Using CCC\",\n", + " \"Ult Count Using RCC\",\n", + " \"Selected Ult CC\",\n", " ],\n", " [\n", - " 'Closed CDF',\n", - " 'Reported CDF',\n", - " ]\n", + " \"Closed CDF\",\n", + " \"Reported CDF\",\n", + " ],\n", ")" ] }, @@ -1396,27 +1534,29 @@ }, "outputs": [], "source": [ - "e3_s4 = e3_s3[['Selected Ult CC']].copy()\n", - "e3_s4['CC Trend'] = e3_freq_trend.latest_diagonal.to_frame()\n", - "e3_s4['Trended Ult CC'] = e3_s4['Selected Ult CC'] * e3_s4['CC Trend']\n", - "e3_s4['Payroll'] = e3_tri['Payroll'].latest_diagonal.to_frame()\n", - "e3_s4['Payroll Trend'] = e3_payroll_trend.latest_diagonal.to_frame()\n", - "e3_s4['Trended Payroll'] = e3_s4['Payroll'] * e3_s4['Payroll Trend']\n", - "e3_s4['Trended Ultimate Freq'] = e3_s4['Trended Ult CC'] / e3_s4['Trended Payroll']\n", - "nb_display(format_exh(\n", - " e3_s4,\n", - " [\n", - " 'Selected Ult CC',\n", - " 'Trended Ult CC',\n", - " 'Payroll',\n", - " 'Trended Payroll',\n", - " ],\n", - " [\n", - " 'CC Trend',\n", - " 'Payroll Trend',\n", - " ],\n", - " {'Trended Ultimate Freq' : \"{:.2%}\"}\n", - "))\n", + "e3_s4 = e3_s3[[\"Selected Ult CC\"]].copy()\n", + "e3_s4[\"CC Trend\"] = e3_freq_trend.latest_diagonal.to_frame()\n", + "e3_s4[\"Trended Ult CC\"] = e3_s4[\"Selected Ult CC\"] * e3_s4[\"CC Trend\"]\n", + "e3_s4[\"Payroll\"] = e3_tri[\"Payroll\"].latest_diagonal.to_frame()\n", + "e3_s4[\"Payroll Trend\"] = e3_payroll_trend.latest_diagonal.to_frame()\n", + "e3_s4[\"Trended Payroll\"] = e3_s4[\"Payroll\"] * e3_s4[\"Payroll Trend\"]\n", + "e3_s4[\"Trended Ultimate Freq\"] = e3_s4[\"Trended Ult CC\"] / e3_s4[\"Trended Payroll\"]\n", + "nb_display(\n", + " format_exh(\n", + " e3_s4,\n", + " [\n", + " \"Selected Ult CC\",\n", + " \"Trended Ult CC\",\n", + " \"Payroll\",\n", + " \"Trended Payroll\",\n", + " ],\n", + " [\n", + " \"CC Trend\",\n", + " \"Payroll Trend\",\n", + " ],\n", + " {\"Trended Ultimate Freq\": \"{:.2%}\"},\n", + " )\n", + ")\n", "print(f\"Selected Frequency at 2008 level {e3_freq_2008:.2%}\")\n", "print(f\"Selected Frequency at 2007 level {float(e3_freq_2007):.2%}\")" ] @@ -1455,13 +1595,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e3_tri[\"Paid Claims\"])\n", - "print('Paid Severities')\n", + "print(\"Paid Severities\")\n", "nb_display(e3_psev)\n", - "print('Reported Claims')\n", + "print(\"Reported Claims\")\n", "nb_display(e3_tri[\"Reported Claims\"])\n", - "print('Reported Severities')\n", + "print(\"Reported Severities\")\n", "nb_display(e3_rsev)" ] }, @@ -1499,16 +1639,16 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_psev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e3_psev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_psev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e3_psev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_psev_selected.cdf_.round(3))" ] }, @@ -1593,30 +1733,40 @@ }, "outputs": [], "source": [ - "e3_psev_df = cl.model_diagnostics(e3_psev_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_resv_df = cl.model_diagnostics(e3_resv_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e3_s8 = e3_psev_df[['development','Latest','CDF','Ultimate']].rename(\n", + "e3_psev_df = (\n", + " cl.model_diagnostics(e3_psev_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_resv_df = (\n", + " cl.model_diagnostics(e3_resv_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e3_s8 = e3_psev_df[[\"development\", \"Latest\", \"CDF\", \"Ultimate\"]].rename(\n", " columns={\n", - " 'development':'Age',\n", - " 'Latest':'Paid Sev',\n", - " 'CDF':'Paid CDF',\n", - " 'Ultimate':'Ult Paid Sev'\n", + " \"development\": \"Age\",\n", + " \"Latest\": \"Paid Sev\",\n", + " \"CDF\": \"Paid CDF\",\n", + " \"Ultimate\": \"Ult Paid Sev\",\n", " }\n", ")\n", - "e3_s8[['Reported Sev','Reported CDF','Ult Reported Sev']] = e3_resv_df[['Latest','CDF','Ultimate']]\n", + "e3_s8[[\"Reported Sev\", \"Reported CDF\", \"Ult Reported Sev\"]] = e3_resv_df[\n", + " [\"Latest\", \"CDF\", \"Ultimate\"]\n", + "]\n", "e3_s8[\"Selected Ult Sev\"] = e3_sev_ult.latest_diagonal.to_frame()\n", "format_exh(\n", - " e3_s8.iloc[:-2,:],\n", + " e3_s8.iloc[:-2, :],\n", " [\n", - " 'Paid Sev',\n", - " 'Reported Sev',\n", - " 'Ult Paid Sev',\n", - " 'Ult Reported Sev',\n", - " 'Selected Ult Sev',\n", + " \"Paid Sev\",\n", + " \"Reported Sev\",\n", + " \"Ult Paid Sev\",\n", + " \"Ult Reported Sev\",\n", + " \"Selected Ult Sev\",\n", " ],\n", " [\n", - " 'Paid CDF',\n", - " 'Reported CDF',\n", + " \"Paid CDF\",\n", + " \"Reported CDF\",\n", " ],\n", ")" ] @@ -1712,31 +1862,31 @@ }, "outputs": [], "source": [ - "e3_s10 = e3_s4[['Payroll']].copy()[-2:]\n", - "e3_s10['Selected Frequency'] = [e3_freq_2007,e3_freq_2008]\n", - "e3_s10['Ult CC'] = e3_s10['Payroll'] * e3_s10['Selected Frequency']\n", - "e3_s10['Selected Severity'] = [e3_sev_2007,e3_sev_2008]\n", - "e3_s10['Ult Claims'] = e3_s10['Ult CC'] * e3_s10['Selected Severity']\n", - "e3_s10['Reported Claims'] = e3_tri['Reported Claims'].latest_diagonal.to_frame()\n", - "e3_s10['Paid Claims'] = e3_tri['Paid Claims'].latest_diagonal.to_frame()\n", - "e3_s10['Case Outstanding'] = e3_s10['Reported Claims'] - e3_s10['Paid Claims']\n", - "e3_s10['IBNR'] = e3_s10['Ult Claims'] - e3_s10['Reported Claims']\n", - "e3_s10['Unpaid'] = e3_s10['IBNR'] + e3_s10['Case Outstanding']\n", + "e3_s10 = e3_s4[[\"Payroll\"]].copy()[-2:]\n", + "e3_s10[\"Selected Frequency\"] = [e3_freq_2007, e3_freq_2008]\n", + "e3_s10[\"Ult CC\"] = e3_s10[\"Payroll\"] * e3_s10[\"Selected Frequency\"]\n", + "e3_s10[\"Selected Severity\"] = [e3_sev_2007, e3_sev_2008]\n", + "e3_s10[\"Ult Claims\"] = e3_s10[\"Ult CC\"] * e3_s10[\"Selected Severity\"]\n", + "e3_s10[\"Reported Claims\"] = e3_tri[\"Reported Claims\"].latest_diagonal.to_frame()\n", + "e3_s10[\"Paid Claims\"] = e3_tri[\"Paid Claims\"].latest_diagonal.to_frame()\n", + "e3_s10[\"Case Outstanding\"] = e3_s10[\"Reported Claims\"] - e3_s10[\"Paid Claims\"]\n", + "e3_s10[\"IBNR\"] = e3_s10[\"Ult Claims\"] - e3_s10[\"Reported Claims\"]\n", + "e3_s10[\"Unpaid\"] = e3_s10[\"IBNR\"] + e3_s10[\"Case Outstanding\"]\n", "format_exh(\n", " e3_s10,\n", " [\n", - " 'Payroll',\n", - " 'Ult CC',\n", - " 'Selected Severity',\n", - " 'Ult Claims',\n", - " 'Reported Claims',\n", - " 'Paid Claims',\n", - " 'Case Outstanding',\n", - " 'IBNR',\n", - " 'Unpaid'\n", + " \"Payroll\",\n", + " \"Ult CC\",\n", + " \"Selected Severity\",\n", + " \"Ult Claims\",\n", + " \"Reported Claims\",\n", + " \"Paid Claims\",\n", + " \"Case Outstanding\",\n", + " \"IBNR\",\n", + " \"Unpaid\",\n", " ],\n", " [],\n", - " {'Selected Frequency':'{:.2%}'}\n", + " {\"Selected Frequency\": \"{:.2%}\"},\n", ")" ] }, @@ -1758,41 +1908,50 @@ }, "outputs": [], "source": [ - "#Exhibit III Sheet 1\n", - "assert np.all(e3_ccc_selected.cdf_.round(3).values[...,:7] == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010]))\n", - "#Exhibit III Sheet 2\n", - "assert np.all(e3_rcc_selected.cdf_.round(3).values == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000]))\n", - "#Exhibit III Sheet 3\n", + "# Exhibit III Sheet 1\n", + "assert np.all(\n", + " e3_ccc_selected.cdf_.round(3).values[..., :7]\n", + " == np.array([1.698, 1.133, 1.071, 1.034, 1.018, 1.014, 1.010])\n", + ")\n", + "# Exhibit III Sheet 2\n", + "assert np.all(\n", + " e3_rcc_selected.cdf_.round(3).values\n", + " == np.array([1.094, 1.022, 1.007, 1.002, 1.000, 1.000, 1.000, 1.000])\n", + ")\n", + "# Exhibit III Sheet 3\n", "assert np.allclose(\n", - " e3_s3['Selected Ult CC'].values[...,1:],\n", + " e3_s3[\"Selected Ult CC\"].values[..., 1:],\n", " np.array([1700, 1774, 1749, 1651, 2982, 2909, 2658]),\n", - " rtol=0.001\n", + " rtol=0.001,\n", ")\n", - "#Exhibit III Sheet 4\n", + "# Exhibit III Sheet 4\n", "assert np.allclose(\n", - " e3_s4['Trended Ultimate Freq'].values[...,1:],\n", + " e3_s4[\"Trended Ultimate Freq\"].values[..., 1:],\n", " np.array([0.0053, 0.0053, 0.0054, 0.0043, 0.0035, 0.0036, 0.0036]),\n", - " atol = 0.0001\n", - " )\n", - "#Exhibit III Sheet 6\n", + " atol=0.0001,\n", + ")\n", + "# Exhibit III Sheet 6\n", "assert np.allclose(\n", " e3_psev_selected.ldf_.round(3).values,\n", " np.array([1.447, 1.249, 1.108, 1.066, 1.038, 1.037, 1.021, 1.150]),\n", - " atol=.001\n", + " atol=0.001,\n", + ")\n", + "# Exhibit III Sheet 7\n", + "assert np.all(\n", + " e3_rsev_selected.cdf_.round(3).values\n", + " == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025])\n", ")\n", - "#Exhibit III Sheet 7\n", - "assert np.all(e3_rsev_selected.cdf_.round(3).values == np.array([1.679, 1.314, 1.189, 1.130, 1.092, 1.065, 1.043, 1.025]))\n", - "#Exhibit III Sheet 8\n", + "# Exhibit III Sheet 8\n", "assert np.allclose(\n", - " e3_s8['Selected Ult Sev'].values[:-2],\n", + " e3_s8[\"Selected Ult Sev\"].values[:-2],\n", " np.array([4371, 4587, 4963, 5242, 5635, 6169]),\n", - " rtol=.001\n", + " rtol=0.001,\n", ")\n", - "#Exhibit III Sheet 9\n", + "# Exhibit III Sheet 9\n", "assert np.allclose(\n", - " e3_s9['Trended Ult Sev'].values[:-2],\n", + " e3_s9[\"Trended Ult Sev\"].values[:-2],\n", " np.array([7251, 7079, 7125, 7000, 7001, 7129]),\n", - " rtol=0.001\n", + " rtol=0.001,\n", ")" ] }, @@ -1832,67 +1991,91 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "xyz_tort_data = pd.DataFrame({\n", - " \"EffDate\": [\"2006-01-01\", \"2007-01-01\"],\n", - " \"RateChange\": [(0.67/0.75) - 1, 0.75-1]\n", - "})\n", + "# loading data and assumptions\n", + "xyz_tort_data = pd.DataFrame(\n", + " {\n", + " \"EffDate\": [\"2006-01-01\", \"2007-01-01\"],\n", + " \"RateChange\": [(0.67 / 0.75) - 1, 0.75 - 1],\n", + " }\n", + ")\n", "xyz_tort_adjustment = cl.ParallelogramOLF(\n", " rate_history=xyz_tort_data,\n", " change_col=\"RateChange\",\n", " date_col=\"EffDate\",\n", - " vertical_line=True\n", + " vertical_line=True,\n", ")\n", - "#Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given\n", + "# Both Chapter 6 and Chapter 8 contain the code to derive these on-level factors from underlying rate changes. We will simply use the factors as given\n", "olf = cl.Triangle(\n", - " data = {\n", - " 'origin':[1998,2002,2003,2004,2005,2006,2007,2008],\n", - " 'valuation':[2008,2008,2008,2008,2008,2008,2008,2008],\n", - " 'On-Level Adjustment':[0,.914,.87,.81,.704,.64,.8,1],\n", + " data={\n", + " \"origin\": [1998, 2002, 2003, 2004, 2005, 2006, 2007, 2008],\n", + " \"valuation\": [2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008],\n", + " \"On-Level Adjustment\": [0, 0.914, 0.87, 0.81, 0.704, 0.64, 0.8, 1],\n", " },\n", - " origin='origin',\n", - " development='valuation',\n", - " columns='On-Level Adjustment',\n", - " cumulative = True\n", + " origin=\"origin\",\n", + " development=\"valuation\",\n", + " columns=\"On-Level Adjustment\",\n", + " cumulative=True,\n", ")\n", "e4_freq_assumptions = {}\n", - "e4_freq_assumptions['all_years'] = {}\n", - "e4_freq_assumptions['all_years_excl_hilo'] = {'drop_high':1, 'drop_low':1}\n", - "e4_freq_assumptions['latest_2'] = {'n_periods':2}\n", + "e4_freq_assumptions[\"all_years\"] = {}\n", + "e4_freq_assumptions[\"all_years_excl_hilo\"] = {\"drop_high\": 1, \"drop_low\": 1}\n", + "e4_freq_assumptions[\"latest_2\"] = {\"n_periods\": 2}\n", "e4_sev_assumptions = {}\n", - "e4_sev_assumptions['latest_5_years'] = {'n_periods':5}\n", - "e4_sev_assumptions['latest_5_years_excl_hilo'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", - "e4_sev_assumptions['latest_3'] = {'n_periods':3}\n", + "e4_sev_assumptions[\"latest_5_years\"] = {\"n_periods\": 5}\n", + "e4_sev_assumptions[\"latest_5_years_excl_hilo\"] = {\n", + " \"n_periods\": 5,\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e4_sev_assumptions[\"latest_3\"] = {\"n_periods\": 3}\n", "\n", "# In Exhibit II, the selected ultimate was actually the average of reported and closed ultimates.\n", "e4_s1_tri = e2_rcc_cl.ultimate_.copy()\n", - "e4_s1_tri = e4_s1_tri.rename(axis='columns',value='Ult CC')\n", - "e4_s1_tri['CC Trend'] = cl.Trend(-.015,dates=('2008-12-31','2002-01-01')).fit(e4_s1_tri).trend_\n", - "e4_s1_tri['Trended Ult CC'] = e4_s1_tri['CC Trend'] * e4_s1_tri['Ult CC']\n", - "e4_s1_tri['Earned Premium'] = e2_tri['Earned Premium'].latest_diagonal\n", - "e4_s1_tri['On-Level Adjustment'] = olf['On-Level Adjustment']\n", - "e4_s1_tri['On-Level Premium'] = e4_s1_tri['On-Level Adjustment'] * e4_s1_tri['Earned Premium']\n", - "e4_s1_tri['Trended Ult Freq'] = e4_s1_tri['Trended Ult CC'] / e4_s1_tri['On-Level Premium']\n", - "e4_s1_tri_2006 = e4_s1_tri.iloc[:,:,4:9,:]\n", - "#calculate average frequencies\n", - "e4_freq = average_sev(e4_s1_tri_2006['Trended Ult Freq'],e4_freq_assumptions)\n", - "#calculate selected frequencies\n", - "e4_freq_2008 = e4_freq['latest_2'].round(4)\n", - "e4_freq_2007 = e4_freq_2008 / e4_s1_tri['CC Trend'].values[0,0,-2,0] * e4_s1_tri['On-Level Adjustment'].values[0,0,-2,0]\n", + "e4_s1_tri = e4_s1_tri.rename(axis=\"columns\", value=\"Ult CC\")\n", + "e4_s1_tri[\"CC Trend\"] = (\n", + " cl.Trend(-0.015, dates=(\"2008-12-31\", \"2002-01-01\")).fit(e4_s1_tri).trend_\n", + ")\n", + "e4_s1_tri[\"Trended Ult CC\"] = e4_s1_tri[\"CC Trend\"] * e4_s1_tri[\"Ult CC\"]\n", + "e4_s1_tri[\"Earned Premium\"] = e2_tri[\"Earned Premium\"].latest_diagonal\n", + "e4_s1_tri[\"On-Level Adjustment\"] = olf[\"On-Level Adjustment\"]\n", + "e4_s1_tri[\"On-Level Premium\"] = (\n", + " e4_s1_tri[\"On-Level Adjustment\"] * e4_s1_tri[\"Earned Premium\"]\n", + ")\n", + "e4_s1_tri[\"Trended Ult Freq\"] = (\n", + " e4_s1_tri[\"Trended Ult CC\"] / e4_s1_tri[\"On-Level Premium\"]\n", + ")\n", + "e4_s1_tri_2006 = e4_s1_tri.iloc[:, :, 4:9, :]\n", + "# calculate average frequencies\n", + "e4_freq = average_sev(e4_s1_tri_2006[\"Trended Ult Freq\"], e4_freq_assumptions)\n", + "# calculate selected frequencies\n", + "e4_freq_2008 = e4_freq[\"latest_2\"].round(4)\n", + "e4_freq_2007 = (\n", + " e4_freq_2008\n", + " / e4_s1_tri[\"CC Trend\"].values[0, 0, -2, 0]\n", + " * e4_s1_tri[\"On-Level Adjustment\"].values[0, 0, -2, 0]\n", + ")\n", "e4_freq_2007 = e4_freq_2007.round(4)\n", "\n", "e4_s2_tri = e2_rsev_cl.ultimate_.copy()\n", - "e4_s2_tri = e4_s2_tri.rename(axis='columns',value='Ult Sev')\n", - "e4_s2_tri['Sev Trend'] = cl.Trend(.05,dates=('2008-12-31','1998-01-01')).fit(e4_s2_tri).trend_.round(3)\n", - "#from Chapter 8 Exhibit III sheet 1\n", - "e4_s2_tri['Tort Reform Factors'] = xyz_tort_adjustment.fit(e4_s2_tri['Ult Sev']).olf_\n", - "e4_s2_tri['Trended Ult Sev'] = e4_s2_tri['Ult Sev'] * e4_s2_tri['Sev Trend'] * e4_s2_tri['Tort Reform Factors']\n", - "e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= '2006']\n", - "#calculate average severities\n", - "e4_sev = average_sev(e4_s2_tri_2006['Trended Ult Sev'],e4_sev_assumptions)\n", - "#calculate selected severities\n", - "e4_sev_2008 = e4_sev['latest_5_years_excl_hilo'].round(0)\n", - "e4_sev_2007 = e4_sev_2008 / e4_s2_tri['Sev Trend'].values[0,0,-2,0] / e4_s2_tri['Tort Reform Factors'].values[0,0,-2,0]\n", + "e4_s2_tri = e4_s2_tri.rename(axis=\"columns\", value=\"Ult Sev\")\n", + "e4_s2_tri[\"Sev Trend\"] = (\n", + " cl.Trend(0.05, dates=(\"2008-12-31\", \"1998-01-01\")).fit(e4_s2_tri).trend_.round(3)\n", + ")\n", + "# from Chapter 8 Exhibit III sheet 1\n", + "e4_s2_tri[\"Tort Reform Factors\"] = xyz_tort_adjustment.fit(e4_s2_tri[\"Ult Sev\"]).olf_\n", + "e4_s2_tri[\"Trended Ult Sev\"] = (\n", + " e4_s2_tri[\"Ult Sev\"] * e4_s2_tri[\"Sev Trend\"] * e4_s2_tri[\"Tort Reform Factors\"]\n", + ")\n", + "e4_s2_tri_2006 = e4_s2_tri[e4_s2_tri.origin <= \"2006\"]\n", + "# calculate average severities\n", + "e4_sev = average_sev(e4_s2_tri_2006[\"Trended Ult Sev\"], e4_sev_assumptions)\n", + "# calculate selected severities\n", + "e4_sev_2008 = e4_sev[\"latest_5_years_excl_hilo\"].round(0)\n", + "e4_sev_2007 = (\n", + " e4_sev_2008\n", + " / e4_s2_tri[\"Sev Trend\"].values[0, 0, -2, 0]\n", + " / e4_s2_tri[\"Tort Reform Factors\"].values[0, 0, -2, 0]\n", + ")\n", "e4_sev_2007 = e4_sev_2007.round(0)" ] }, @@ -1931,13 +2114,17 @@ "outputs": [], "source": [ "e4_s1 = e4_s1_tri.to_frame().T\n", - "e4_s1[['CC Trend','On-Level Adjustment']] = e4_s1[['CC Trend','On-Level Adjustment']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e4_s1[['Trended Ult CC','On-Level Premium']] = (e4_s1[['Trended Ult CC','On-Level Premium']] + 1e-9).round(0)\n", - "e4_s1['Trended Ult Freq'] = e4_s1[\"Trended Ult Freq\"].map(\"{:.2%}\".format)\n", + "e4_s1[[\"CC Trend\", \"On-Level Adjustment\"]] = e4_s1[\n", + " [\"CC Trend\", \"On-Level Adjustment\"]\n", + "].round(3)\n", + "# using floating point offset to achieve standard rounding\n", + "e4_s1[[\"Trended Ult CC\", \"On-Level Premium\"]] = (\n", + " e4_s1[[\"Trended Ult CC\", \"On-Level Premium\"]] + 1e-9\n", + ").round(0)\n", + "e4_s1[\"Trended Ult Freq\"] = e4_s1[\"Trended Ult Freq\"].map(\"{:.2%}\".format)\n", "nb_display(e4_s1.iloc[4:9])\n", - "print('Average Trended Frequency at 2008 Cost Level')\n", - "for k,v in e4_freq.items():\n", + "print(\"Average Trended Frequency at 2008 Cost Level\")\n", + "for k, v in e4_freq.items():\n", " print(f\"\\t{k:<20} \\t{v:.2%}\")\n", "print(f\"Selected 2008 Frequency \\t{e4_freq_2008:.2%}\")\n", "print(f\"Estimated 2007 Frequency \\t{e4_freq_2007:.2%}\")" @@ -1978,12 +2165,16 @@ "outputs": [], "source": [ "e4_s2 = e4_s2_tri.to_frame().T\n", - "e4_s2[['Sev Trend','Tort Reform Factors']] = e4_s2[['Sev Trend','Tort Reform Factors']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e4_s2[['Trended Ult Sev','Ult Sev']] = (e4_s2[['Trended Ult Sev','Ult Sev']] + 1e-9).round(0)\n", + "e4_s2[[\"Sev Trend\", \"Tort Reform Factors\"]] = e4_s2[\n", + " [\"Sev Trend\", \"Tort Reform Factors\"]\n", + "].round(3)\n", + "# using floating point offset to achieve standard rounding\n", + "e4_s2[[\"Trended Ult Sev\", \"Ult Sev\"]] = (\n", + " e4_s2[[\"Trended Ult Sev\", \"Ult Sev\"]] + 1e-9\n", + ").round(0)\n", "nb_display(e4_s2.iloc[:9])\n", - "print('Average Trended Severity at 2008 Cost Level')\n", - "for k,v in e4_sev.items():\n", + "print(\"Average Trended Severity at 2008 Cost Level\")\n", + "for k, v in e4_sev.items():\n", " print(f\"\\t{k:<25} \\t{v.round(0)}\")\n", "print(f\"Selected 2008 Severity \\t\\t\\t{e4_sev_2008}\")\n", "print(f\"Estimated 2007 Severity \\t\\t{e4_sev_2007}\")" @@ -2023,16 +2214,21 @@ }, "outputs": [], "source": [ - "e4_s3 = e4_s1_tri['Earned Premium'].to_frame(keepdims=True).set_index('origin').iloc[-2:][['Earned Premium']]\n", - "e4_s3['Selected Frequency'] = [e4_freq_2007,e4_freq_2008]\n", - "e4_s3['Ult CC'] = e4_s3['Earned Premium'] * e4_s3['Selected Frequency']\n", - "e4_s3['Selected Severity'] = [e4_sev_2007,e4_sev_2008]\n", - "e4_s3['Ult Claims'] = e4_s3['Ult CC'] * e4_s3['Selected Severity']\n", - "e4_s3['Reported Claims'] = (e2_tri['Reported Claims']*1000).latest_diagonal.to_frame()\n", - "e4_s3['Paid Claims'] = (e2_tri['Paid Claims']*1000).latest_diagonal.to_frame()\n", - "e4_s3['Case Outstanding'] = e4_s3['Reported Claims'] - e4_s3['Paid Claims']\n", - "e4_s3['IBNR'] = e4_s3['Ult Claims'] - e4_s3['Reported Claims']\n", - "e4_s3['Unpaid'] = e4_s3['IBNR'] + e4_s3['Case Outstanding']\n", + "e4_s3 = (\n", + " e4_s1_tri[\"Earned Premium\"]\n", + " .to_frame(keepdims=True)\n", + " .set_index(\"origin\")\n", + " .iloc[-2:][[\"Earned Premium\"]]\n", + ")\n", + "e4_s3[\"Selected Frequency\"] = [e4_freq_2007, e4_freq_2008]\n", + "e4_s3[\"Ult CC\"] = e4_s3[\"Earned Premium\"] * e4_s3[\"Selected Frequency\"]\n", + "e4_s3[\"Selected Severity\"] = [e4_sev_2007, e4_sev_2008]\n", + "e4_s3[\"Ult Claims\"] = e4_s3[\"Ult CC\"] * e4_s3[\"Selected Severity\"]\n", + "e4_s3[\"Reported Claims\"] = (e2_tri[\"Reported Claims\"] * 1000).latest_diagonal.to_frame()\n", + "e4_s3[\"Paid Claims\"] = (e2_tri[\"Paid Claims\"] * 1000).latest_diagonal.to_frame()\n", + "e4_s3[\"Case Outstanding\"] = e4_s3[\"Reported Claims\"] - e4_s3[\"Paid Claims\"]\n", + "e4_s3[\"IBNR\"] = e4_s3[\"Ult Claims\"] - e4_s3[\"Reported Claims\"]\n", + "e4_s3[\"Unpaid\"] = e4_s3[\"IBNR\"] + e4_s3[\"Case Outstanding\"]\n", "e4_s3.T" ] }, @@ -2054,19 +2250,18 @@ }, "outputs": [], "source": [ - "#Exhibit IV Sheet 1\n", - "assert np.all(e4_s1.iloc[4:9]['Trended Ult Freq'].values == np.array(['2.54%', '2.51%', '2.65%', '2.36%', '2.37%']))\n", - "#Exhibit IV Sheet 2\n", + "# Exhibit IV Sheet 1\n", + "assert np.all(\n", + " e4_s1.iloc[4:9][\"Trended Ult Freq\"].values\n", + " == np.array([\"2.54%\", \"2.51%\", \"2.65%\", \"2.36%\", \"2.37%\"])\n", + ")\n", + "# Exhibit IV Sheet 2\n", "assert np.allclose(\n", - " list(e4_sev.values()),\n", - " np.array([26669., 26720., 27254.]),\n", - " rtol=0.001\n", + " list(e4_sev.values()), np.array([26669.0, 26720.0, 27254.0]), rtol=0.001\n", ")\n", - "#Exhibit IV Sheet 3\n", + "# Exhibit IV Sheet 3\n", "assert np.allclose(\n", - " e4_s3.iloc[:,4].values,\n", - " np.array([30512152., 30140260.]),\n", - " rtol=0.001\n", + " e4_s3.iloc[:, 4].values, np.array([30512152.0, 30140260.0]), rtol=0.001\n", ")" ] }, @@ -2106,85 +2301,111 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e5_tri = cl.load_sample('friedland_gl_insurer')\n", + "# loading data and assumptions\n", + "e5_tri = cl.load_sample(\"friedland_gl_insurer\")\n", "e5_cnt_assumptions = {}\n", - "e5_cnt_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e5_cnt_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e5_cnt_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", - "e5_cnt_assumptions['volume_5'] = {'n_periods':5, 'average':'volume'}\n", - "e5_cnt_assumptions['volume_3'] = {'n_periods':3, 'average':'volume'}\n", + "e5_cnt_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e5_cnt_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e5_cnt_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", + "e5_cnt_assumptions[\"volume_5\"] = {\"n_periods\": 5, \"average\": \"volume\"}\n", + "e5_cnt_assumptions[\"volume_3\"] = {\"n_periods\": 3, \"average\": \"volume\"}\n", "e5_disp_assumptions = {}\n", - "e5_disp_assumptions['simple_5'] = {'n_periods':5, 'average':'simple'}\n", - "e5_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e5_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e5_disp_assumptions[\"simple_5\"] = {\"n_periods\": 5, \"average\": \"simple\"}\n", + "e5_disp_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e5_disp_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e5_trend_assumptions = {}\n", - "e5_trend_assumptions['all_years'] = {}\n", - "e5_trend_assumptions['latest_6'] = {'n_periods':6}\n", - "e5_trend_assumptions['latest_4'] = {'n_periods':4}\n", + "e5_trend_assumptions[\"all_years\"] = {}\n", + "e5_trend_assumptions[\"latest_6\"] = {\"n_periods\": 6}\n", + "e5_trend_assumptions[\"latest_4\"] = {\"n_periods\": 4}\n", "e5_sev_assumptions = {}\n", - "e5_sev_assumptions['simple_5'] = {'n_periods':5}\n", - "e5_sev_assumptions['simple_3'] = {'n_periods':3}\n", - "e5_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "e5_sev_assumptions[\"simple_5\"] = {\"n_periods\": 5}\n", + "e5_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3}\n", + "e5_sev_assumptions[\"medial_5x1\"] = {\"n_periods\": 5, \"drop_high\": 1, \"drop_low\": 1}\n", "\n", - "#developing closed claim counts\n", - "e5_ccc_devs = average_dev(e5_tri['Closed Claim Counts'],e5_cnt_assumptions)\n", - "e5_ccc_selected = cl.TailConstant(tail = 1.100, projection_period = 0).fit_transform(e5_ccc_devs['volume_3'])\n", + "# developing closed claim counts\n", + "e5_ccc_devs = average_dev(e5_tri[\"Closed Claim Counts\"], e5_cnt_assumptions)\n", + "e5_ccc_selected = cl.TailConstant(tail=1.100, projection_period=0).fit_transform(\n", + " e5_ccc_devs[\"volume_3\"]\n", + ")\n", "e5_ccc_selected.ldf_ = e5_ccc_selected.ldf_.round(3)\n", "\n", - "#developing reported claim counts\n", - "e5_rcc_devs = average_dev(e5_tri['Reported Claim Counts'],e5_cnt_assumptions)\n", - "e5_rcc_selected = cl.TailConstant(tail = 1.0, projection_period = 0).fit_transform(e5_rcc_devs['volume_3'])\n", + "# developing reported claim counts\n", + "e5_rcc_devs = average_dev(e5_tri[\"Reported Claim Counts\"], e5_cnt_assumptions)\n", + "e5_rcc_selected = cl.TailConstant(tail=1.0, projection_period=0).fit_transform(\n", + " e5_rcc_devs[\"volume_3\"]\n", + ")\n", "e5_rcc_selected.ldf_ = e5_rcc_selected.ldf_.round(3)\n", "\n", - "#combining closed and reported claim counts\n", + "# combining closed and reported claim counts\n", "e5_ccc_cl = cl.Chainladder().fit(e5_ccc_selected)\n", "e5_rcc_cl = cl.Chainladder().fit(e5_rcc_selected)\n", - "e5_cc_ult = ((e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_)/2)\n", + "e5_cc_ult = (e5_ccc_cl.ultimate_ + e5_rcc_cl.ultimate_) / 2\n", "\n", - "#calculating disposal rate and complete the closed count triangle\n", - "e5_drs = average_dr(e5_tri['Closed Claim Counts'],e5_cc_ult,e5_disp_assumptions)\n", - "e5_dr_selected = e5_drs['medial_5x1']\n", + "# calculating disposal rate and complete the closed count triangle\n", + "e5_drs = average_dr(e5_tri[\"Closed Claim Counts\"], e5_cc_ult, e5_disp_assumptions)\n", + "e5_dr_selected = e5_drs[\"medial_5x1\"]\n", "e5_dr_selected.disposal_rate_ = e5_dr_selected.disposal_rate_.round(3)\n", - "e5_dr_tri = e5_dr_selected.transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult)\n", + "e5_dr_tri = e5_dr_selected.transform(\n", + " e5_tri[\"Closed Claim Counts\"], sample_weight=e5_cc_ult\n", + ")\n", "\n", - "#calculate and select incremental paid severity\n", - "e5_ipsev = e5_tri[\"Paid Claims\"].cum_to_incr() / e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", - "e5_regs = regs(e5_ipsev,e5_trend_assumptions)\n", - "e5_trend,e5_rsq = reg_outputs(e5_regs,e5_ipsev.development)\n", - "e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin>'2001'],{'all_years_ex_2001':{}})\n", - "e5_trend_ex2001,e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001,e5_ipsev.development)\n", - "e5_sevs = average_sev(e5_ipsev.trend(0.05),e5_sev_assumptions)\n", - "e5_sevs_sel = e5_sevs['simple_3'].copy()\n", - "e5_sevs_sel.origin = ['2008']\n", + "# calculate and select incremental paid severity\n", + "e5_ipsev = (\n", + " e5_tri[\"Paid Claims\"].cum_to_incr() / e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ")\n", + "e5_regs = regs(e5_ipsev, e5_trend_assumptions)\n", + "e5_trend, e5_rsq = reg_outputs(e5_regs, e5_ipsev.development)\n", + "e5_regs_ex2001 = regs(e5_ipsev[e5_ipsev.origin > \"2001\"], {\"all_years_ex_2001\": {}})\n", + "e5_trend_ex2001, e5_rsq_ex2001 = reg_outputs(e5_regs_ex2001, e5_ipsev.development)\n", + "e5_sevs = average_sev(e5_ipsev.trend(0.05), e5_sev_assumptions)\n", + "e5_sevs_sel = e5_sevs[\"simple_3\"].copy()\n", + "e5_sevs_sel.origin = [\"2008\"]\n", "\n", - "#calculate and select tail paid severity\n", + "# calculate and select tail paid severity\n", "e5_incr_ccc = e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", "e5_incr_paid = e5_tri[\"Paid Claims\"].cum_to_incr()\n", "e5_incr_paid_trended = e5_incr_paid.trend(0.05)\n", "\n", - "#complete the incremental paid severity triangle\n", + "# complete the incremental paid severity triangle\n", "e5_ipsev_full = e5_ipsev.copy()\n", - "#extending the severity triangle to 108 months\n", - "e5_ipsev_full = cl.concat((e5_ipsev_full,e5_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", - "#setting the before 60 months to selected incremental\n", - "e5_ipsev_full.iloc[:,:,:,:5] = e5_sevs_sel.iloc[:,:,:,:5]\n", - "#setting the after 72 months to selected tail\n", - "e5_ipsev_full.iloc[:,:,:,5:] = (\n", + "# extending the severity triangle to 108 months\n", + "e5_ipsev_full = cl.concat(\n", + " (e5_ipsev_full, e5_ipsev_full.latest_diagonal.rename(\"development\", [9999])), axis=3\n", + ")\n", + "# setting the before 60 months to selected incremental\n", + "e5_ipsev_full.iloc[:, :, :, :5] = e5_sevs_sel.iloc[:, :, :, :5]\n", + "# setting the after 72 months to selected tail\n", + "e5_ipsev_full.iloc[:, :, :, 5:] = (\n", " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", - " /\n", - " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum() \n", + " / e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()\n", ")\n", - "#setting the valuation_date to the future\n", + "# setting the valuation_date to the future\n", "e5_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", - "#trending back from latest accident year\n", - "e5_ipsev_full_detrended = e5_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01')\n", - "#compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle\n", - "e5_ipsev_full_complete = e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date] + e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]\n", + "# trending back from latest accident year\n", + "e5_ipsev_full_detrended = e5_ipsev_full.trend(\n", + " 1 / 1.05 - 1, start=\"2008-12-31\", end=\"2001-01-01\"\n", + ")\n", + "# compositing the actual incremental severities with dtrended selected to complete the incremental paid severity triangle\n", + "e5_ipsev_full_complete = (\n", + " e5_ipsev[e5_ipsev.valuation <= e5_ipsev.valuation_date]\n", + " + e5_ipsev_full_detrended[\n", + " e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date\n", + " ]\n", + ")\n", "\n", - "#complete the incremental paid triangle\n", + "# complete the incremental paid triangle\n", "e5_s11_ccc = e5_dr_tri.full_triangle_.cum_to_incr()\n", - "e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete\n" + "e5_s11_ip = e5_s11_ccc * e5_ipsev_full_complete" ] }, { @@ -2318,10 +2539,22 @@ }, "outputs": [], "source": [ - "e5_ccc_df = cl.model_diagnostics(e5_ccc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e5_rcc_df = cl.model_diagnostics(e5_rcc_cl).to_frame(keepdims=True,implicit_axis=True).set_index('origin')\n", - "e5_s3 = e5_ccc_df[['development','Latest','Ultimate']].rename(columns={'Latest':'Closed Claim Counts','Ultimate':'Ult Count Using CCC'})\n", - "e5_s3[['Reported Claim Counts','Ult Count Using RCC']] = e5_rcc_df[['Latest','Ultimate']]\n", + "e5_ccc_df = (\n", + " cl.model_diagnostics(e5_ccc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e5_rcc_df = (\n", + " cl.model_diagnostics(e5_rcc_cl)\n", + " .to_frame(keepdims=True, implicit_axis=True)\n", + " .set_index(\"origin\")\n", + ")\n", + "e5_s3 = e5_ccc_df[[\"development\", \"Latest\", \"Ultimate\"]].rename(\n", + " columns={\"Latest\": \"Closed Claim Counts\", \"Ultimate\": \"Ult Count Using CCC\"}\n", + ")\n", + "e5_s3[[\"Reported Claim Counts\", \"Ult Count Using RCC\"]] = e5_rcc_df[\n", + " [\"Latest\", \"Ultimate\"]\n", + "]\n", "e5_s3[\"Selected Ult CC\"] = e5_cc_ult.round(0).latest_diagonal.to_frame()\n", "e5_s3" ] @@ -2360,11 +2593,15 @@ }, "outputs": [], "source": [ - "print('Part 1 - Disposal Rate Triangle')\n", - "nb_display(cl.DisposalRate().fit_transform(e5_tri['Closed Claim Counts'],sample_weight = e5_cc_ult).disposal_rate_tri.round(3))\n", - "print('PART 2 - Average Disposal Rate Factors')\n", + "print(\"Part 1 - Disposal Rate Triangle\")\n", + "nb_display(\n", + " cl.DisposalRate()\n", + " .fit_transform(e5_tri[\"Closed Claim Counts\"], sample_weight=e5_cc_ult)\n", + " .disposal_rate_tri.round(3)\n", + ")\n", + "print(\"PART 2 - Average Disposal Rate Factors\")\n", "nb_display(combine_disposal(e5_drs).round(3).to_frame())\n", - "print('PART 3 - Selected Disposal Rate Factors')\n", + "print(\"PART 3 - Selected Disposal Rate Factors\")\n", "nb_display(e5_dr_selected.disposal_rate_)" ] }, @@ -2402,9 +2639,9 @@ }, "outputs": [], "source": [ - "print('Closed Claim Counts')\n", - "nb_display(e5_tri['Closed Claim Counts'])\n", - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Closed Claim Counts\")\n", + "nb_display(e5_tri[\"Closed Claim Counts\"])\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e5_dr_tri.full_triangle_.cum_to_incr().round(0))" ] }, @@ -2442,13 +2679,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e5_tri[\"Paid Claims\"])\n", - "print('Incremental Paid CLaims')\n", + "print(\"Incremental Paid CLaims\")\n", "nb_display(e5_tri[\"Paid Claims\"].cum_to_incr())\n", - "print('Incremental Closed Claim Counts')\n", + "print(\"Incremental Closed Claim Counts\")\n", "nb_display(e5_tri[\"Closed Claim Counts\"].cum_to_incr())\n", - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)" ] }, @@ -2486,12 +2723,12 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Annual Change based on Exponential Regression')\n", - "nb_display(pd.concat([e5_trend.round(3),e5_trend_ex2001.round(3)]))\n", - "print('Goodness of Fit Test of Exponential Regression (R-Squared)')\n", - "nb_display(pd.concat([e5_rsq.round(3),e5_rsq_ex2001.round(3)]))" + "print(\"Annual Change based on Exponential Regression\")\n", + "nb_display(pd.concat([e5_trend.round(3), e5_trend_ex2001.round(3)]))\n", + "print(\"Goodness of Fit Test of Exponential Regression (R-Squared)\")\n", + "nb_display(pd.concat([e5_rsq.round(3), e5_rsq_ex2001.round(3)]))" ] }, { @@ -2528,14 +2765,14 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Trended Incremental Paid Severities Assuming 5% Annual Trend')\n", + "print(\"Trended Incremental Paid Severities Assuming 5% Annual Trend\")\n", "nb_display(e5_ipsev.trend(0.05))\n", - "print('Average Trended Incremental Paid Severities')\n", + "print(\"Average Trended Incremental Paid Severities\")\n", "nb_display(combine_tri(e5_sevs).round(0).to_frame())\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e5_sevs_sel[e5_sevs_sel.development<=60])" + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e5_sevs_sel[e5_sevs_sel.development <= 60])" ] }, { @@ -2573,26 +2810,34 @@ "outputs": [], "source": [ "e5_s9 = pd.DataFrame(\n", - " data = [[\n", - " e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(), \n", - " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum()\n", - " ]],\n", - " index = ['Total Closed Claim Counts'],\n", - " columns = ['Age 60 & Older','Age 72 & Older']\n", - ")\n", - "e5_s9.loc['Total Trended Paid Claims'] = [\n", - " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(), \n", - " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum()\n", + " data=[\n", + " [\n", + " e5_incr_ccc[e5_incr_ccc.development >= 60].sum().sum(),\n", + " e5_incr_ccc[e5_incr_ccc.development >= 72].sum().sum(),\n", + " ]\n", + " ],\n", + " index=[\"Total Closed Claim Counts\"],\n", + " columns=[\"Age 60 & Older\", \"Age 72 & Older\"],\n", + ")\n", + "e5_s9.loc[\"Total Trended Paid Claims\"] = [\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60].sum().sum(),\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 72].sum().sum(),\n", + "]\n", + "e5_s9.loc[\"Estimated Trended Tail Severity\"] = e5_s9.iloc[1] / e5_s9.iloc[0]\n", + "e5_s9.loc[\"Estimated Incremental Trended Tail Severity\"] = e5_sevs_sel.iloc[\n", + " :, :, :, 4:6\n", + "].values.flatten()\n", + "e5_s9.loc[\"Selected Trended Paid Severity\"] = e5_ipsev_full.values[0, 0, -1, 4:6]\n", + "print(\"Incremental Closed Claim Counts\")\n", + "nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= \"2004\"])\n", + "print(\"Incremental Paid CLaims\")\n", + "nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= \"2004\"])\n", + "print(\"Trended Incremental Paid Severities\")\n", + "nb_display(\n", + " e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][\n", + " e5_incr_paid_trended.origin <= \"2004\"\n", " ]\n", - "e5_s9.loc['Estimated Trended Tail Severity'] = e5_s9.iloc[1] / e5_s9.iloc[0]\n", - "e5_s9.loc['Estimated Incremental Trended Tail Severity'] = e5_sevs_sel.iloc[:,:,:,4:6].values.flatten()\n", - "e5_s9.loc['Selected Trended Paid Severity'] = e5_ipsev_full.values[0,0,-1,4:6]\n", - "print('Incremental Closed Claim Counts')\n", - "nb_display(e5_incr_ccc[e5_incr_ccc.development >= 60][e5_incr_ccc.origin <= '2004'])\n", - "print('Incremental Paid CLaims')\n", - "nb_display(e5_incr_paid[e5_incr_paid.development >= 60][e5_incr_paid.origin <= '2004'])\n", - "print('Trended Incremental Paid Severities')\n", - "nb_display(e5_incr_paid_trended[e5_incr_paid_trended.development >= 60][e5_incr_paid_trended.origin <= '2004'])\n", + ")\n", "nb_display(e5_s9)" ] }, @@ -2630,11 +2875,13 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e5_ipsev)\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e5_ipsev_full[e5_ipsev_full.origin == '2008'])\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e5_ipsev_full[e5_ipsev_full.origin == \"2008\"])\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate\"\n", + ")\n", "nb_display(e5_ipsev_full_complete)" ] }, @@ -2672,13 +2919,15 @@ }, "outputs": [], "source": [ - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e5_s11_ccc.round(0))\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate')\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate\"\n", + ")\n", "nb_display(e5_ipsev_full_complete)\n", - "print('Projected Incremental Paid Claims')\n", + "print(\"Projected Incremental Paid Claims\")\n", "nb_display(e5_s11_ip)\n", - "print('Projected Cumulative Paid Claims')\n", + "print(\"Projected Cumulative Paid Claims\")\n", "nb_display(e5_s11_ip.incr_to_cum())" ] }, @@ -2717,9 +2966,9 @@ "outputs": [], "source": [ "summary_exh(\n", - " e5_tri['Reported Claims']/1000,\n", - " e5_tri['Paid Claims']/1000,\n", - " e5_s11_ip.incr_to_cum()/1000\n", + " e5_tri[\"Reported Claims\"] / 1000,\n", + " e5_tri[\"Paid Claims\"] / 1000,\n", + " e5_s11_ip.incr_to_cum() / 1000,\n", ")" ] }, @@ -2749,12 +2998,9 @@ "execution_count": 0, "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, - "nuid": "2a46eef8-f279-4172-b5e1-a23f079ad80e", + "nuid": "520b1f99-ec88-4331-8cdf-56f3bb0d36e2", "showTitle": false, "tableResultSettingsMap": {}, "title": "" @@ -2762,52 +3008,95 @@ }, "outputs": [], "source": [ - "#Exhibit V Sheet 1\n", - "assert np.all(e5_ccc_selected.cdf_.round(3).values == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100]))\n", - "#Exhibit V Sheet 2\n", - "assert np.all(e5_rcc_selected.cdf_.round(3).values == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000]))\n", - "#Exhibit V Sheet 3\n", - "assert np.all(e5_s3['Selected Ult CC'].values==np.array([873., 720., 626., 629., 588., 553, 438., 609.]))\n", - "#Exhibit V Sheet 4\n", - "assert np.all(e5_dr_selected.disposal_rate_.values == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000]))\n", - "#Exhibit V Sheet 5\n", - "lhs = (e5_dr_tri.full_triangle_.cum_to_incr()-e5_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", - "rhs = np.array([\n", - " 77., \n", - " 24., 70., \n", - " 12., 18., 54., \n", - " 46., 13., 19., 57., \n", - " 52., 45., 13., 19., 56., \n", - " 76., 49., 43., 12., 18., 54., \n", - " 67., 55., 36., 31., 9., 13., 39., \n", - "140., 91., 75., 49., 43., 12., 18., 53.\n", - "])\n", + "# ruff: noqa: E241\n", + "# Exhibit V Sheet 1\n", + "assert np.all(\n", + " e5_ccc_selected.cdf_.round(3).values\n", + " == np.array([4.769, 2.199, 1.682, 1.390, 1.256, 1.174, 1.123, 1.100])\n", + ")\n", + "# Exhibit V Sheet 2\n", + "assert np.all(\n", + " e5_rcc_selected.cdf_.round(3).values\n", + " == np.array([0.753, 0.746, 0.818, 0.897, 0.932, 0.968, 1.007, 1.000])\n", + ")\n", + "# Exhibit V Sheet 3\n", + "assert np.all(\n", + " e5_s3[\"Selected Ult CC\"].values\n", + " == np.array([873.0, 720.0, 626.0, 629.0, 588.0, 553, 438.0, 609.0])\n", + ")\n", + "# Exhibit V Sheet 4\n", + "assert np.all(\n", + " e5_dr_selected.disposal_rate_.values\n", + " == np.array([0.200, 0.433, 0.585, 0.710, 0.791, 0.862, 0.882, 0.912, 1.000])\n", + ")\n", + "# Exhibit V Sheet 5\n", + "lhs = (\n", + " e5_dr_tri.full_triangle_.cum_to_incr() - e5_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ").values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 77., \n", + " 24., 70., \n", + " 12., 18., 54., \n", + " 46., 13., 19., 57., \n", + " 52., 45., 13., 19., 56., \n", + " 76., 49., 43., 12., 18., 54., \n", + " 67., 55., 36., 31., 9., 13., 39., \n", + " 140., 91., 75., 49., 43., 12., 18., 53.,\n", + " ]\n", + ")\n", "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", - "#Exhibit V Sheet 7\n", - "assert np.all(e5_trend.loc['latest_4'].values[:7].round(3) == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331]))\n", - "assert np.all(e5_rsq.loc['latest_6'].values[:7].round(3) == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000]))\n", - "#Exhibit V Sheet 8\n", - "assert np.all(e5_sevs_sel.values[...,:5].round(0) == np.array([11259., 32980., 65523., 80544., 140802.]))\n", - "#Exhibit V Sheet 9\n", - "assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160., 175816.]))\n", - "#Exhibit V Sheet 10\n", - "lhs = e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date].round(0).values.flatten()\n", - "rhs = np.array([\n", - " 124949.,\n", - " 131196.,131196.,\n", - " 137756.,137756.,137756.,\n", - " 144644.,144644.,144644.,144644.,\n", - " 121630.,151876.,151876.,151876.,151876.,\n", - " 73056.,\t127711.,159470.,159470.,159470.,159470.,\n", - " 62403.,\t76709.,\t134097.,167443.,167443.,167443.,167443.,\n", - "32980., 65523.,\t80544.,\t140802.,175816.,175816.,175816.,175816.,\n", - "])\n", + "# Exhibit V Sheet 7\n", + "assert np.all(\n", + " e5_trend.loc[\"latest_4\"].values[:7].round(3)\n", + " == np.array([0.018, 0.016, 0.095, 0.050, 0.122, -0.294, -0.331])\n", + ")\n", + "assert np.all(\n", + " e5_rsq.loc[\"latest_6\"].values[:7].round(3)\n", + " == np.array([0.704, 0.000, 0.644, 0.722, 0.084, 0.190, 1.000])\n", + ")\n", + "# Exhibit V Sheet 8\n", + "assert np.all(\n", + " e5_sevs_sel.values[..., :5].round(0)\n", + " == np.array([11259.0, 32980.0, 65523.0, 80544.0, 140802.0])\n", + ")\n", + "# Exhibit V Sheet 9\n", + "assert np.all(e5_s9.iloc[2].values.round(0) == np.array([144160.0, 175816.0]))\n", + "# Exhibit V Sheet 10\n", + "lhs = (\n", + " e5_ipsev_full_detrended[e5_ipsev_full_detrended.valuation > e5_ipsev.valuation_date]\n", + " .round(0)\n", + " .values.flatten()\n", + ")\n", + "rhs = np.array(\n", + " [\n", + " 124949.,\n", + " 131196.,131196.,\n", + " 137756.,137756.,137756.,\n", + " 144644.,144644.,144644.,144644.,\n", + " 121630.,151876.,151876.,151876.,151876.,\n", + " 73056.,\t127711.,159470.,159470.,159470.,159470.,\n", + " 62403.,\t76709.,\t134097.,167443.,167443.,167443.,167443.,\n", + " 32980., 65523.,\t80544.,\t140802.,175816.,175816.,175816.,175816.,\n", + " ]\n", + ")\n", "assert np.all(lhs[~np.isnan(lhs)] == rhs)\n", - "#Exhibit V Sheet 11\n", + "# Exhibit V Sheet 11\n", "assert np.allclose(\n", - " e5_s11_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", - " np.array([39497433., 44743308., 35510981., 37766027., 42007442., 41113459., 32859080., 47109641.]),\n", - " rtol = .001\n", + " e5_s11_ip.incr_to_cum().iloc[:, :, :, -1].values.round(0).flatten(),\n", + " np.array(\n", + " [\n", + " 39497433.0,\n", + " 44743308.0,\n", + " 35510981.0,\n", + " 37766027.0,\n", + " 42007442.0,\n", + " 41113459.0,\n", + " 32859080.0,\n", + " 47109641.0,\n", + " ]\n", + " ),\n", + " rtol=0.001,\n", ")" ] }, @@ -2845,61 +3134,77 @@ }, "outputs": [], "source": [ - "#loading data and assumptions\n", - "e6_tri = e2_tri[e2_tri.origin >= '2001'][e2_tri.development <= 96]\n", + "# loading data and assumptions\n", + "e6_tri = e2_tri[e2_tri.origin >= \"2001\"][e2_tri.development <= 96]\n", "e6_disp_assumptions = {}\n", - "e6_disp_assumptions['simple_3'] = {'n_periods':3, 'average':'simple'}\n", - "e6_disp_assumptions['simple_2'] = {'n_periods':2, 'average':'simple'}\n", - "e6_disp_assumptions['medial_5x1'] = {'n_periods':5, 'average':'simple','drop_high':1, 'drop_low':1}\n", + "e6_disp_assumptions[\"simple_3\"] = {\"n_periods\": 3, \"average\": \"simple\"}\n", + "e6_disp_assumptions[\"simple_2\"] = {\"n_periods\": 2, \"average\": \"simple\"}\n", + "e6_disp_assumptions[\"medial_5x1\"] = {\n", + " \"n_periods\": 5,\n", + " \"average\": \"simple\",\n", + " \"drop_high\": 1,\n", + " \"drop_low\": 1,\n", + "}\n", "e6_sev_assumptions = {}\n", - "e6_sev_assumptions['simple_3'] = {'n_periods':3}\n", - "e6_sev_assumptions['simple_2'] = {'n_periods':2}\n", - "e6_sev_assumptions['medial_5x1'] = {'n_periods':5,'drop_high':1, 'drop_low':1}\n", + "e6_sev_assumptions[\"simple_3\"] = {\"n_periods\": 3}\n", + "e6_sev_assumptions[\"simple_2\"] = {\"n_periods\": 2}\n", + "e6_sev_assumptions[\"medial_5x1\"] = {\"n_periods\": 5, \"drop_high\": 1, \"drop_low\": 1}\n", "\n", - "#calculating disposal rate and complete the closed count triangle\n", - "e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= '2001']\n", - "e6_drs = average_dr(e6_tri['Closed Claim Counts'],e6_rcc_ult,e6_disp_assumptions)\n", - "e6_dr_selected = e6_drs['simple_2']\n", + "# calculating disposal rate and complete the closed count triangle\n", + "e6_rcc_ult = e2_rcc_cl.ultimate_[e2_rcc_cl.ultimate_.origin >= \"2001\"]\n", + "e6_drs = average_dr(e6_tri[\"Closed Claim Counts\"], e6_rcc_ult, e6_disp_assumptions)\n", + "e6_dr_selected = e6_drs[\"simple_2\"]\n", "e6_dr_selected.disposal_rate_ = e6_dr_selected.disposal_rate_.round(3)\n", - "e6_dr_tri = e6_dr_selected.transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult)\n", + "e6_dr_tri = e6_dr_selected.transform(\n", + " e6_tri[\"Closed Claim Counts\"], sample_weight=e6_rcc_ult\n", + ")\n", "\n", - "#calculate and select incremental paid severity\n", - "#in a real world scenario, it's possible to calculate incremental paid severity like this\n", + "# calculate and select incremental paid severity\n", + "# in a real world scenario, it's possible to calculate incremental paid severity like this\n", "# e6_tri[\"Paid Claims\"].cum_to_incr() / e6_tri[\"Closed Claim Counts\"].cum_to_incr() * 1000\n", - "#however, the cumulative paid data in Friedland is only given in thousands\n", - "#therefore, we will directly use the incremental paid severity data as given\n", - "e6_ipsev = e6_tri['Incremental Paid Severity']\n", + "# however, the cumulative paid data in Friedland is only given in thousands\n", + "# therefore, we will directly use the incremental paid severity data as given\n", + "e6_ipsev = e6_tri[\"Incremental Paid Severity\"]\n", "e6_ipsev_adj = e6_ipsev.trend(0.05) * xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", - "e6_sevs = average_sev(e6_ipsev_adj,e6_sev_assumptions)\n", - "e6_sevs_sel = e6_sevs['simple_2'].copy()\n", - "e6_sevs_sel.origin = ['2008']\n", + "e6_sevs = average_sev(e6_ipsev_adj, e6_sev_assumptions)\n", + "e6_sevs_sel = e6_sevs[\"simple_2\"].copy()\n", + "e6_sevs_sel.origin = [\"2008\"]\n", "\n", - "#calculate and select tail paid severity\n", + "# calculate and select tail paid severity\n", "e6_incr_ccc = e6_tri[\"Closed Claim Counts\"].cum_to_incr()\n", "e6_incr_paid = e6_tri[\"Paid Claims\"].cum_to_incr() * 1000\n", "e6_incr_paid_trended = e6_ipsev_adj * e6_incr_ccc\n", "\n", "e6_ipsev_full = e6_ipsev_adj.copy()\n", - "#extending the severity triangle to 108 months\n", - "e6_ipsev_full = cl.concat((e6_ipsev_full,e6_ipsev_full.latest_diagonal.rename(\"development\",[9999])),axis=3)\n", - "#setting the before 72 months to selected incremental\n", - "e6_ipsev_full.iloc[:,:,:,:6] = e6_sevs_sel.iloc[:,:,:,:6]\n", - "#setting the after 86 months to selected tail\n", - "e6_ipsev_full.iloc[:,:,:,6:] = (\n", + "# extending the severity triangle to 108 months\n", + "e6_ipsev_full = cl.concat(\n", + " (e6_ipsev_full, e6_ipsev_full.latest_diagonal.rename(\"development\", [9999])), axis=3\n", + ")\n", + "# setting the before 72 months to selected incremental\n", + "e6_ipsev_full.iloc[:, :, :, :6] = e6_sevs_sel.iloc[:, :, :, :6]\n", + "# setting the after 86 months to selected tail\n", + "e6_ipsev_full.iloc[:, :, :, 6:] = (\n", " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", - " /\n", - " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum() \n", + " / e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()\n", ")\n", - "#setting the valuation_date to the future\n", + "# setting the valuation_date to the future\n", "e6_ipsev_full.valuation_date = pd.to_datetime(cl.options.ULT_VAL)\n", - "#trending back from latest accident year\n", - "e6_ipsev_full_detrended = e6_ipsev_full.trend(1/1.05-1,start='2008-12-31',end='2001-01-01') / xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", - "#compositing the actual incremental severities with dtrended selected\n", - "e6_ipsev_full_complete = e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date] + e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date]\n", + "# trending back from latest accident year\n", + "e6_ipsev_full_detrended = (\n", + " e6_ipsev_full.trend(1 / 1.05 - 1, start=\"2008-12-31\", end=\"2001-01-01\")\n", + " / xyz_tort_adjustment.fit(e6_ipsev).olf_.values\n", + ")\n", + "# compositing the actual incremental severities with dtrended selected\n", + "e6_ipsev_full_complete = (\n", + " e6_ipsev[e6_ipsev.valuation <= e6_ipsev.valuation_date]\n", + " + e6_ipsev_full_detrended[\n", + " e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date\n", + " ]\n", + ")\n", "\n", - "#complete the incremental paid triangle\n", + "# complete the incremental paid triangle\n", "e6_s7_ccc = e6_dr_tri.full_triangle_.cum_to_incr()\n", - "e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000\n" + "e6_s7_ip = e6_s7_ccc * e6_ipsev_full_complete / 1000" ] }, { @@ -2936,13 +3241,17 @@ }, "outputs": [], "source": [ - "print('Part 1 - Disposal Rate Triangle')\n", - "nb_display(cl.DisposalRate().fit_transform(e6_tri['Closed Claim Counts'],sample_weight = e6_rcc_ult).disposal_rate_tri.round(3))\n", + "print(\"Part 1 - Disposal Rate Triangle\")\n", + "nb_display(\n", + " cl.DisposalRate()\n", + " .fit_transform(e6_tri[\"Closed Claim Counts\"], sample_weight=e6_rcc_ult)\n", + " .disposal_rate_tri.round(3)\n", + ")\n", "\n", - "print('PART 2 - Average Disposal Rate Factors')\n", + "print(\"PART 2 - Average Disposal Rate Factors\")\n", "nb_display(combine_disposal(e6_drs).round(3).to_frame())\n", "\n", - "print('PART 3 - Selected Disposal Rate Factors')\n", + "print(\"PART 3 - Selected Disposal Rate Factors\")\n", "nb_display(e6_dr_selected.disposal_rate_)" ] }, @@ -2980,9 +3289,9 @@ }, "outputs": [], "source": [ - "print('Closed Claim Counts')\n", - "nb_display(e6_tri['Closed Claim Counts'])\n", - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Closed Claim Counts\")\n", + "nb_display(e6_tri[\"Closed Claim Counts\"])\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e6_dr_tri.full_triangle_.cum_to_incr().round(0))" ] }, @@ -3020,13 +3329,13 @@ }, "outputs": [], "source": [ - "print('Paid Claims')\n", + "print(\"Paid Claims\")\n", "nb_display(e6_tri[\"Paid Claims\"])\n", - "print('Incremental Paid CLaims')\n", + "print(\"Incremental Paid CLaims\")\n", "nb_display(e6_tri[\"Paid Claims\"].cum_to_incr())\n", - "print('Incremental Closed Claim Counts')\n", + "print(\"Incremental Closed Claim Counts\")\n", "nb_display(e6_tri[\"Closed Claim Counts\"].cum_to_incr())\n", - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)" ] }, @@ -3064,14 +3373,16 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)\n", - "print('Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform')\n", + "print(\n", + " \"Trended Incremental Paid Severities Assuming 5% Annual Trend and Adjusted for Tort Reform\"\n", + ")\n", "nb_display(e6_ipsev_adj)\n", - "print('Average Trended Incremental Paid Severities')\n", + "print(\"Average Trended Incremental Paid Severities\")\n", "nb_display(combine_tri(e6_sevs).round(0).to_frame())\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e6_sevs_sel[e6_sevs_sel.development<=72])" + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e6_sevs_sel[e6_sevs_sel.development <= 72])" ] }, { @@ -3108,27 +3419,35 @@ }, "outputs": [], "source": [ - "print('Incremental Closed Claim Counts')\n", - "nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= '2003'])\n", - "print('Incremental Paid CLaims')\n", - "nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= '2003'])\n", - "print('Trended Incremental Paid Severities')\n", - "nb_display(e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][e6_incr_paid_trended.origin <= '2003'])\n", - "e6_s5 = pd.DataFrame(\n", - " data = [[\n", - " e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(), \n", - " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum()\n", - " ]],\n", - " index = ['Total Closed Claim Counts'],\n", - " columns = ['Age 72 & Older','Age 84 & Older']\n", - ")\n", - "e6_s5.loc['Total Trended Paid Claims'] = [\n", - " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(), \n", - " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum()\n", + "print(\"Incremental Closed Claim Counts\")\n", + "nb_display(e6_incr_ccc[e6_incr_ccc.development >= 72][e6_incr_ccc.origin <= \"2003\"])\n", + "print(\"Incremental Paid CLaims\")\n", + "nb_display(e6_incr_paid[e6_incr_paid.development >= 72][e6_incr_paid.origin <= \"2003\"])\n", + "print(\"Trended Incremental Paid Severities\")\n", + "nb_display(\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72][\n", + " e6_incr_paid_trended.origin <= \"2003\"\n", " ]\n", - "e6_s5.loc['Estimated Trended Tail Severity'] = e6_s5.iloc[1] / e6_s5.iloc[0]\n", - "e6_s5.loc['Estimated Incremental Trended Tail Severity'] = e6_sevs_sel.iloc[:,:,:,5:7].values.flatten()\n", - "e6_s5.loc['Selected Trended Paid Severity'] = e6_ipsev_full.values[0,0,-1,5:7]\n", + ")\n", + "e6_s5 = pd.DataFrame(\n", + " data=[\n", + " [\n", + " e6_incr_ccc[e6_incr_ccc.development >= 72].sum().sum(),\n", + " e6_incr_ccc[e6_incr_ccc.development >= 84].sum().sum(),\n", + " ]\n", + " ],\n", + " index=[\"Total Closed Claim Counts\"],\n", + " columns=[\"Age 72 & Older\", \"Age 84 & Older\"],\n", + ")\n", + "e6_s5.loc[\"Total Trended Paid Claims\"] = [\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 72].sum().sum(),\n", + " e6_incr_paid_trended[e6_incr_paid_trended.development >= 84].sum().sum(),\n", + "]\n", + "e6_s5.loc[\"Estimated Trended Tail Severity\"] = e6_s5.iloc[1] / e6_s5.iloc[0]\n", + "e6_s5.loc[\"Estimated Incremental Trended Tail Severity\"] = e6_sevs_sel.iloc[\n", + " :, :, :, 5:7\n", + "].values.flatten()\n", + "e6_s5.loc[\"Selected Trended Paid Severity\"] = e6_ipsev_full.values[0, 0, -1, 5:7]\n", "nb_display(e6_s5)" ] }, @@ -3166,11 +3485,13 @@ }, "outputs": [], "source": [ - "print('Incremental Paid Severities')\n", + "print(\"Incremental Paid Severities\")\n", "nb_display(e6_ipsev)\n", - "print('Selected Incremental Paid Severities')\n", - "nb_display(e6_ipsev_full[e6_ipsev_full.origin == '2008'])\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "print(\"Selected Incremental Paid Severities\")\n", + "nb_display(e6_ipsev_full[e6_ipsev_full.origin == \"2008\"])\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment\"\n", + ")\n", "nb_display(e6_ipsev_full_complete)" ] }, @@ -3208,13 +3529,15 @@ }, "outputs": [], "source": [ - "print('Projected Incremental Closed Claim Counts')\n", + "print(\"Projected Incremental Closed Claim Counts\")\n", "nb_display(e6_s7_ccc.round(0))\n", - "print('Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment')\n", + "print(\n", + " \"Incremental Paid Severities Adjusted to Cost Level of Accident Year Assuming 5% Annual Trend Rate and Tort Reform Adjustment\"\n", + ")\n", "nb_display(e6_ipsev_full_complete)\n", - "print('Projected Incremental Paid Claims')\n", + "print(\"Projected Incremental Paid Claims\")\n", "nb_display(e6_s7_ip)\n", - "print('Projected Cumulative Paid Claims')\n", + "print(\"Projected Cumulative Paid Claims\")\n", "nb_display(e6_s7_ip.incr_to_cum())" ] }, @@ -3252,11 +3575,7 @@ }, "outputs": [], "source": [ - "summary_exh(\n", - " e6_tri['Reported Claims'],\n", - " e6_tri['Paid Claims'],\n", - " e6_s7_ip.incr_to_cum()\n", - ")" + "summary_exh(e6_tri[\"Reported Claims\"], e6_tri[\"Paid Claims\"], e6_s7_ip.incr_to_cum())" ] }, { @@ -3264,12 +3583,9 @@ "execution_count": 0, "metadata": { "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, + "cellMetadata": {}, "inputWidgets": {}, - "nuid": "e03f91fe-ff00-47ee-9994-3caf56a38ab1", + "nuid": "d192b52a-32a1-4b88-895c-7e6747278d7f", "showTitle": false, "tableResultSettingsMap": {}, "title": "" @@ -3277,44 +3593,58 @@ }, "outputs": [], "source": [ - "#Exhibit VI Sheet 1\n", - "assert np.all(e6_dr_selected.disposal_rate_.values == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000]))\n", - "#Exhibit VI Sheet 2\n", - "lhs = (e6_dr_tri.full_triangle_.cum_to_incr()-e6_tri['Closed Claim Counts'].cum_to_incr()).values.flatten()\n", - "rhs = np.array([\n", - " 9., \n", - " 21., 10., \n", - " 39., 16., 8., \n", - " 109., 78., 31., 16., \n", - " 235., 103., 74., 29., 15., \n", - " 177., 155., 68., 48., 19., 10., \n", - " 160., 133., 116., 51., 36., 15., 7., \n", - "389., 156., 130., 114., 50., 36., 14., 7.\n", - "])\n", + "# Exhibit VI Sheet 1\n", + "assert np.all(\n", + " e6_dr_selected.disposal_rate_.values\n", + " == np.array([0.244, 0.572, 0.704, 0.814, 0.910, 0.952, 0.982, 0.994, 1.000])\n", + ")\n", + "# Exhibit VI Sheet 2\n", + "lhs = (\n", + " e6_dr_tri.full_triangle_.cum_to_incr() - e6_tri[\"Closed Claim Counts\"].cum_to_incr()\n", + ").values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 9., \n", + " 21., 10., \n", + " 39., 16., 8., \n", + " 109., 78., 31., 16., \n", + " 235., 103., 74., 29., 15., \n", + " 177., 155., 68., 48., 19., 10., \n", + " 160., 133., 116., 51., 36., 15., 7., \n", + " 389., 156., 130., 114., 50., 36., 14., 7.,\n", + " ]\n", + ")\n", "assert np.all(abs(lhs[~np.isnan(lhs)] - rhs) < 1)\n", - "#Exhibit VI Sheet 4\n", - "assert np.all(e6_sevs_sel.values[...,:6].round(0) == np.array([11807.,15165.,26043.,35183.,41908.,62206.]))\n", - "#Exhibit VI Sheet 5\n", - "assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.,70432.]))\n", - "#Exhibit VI Sheet 6\n", - "lhs = e6_ipsev_full_detrended[e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date].values.flatten()\n", - "rhs = np.array([\n", - " 74709., \n", - " 78444., 78444., \n", - " 82367., 82367., 82367., \n", - " 76384., 86485., 86485., 86485., \n", - " 54032., 80203., 90809., 90809., 90809., \n", - " 42549., 50682., 75230., 85179., 85179., 85179., \n", - " 24803., 33508., 39912., 59244., 67079., 67079., 67079., \n", - "15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., \n", - "])\n", + "# Exhibit VI Sheet 4\n", + "assert np.all(\n", + " e6_sevs_sel.values[..., :6].round(0)\n", + " == np.array([11807.0, 15165.0, 26043.0, 35183.0, 41908.0, 62206.0])\n", + ")\n", + "# Exhibit VI Sheet 5\n", + "assert np.all(e6_s5.iloc[4].values.round(0) == np.array([62206.0, 70432.0]))\n", + "# Exhibit VI Sheet 6\n", + "lhs = e6_ipsev_full_detrended[\n", + " e6_ipsev_full_detrended.valuation > e6_ipsev.valuation_date\n", + "].values.flatten()\n", + "rhs = np.array(\n", + " [\n", + " 74709., \n", + " 78444., 78444., \n", + " 82367., 82367., 82367., \n", + " 76384., 86485., 86485., 86485., \n", + " 54032., 80203., 90809., 90809., 90809., \n", + " 42549., 50682., 75230., 85179., 85179., 85179., \n", + " 24803., 33508., 39912., 59244., 67079., 67079., 67079., \n", + " 15165., 26043., 35183., 41908., 62206., 70432., 70432., 70432., \n", + " ]\n", + ")\n", "assert np.allclose(lhs[~np.isnan(lhs)], rhs, atol=1)\n", - "#Exhibit VI Sheet 7\n", + "# Exhibit VI Sheet 7\n", "assert np.allclose(\n", - " e6_s7_ip.incr_to_cum().iloc[:,:,:,-1].values.round(0).flatten(),\n", - " np.array([39192.,46869.,44479.,71906.,71684.,49913.,31805.,29828.]),\n", - " rtol = .001\n", - ")\n" + " e6_s7_ip.incr_to_cum().iloc[:, :, :, -1].values.round(0).flatten(),\n", + " np.array([39192.0, 46869.0, 44479.0, 71906.0, 71684.0, 49913.0, 31805.0, 29828.0]),\n", + " rtol=0.001,\n", + ")" ] } ], From 9c11fad57714d1d5e5680769ad516a20f2275ed5 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 22:44:35 +0000 Subject: [PATCH 26/27] ruff fix --- chainladder/utils/tests/test_utilities.py | 4 +- docs/friedland/chapter_11.ipynb | 151 ++++++++++++---------- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 5f33c24ee..f52249d2d 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -189,8 +189,8 @@ def test_model_diagnostics_groupby(prism, atol): est = cl.Chainladder().fit(dev.transform(prism["Incurred"])) lhs = cl.model_diagnostics(est, groupby=['Line']) rhs = cl.model_diagnostics(cl.Chainladder().fit(dev.transform(prism["Incurred"].groupby('Line').sum()))) - assert np.allclose(lhs['Ultimate'].values, rhs['Ultimate'].values,atol=atol, equal_nan=True) - assert np.allclose(np.nan_to_num(lhs['IBNR'].values), np.nan_to_num(rhs['IBNR'].values),atol=atol, equal_nan=True) + assert np.allclose(lhs['Ultimate'].values, rhs['Ultimate'].values, atol=atol, equal_nan=True) + assert np.allclose(np.nan_to_num(lhs['IBNR'].values), np.nan_to_num(rhs['IBNR'].values), atol=atol, equal_nan=True) def test_concat_immutability(raa): diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 4a270d4f0..1fac8ff23 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -52,18 +52,17 @@ " date_format: str = \"{:%Y}\",\n", "):\n", " return (\n", - " exh_df.reset_index()\n", + " exh_df\n", + " .reset_index()\n", " .rename(columns={\"origin\": \"Accident Year\"})\n", " .style.hide(axis=\"index\")\n", " .set_properties(**{\"text-align\": \"right\"})\n", - " .format(\n", - " {\n", - " \"Accident Year\": date_format,\n", - " **{x: \"{:,.0f}\" for x in value_cols},\n", - " **{x: \"{:,.3f}\" for x in factor_cols},\n", - " **other_formats,\n", - " }\n", - " )\n", + " .format({\n", + " \"Accident Year\": date_format,\n", + " **{x: \"{:,.0f}\" for x in value_cols},\n", + " **{x: \"{:,.3f}\" for x in factor_cols},\n", + " **other_formats,\n", + " })\n", " )\n", "\n", "\n", @@ -376,12 +375,14 @@ "outputs": [], "source": [ "e1_ccc_df = (\n", - " cl.model_diagnostics(e1_ccc_cl)\n", + " cl\n", + " .model_diagnostics(e1_ccc_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", "e1_rcc_df = (\n", - " cl.model_diagnostics(e1_rcc_cl)\n", + " cl\n", + " .model_diagnostics(e1_rcc_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", @@ -571,7 +572,8 @@ "outputs": [], "source": [ "e1_rsev_ult_df = (\n", - " cl.model_diagnostics(e1_rsev_cl)\n", + " cl\n", + " .model_diagnostics(e1_rsev_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", @@ -632,8 +634,10 @@ }, "outputs": [], "source": [ - "e1_s8 = summary_exh(e1_tri['Reported Claims']/1000,e1_tri['Paid Claims']/1000,e1_ult)\n", - "format_exh(e1_s8,e1_s8.columns,[],{},\"{:%Y%m}\")" + "e1_s8 = summary_exh(\n", + " e1_tri[\"Reported Claims\"] / 1000, e1_tri[\"Paid Claims\"] / 1000, e1_ult\n", + ")\n", + "format_exh(e1_s8, e1_s8.columns, [], {}, \"{:%Y%m}\")" ] }, { @@ -825,19 +829,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e2_tri['Closed Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e2_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e2_tri[\"Closed Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e2_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e2_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e2_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e2_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display(1/e2_ccc_selected.cdf_.round(3))" + "print(\"Percent Closed\")\n", + "nb_display(1 / e2_ccc_selected.cdf_.round(3))" ] }, { @@ -926,12 +930,14 @@ "outputs": [], "source": [ "e2_ccc_df = (\n", - " cl.model_diagnostics(e2_ccc_cl)\n", + " cl\n", + " .model_diagnostics(e2_ccc_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", "e2_rcc_df = (\n", - " cl.model_diagnostics(e2_rcc_cl)\n", + " cl\n", + " .model_diagnostics(e2_rcc_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", @@ -1085,7 +1091,8 @@ "outputs": [], "source": [ "e2_rsev_df = (\n", - " cl.model_diagnostics(e2_rsev_cl)\n", + " cl\n", + " .model_diagnostics(e2_rsev_cl)\n", " .to_frame(keepdims=True, implicit_axis=True)\n", " .set_index(\"origin\")\n", ")\n", @@ -1270,7 +1277,7 @@ "e3_sevavg_assumptions[\"latest_3\"] = {\"n_periods\": 3}\n", "\n", "# developing closed claim counts\n", - "# There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor'\n", + "# There is a typo in the text. We will use a tail of 1.002 to match the 84-ult factor\n", "e3_ccc_devs = average_dev(e3_tri[\"Closed Claim Counts\"], e3_cnt_assumptions)\n", "e3_ccc_selected = cl.TailConstant(tail=1.002, projection_period=0).fit_transform(\n", " e3_ccc_devs[\"volume_5\"]\n", @@ -1686,16 +1693,16 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", + "print(\"PART 1 - Data Triangle\")\n", "nb_display(e3_rsev)\n", - "print('PART 2 - Age-to-Age Factors')\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", "nb_display(e3_rsev.age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e3_rsev_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e3_rsev_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e3_rsev_selected.cdf_.round(3))" ] }, @@ -1805,24 +1812,26 @@ }, "outputs": [], "source": [ - "e3_s9 = e3_s8[['Selected Ult Sev']].copy()\n", - "e3_s9['Sev Trend'] = e3_sev_trend.latest_diagonal.to_frame()\n", - "e3_s9['Trended Ult Sev'] = e3_sev_ult_trended.latest_diagonal.to_frame()\n", - "e3_s9[['Sev Trend']] = e3_s9[['Sev Trend']].round(3)\n", - "#using floating point offset to achieve standard rounding\n", - "e3_s9[['Trended Ult Sev']] = (e3_s9[['Trended Ult Sev']] + 1e-9).round(0)\n", - "nb_display(format_exh(\n", - " e3_s9.iloc[:-2,:],\n", - " [\n", - " 'Selected Ult Sev',\n", - " 'Trended Ult Sev',\n", - " ],\n", - " [\n", - " 'Sev Trend',\n", - " ],\n", - "))\n", - "print('Average Trended Severity at 2008 Cost Level')\n", - "for k,v in e3_sevs.items():\n", + "e3_s9 = e3_s8[[\"Selected Ult Sev\"]].copy()\n", + "e3_s9[\"Sev Trend\"] = e3_sev_trend.latest_diagonal.to_frame()\n", + "e3_s9[\"Trended Ult Sev\"] = e3_sev_ult_trended.latest_diagonal.to_frame()\n", + "e3_s9[[\"Sev Trend\"]] = e3_s9[[\"Sev Trend\"]].round(3)\n", + "# using floating point offset to achieve standard rounding\n", + "e3_s9[[\"Trended Ult Sev\"]] = (e3_s9[[\"Trended Ult Sev\"]] + 1e-9).round(0)\n", + "nb_display(\n", + " format_exh(\n", + " e3_s9.iloc[:-2, :],\n", + " [\n", + " \"Selected Ult Sev\",\n", + " \"Trended Ult Sev\",\n", + " ],\n", + " [\n", + " \"Sev Trend\",\n", + " ],\n", + " )\n", + ")\n", + "print(\"Average Trended Severity at 2008 Cost Level\")\n", + "for k, v in e3_sevs.items():\n", " print(f\"\\t{k:<20} \\t{v:,.0f}\")\n", "print(f\"Selected 2008 Severity \\t\\t{e3_sev_2008:,.0f}\")\n", "print(f\"Estimated 2007 Severity \\t{e3_sev_2007:,.0f}\")" @@ -2442,19 +2451,19 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e5_tri['Closed Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e5_tri['Closed Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e5_tri[\"Closed Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e5_tri[\"Closed Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e5_ccc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", - "print('Selected')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", + "print(\"Selected\")\n", "nb_display(e5_ccc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e5_ccc_selected.cdf_.round(3))\n", - "print('Percent Closed')\n", - "nb_display((1/e5_ccc_selected.cdf_).round(3))" + "print(\"Percent Closed\")\n", + "nb_display((1 / e5_ccc_selected.cdf_).round(3))" ] }, { @@ -2491,18 +2500,18 @@ }, "outputs": [], "source": [ - "print('PART 1 - Data Triangle')\n", - "nb_display(e5_tri['Reported Claim Counts'])\n", - "print('PART 2 - Age-to-Age Factors')\n", - "nb_display(e5_tri['Reported Claim Counts'].age_to_age.round(3))\n", - "print('PART 3 - Average Age-to-Age Factor')\n", + "print(\"PART 1 - Data Triangle\")\n", + "nb_display(e5_tri[\"Reported Claim Counts\"])\n", + "print(\"PART 2 - Age-to-Age Factors\")\n", + "nb_display(e5_tri[\"Reported Claim Counts\"].age_to_age.round(3))\n", + "print(\"PART 3 - Average Age-to-Age Factor\")\n", "nb_display(combine_ldf(e5_rcc_devs).round(3).to_frame().rename_axis(\"\"))\n", - "print('PART 4 - Selected Age-to-Age Factors')\n", + "print(\"PART 4 - Selected Age-to-Age Factors\")\n", "nb_display(e5_rcc_selected.ldf_)\n", - "print('CDF to Ultimate')\n", + "print(\"CDF to Ultimate\")\n", "nb_display(e5_rcc_selected.cdf_.round(3))\n", - "print('Percent Reported')\n", - "nb_display((1/e5_rcc_selected.cdf_).round(3))" + "print(\"Percent Reported\")\n", + "nb_display((1 / e5_rcc_selected.cdf_).round(3))" ] }, { From 40dbd46ea083b318c4a7175a4fed405e2190e7d4 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Wed, 2 Sep 2026 23:02:42 +0000 Subject: [PATCH 27/27] ruff fix --- docs/friedland/chapter_11.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/friedland/chapter_11.ipynb b/docs/friedland/chapter_11.ipynb index 1fac8ff23..be630a6fd 100644 --- a/docs/friedland/chapter_11.ipynb +++ b/docs/friedland/chapter_11.ipynb @@ -3017,7 +3017,7 @@ }, "outputs": [], "source": [ - "# ruff: noqa: E241\n", + "# ruff: noqa: E231, E241, E242\n", "# Exhibit V Sheet 1\n", "assert np.all(\n", " e5_ccc_selected.cdf_.round(3).values\n", @@ -3602,6 +3602,7 @@ }, "outputs": [], "source": [ + "# ruff: noqa: E231, E241, E242\n", "# Exhibit VI Sheet 1\n", "assert np.all(\n", " e6_dr_selected.disposal_rate_.values\n",