def test_grid(): # Load Data clrd = cl.load_sample("clrd") medmal_paid = clrd.groupby("LOB").sum().loc["medmal"]["CumPaidLoss"] medmal_prem = (clrd.groupby("LOB").sum().loc["medmal"] ["EarnedPremDIR"].latest_diagonal) medmal_prem.rename("development", ["premium"]) # Pipeline dev = cl.Development() tail = cl.TailCurve() benk = cl.Benktander() steps = [("dev", dev), ("tail", tail), ("benk", benk)] pipe = cl.Pipeline(steps) # Prep Benktander Grid Search with various assumptions, and a scoring function param_grid = dict(benk__n_iters=[250], benk__apriori=[1.00]) scoring = {"IBNR": lambda x: x.named_steps.benk.ibnr_.sum()} grid = cl.GridSearch(pipe, param_grid, scoring=scoring) # Perform Grid Search grid.fit(medmal_paid, benk__sample_weight=medmal_prem) assert (grid.results_["IBNR"][0] == cl.Benktander( n_iters=250, apriori=1).fit( cl.TailCurve().fit_transform( cl.Development().fit_transform(medmal_paid)), sample_weight=medmal_prem, ).ibnr_.sum())
def test_grid(): # Load Data clrd = cl.load_dataset('clrd') medmal_paid = clrd.groupby('LOB').sum().loc['medmal']['CumPaidLoss'] medmal_prem = clrd.groupby( 'LOB').sum().loc['medmal']['EarnedPremDIR'].latest_diagonal medmal_prem.rename('development', ['premium']) # Pipeline dev = cl.Development() tail = cl.TailCurve() benk = cl.Benktander() steps = [('dev', dev), ('tail', tail), ('benk', benk)] pipe = cl.Pipeline(steps) # Prep Benktander Grid Search with various assumptions, and a scoring function param_grid = dict(benk__n_iters=[250], benk__apriori=[1.00]) scoring = {'IBNR': lambda x: x.named_steps.benk.ibnr_.sum()[0]} grid = cl.GridSearch(pipe, param_grid, scoring=scoring) # Perform Grid Search grid.fit(medmal_paid, benk__sample_weight=medmal_prem) assert grid.results_['IBNR'][0] == \ cl.Benktander(n_iters=250, apriori=1).fit(cl.TailCurve().fit_transform(cl.Development().fit_transform(medmal_paid)), sample_weight=medmal_prem).ibnr_.sum()[0]
def test_pipeline(): tri = cl.load_sample('clrd').groupby('LOB').sum()[[ 'CumPaidLoss', 'IncurLoss', 'EarnedPremDIR' ]] tri['CaseIncurredLoss'] = tri['IncurLoss'] - tri['CumPaidLoss'] X = tri[['CumPaidLoss', 'CaseIncurredLoss']] sample_weight = tri['EarnedPremDIR'].latest_diagonal dev = [ cl.Development(), cl.ClarkLDF(), cl.Trend(), cl.IncrementalAdditive(), cl.MunichAdjustment(paid_to_incurred=('CumPaidLoss', 'CaseIncurredLoss')), cl.CaseOutstanding(paid_to_incurred=('CumPaidLoss', 'CaseIncurredLoss')) ] tail = [cl.TailCurve(), cl.TailConstant(), cl.TailBondy(), cl.TailClark()] ibnr = [ cl.Chainladder(), cl.BornhuetterFerguson(), cl.Benktander(n_iters=2), cl.CapeCod() ] for model in list(itertools.product(dev, tail, ibnr)): print(model) cl.Pipeline( steps=[('dev', model[0]), ('tail', model[1]), ('ibnr', model[2])]).fit_predict( X, sample_weight=sample_weight).ibnr_.sum( 'origin').sum('columns').sum()
def test_fit_period(): tri = cl.load_dataset('tail_sample') dev = cl.Development(average='simple').fit_transform(tri) assert round( cl.TailCurve(fit_period=slice(-6, None, None), extrap_periods=10).fit(dev).cdf_['paid'].values[0, 0, 0, -2], 3) == 1.044
def test_fit_period(): tri = cl.load_sample('tail_sample') dev = cl.Development(average='simple').fit_transform(tri) assert round( cl.TailCurve(fit_period=(tri.ddims[-7], None), extrap_periods=10).fit(dev).cdf_['paid'].set_backend( 'numpy', inplace=True).values[0, 0, 0, -2], 3) == 1.044
def test_fit_period(): tri = cl.load_sample("tail_sample") dev = cl.Development(average="simple").fit_transform(tri) assert (round( cl.TailCurve(fit_period=(tri.ddims[-7], None), extrap_periods=10).fit(dev).cdf_["paid"].set_backend( "numpy", inplace=True).values[0, 0, 0, -2], 3, ) == 1.044)
def test_basic_transform(raa): cl.Development().fit_transform(raa) cl.ClarkLDF().fit_transform(raa) cl.TailClark().fit_transform(raa) cl.TailBondy().fit_transform(raa) cl.TailConstant().fit_transform(raa) cl.TailCurve().fit_transform(raa) cl.BootstrapODPSample().fit_transform(raa) cl.IncrementalAdditive().fit_transform(raa, sample_weight=raa.latest_diagonal)
def test_basic_transform(): tri = cl.load_sample("raa") cl.Development().fit_transform(tri) cl.ClarkLDF().fit_transform(tri) cl.TailClark().fit_transform(tri) cl.TailBondy().fit_transform(tri) cl.TailConstant().fit_transform(tri) cl.TailCurve().fit_transform(tri) cl.BootstrapODPSample().fit_transform(tri) cl.IncrementalAdditive().fit_transform(tri, sample_weight=tri.latest_diagonal)
def mack_p(data, average, est_sigma, tail): if tail: return cl.MackChainladder().fit( cl.TailCurve(curve='exponential').fit_transform( cl.Development(average=average, sigma_interpolation=est_sigma).fit_transform( cl.load_sample(data)))) else: return cl.MackChainladder().fit( cl.Development(average=average, sigma_interpolation=est_sigma).fit_transform( cl.load_sample(data)))
=========================== This example demonstrates how you can slice triangle objects to perform a typical 'Actual vs Expected' analysis. We will use Medical Malpractice payment patterns for the demo. """ import chainladder as cl # Load the data tri_1997 = cl.load_sample('clrd') tri_1997 = tri_1997.groupby('LOB').sum().loc['medmal']['CumPaidLoss'] # Create a triangle as of the previous valuation and build IBNR model tri_1996 = tri_1997[tri_1997.valuation < '1997'] model_1996 = cl.Chainladder().fit(cl.TailCurve().fit_transform(tri_1996)) # Slice the expected losses from the 1997 calendar period of the model ave = model_1996.full_triangle_.dev_to_val() ave = ave[ave.valuation == tri_1997.valuation_date].rename( 'columns', 'Expected') # Slice the actual losses from the 1997 calendar period for prior AYs ave['Actual'] = tri_1997.latest_diagonal[tri_1997.origin < '1997'] ave['Actual - Expected'] = ave['Actual'] - ave['Expected'] # Plotting ave.to_frame().T.plot(y='Actual - Expected', kind='bar', legend=False, grid=True).set(title='Calendar Period 1997 Performance',
def mack_p(data, average, est_sigma): return cl.TailCurve(curve='exponential').fit_transform(cl.Development(average=average, sigma_interpolation=est_sigma).fit_transform(cl.load_dataset(data)))
""" =============================== Attachment Age Smoothing =============================== This simple example demonstrates how a Tail ``attachment_age`` can be used to smooth over development patterns within the triangle. Regardless of where the ``attachment_age`` is set, the patterns will always extrapolate one year past the highest known lag of the `Triangle` before applying a terminal tail factor. """ import chainladder as cl import pandas as pd raa = cl.load_sample('raa') pd.concat( (cl.TailCurve().fit(raa).ldf_.T.iloc[:, 0].rename('Unsmoothed'), cl.TailCurve( attachment_age=12).fit(raa).ldf_.T.iloc[:, 0].rename('Curve Fit')), 1).plot(grid=True, title='Exponential Smoothing of LDF')
special case of the Benktander model where ``n_iters = 1`` for BornhuetterFerguson and as ``n_iters`` approaches infinity yields the chainladder. As ``n_iters`` increases the apriori selection becomes less relevant regardless of initial choice. """ import chainladder as cl # Load Data clrd = cl.load_sample('clrd') medmal_paid = clrd.groupby('LOB').sum().loc['medmal', 'CumPaidLoss'] medmal_prem = clrd.groupby('LOB').sum().loc['medmal', 'EarnedPremDIR'].latest_diagonal medmal_prem.rename('development', ['premium']) # Generate LDFs and Tail Factor medmal_paid = cl.Development().fit_transform(medmal_paid) medmal_paid = cl.TailCurve().fit_transform(medmal_paid) # Benktander Model benk = cl.Benktander() # Prep Benktander Grid Search with various assumptions, and a scoring function param_grid = dict(n_iters=list(range(1,100,2)), apriori=[0.50, 0.75, 1.00]) scoring = {'IBNR':lambda x: x.ibnr_.sum()} grid = cl.GridSearch(benk, param_grid, scoring=scoring) # Perform Grid Search grid.fit(medmal_paid, sample_weight=medmal_prem) # Plot data grid.results_.pivot(index='n_iters', columns='apriori', values='IBNR').plot( title='Benktander convergence to Chainladder', grid=True).set(ylabel='IBNR')
we can see that the "Inverse Power" curve fit doesn't converge to its asymptotic value. """ import chainladder as cl tri = cl.load_sample('clrd').groupby('LOB').sum().loc['medmal', 'CumPaidLoss'] # Create a fuction to grab the scalar tail value. def scoring(model): """ Scoring functions must return a scalar """ return model.tail_.iloc[0, 0] # Create a grid of scenarios param_grid = dict(extrap_periods=list(range(1, 100, 6)), curve=['inverse_power', 'exponential']) # Fit Grid model = cl.GridSearch(cl.TailCurve(), param_grid=param_grid, scoring=scoring).fit(tri) # Plot results model.results_.pivot( columns='curve', index='extrap_periods', values='score').plot( grid=True, ylim=(1, None), title='Curve Fit Sensitivity to Extrapolation Period').set( ylabel='Tail Factor')
""" ====================================== Tail Curve Fit Comparison ====================================== This example demonstrates how the ``inverse_power`` curve generally produces more conservative tail factors than the ``exponential`` fit. """ import chainladder as cl import pandas as pd clrd = cl.load_sample('clrd').groupby('LOB').sum()['CumPaidLoss'] cdf_ip = cl.TailCurve(curve='inverse_power').fit(clrd) cdf_xp = cl.TailCurve(curve='exponential').fit(clrd) pd.concat( (cdf_ip.tail_.rename("Inverse Power"), cdf_xp.tail_.rename("Exponential")), axis=1).plot(kind='bar', grid=True, title='Curve Fit Comparison').set(xlabel='Industry', ylabel='Tail Factor')