def test_mcl_ult(): mcl = cl.load_sample("mcl") dev = cl.Development().fit_transform(mcl) cl_traditional = cl.Chainladder().fit(dev).ultimate_ dev_munich = cl.MunichAdjustment( paid_to_incurred=[("paid", "incurred")]).fit_transform(dev) cl_munich = cl.Chainladder().fit(dev_munich).ultimate_
def test_voting_ultimate(): clrd = cl.load_sample("clrd")[["CumPaidLoss", "EarnedPremDIR"]] clrd = clrd[clrd["LOB"] == "wkcomp"] bcl_ult = cl.Chainladder().fit(clrd["CumPaidLoss"].sum(), ).ultimate_ bf_ult = cl.BornhuetterFerguson().fit( clrd["CumPaidLoss"].sum(), sample_weight=clrd["EarnedPremDIR"].sum().latest_diagonal).ultimate_ cc_ult = cl.CapeCod().fit( clrd["CumPaidLoss"].sum(), sample_weight=clrd["EarnedPremDIR"].sum().latest_diagonal).ultimate_ bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] weights = np.array([[0.25, 0.25, 0.5]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) vot_ult = cl.VotingChainladder(estimators=estimators, weights=weights).fit( clrd["CumPaidLoss"].sum(), sample_weight=clrd["EarnedPremDIR"].sum().latest_diagonal, ).ultimate_ weights = weights[..., np.newaxis] assert abs((bcl_ult * weights[..., 0, :] + bf_ult * weights[..., 1, :] + cc_ult * weights[..., 2, :]).sum() - vot_ult.sum()) < 1
def test_json_subtri(): assert ( cl.read_json( cl.Chainladder().fit_predict(cl.load_sample("raa")).to_json() ).full_triangle_ == cl.Chainladder().fit_predict(cl.load_sample("raa")).full_triangle_ )
def test_mcl_rollforward(): mcl = cl.load_sample("mcl") mcl_prior = mcl[mcl.valuation < mcl.valuation_date] munich = cl.MunichAdjustment( paid_to_incurred=[("paid", "incurred")]).fit(mcl_prior) new = munich.transform(mcl) cl.Chainladder().fit(new).ultimate_
def test_bf_eq_cl_when_using_cl_apriori(): cl_ult = cl.Chainladder().fit(cl.load_sample('quarterly')).ultimate_ cl_ult.rename('development', ['apriori']) bf_ult = cl.BornhuetterFerguson().fit(cl.load_sample('quarterly'), sample_weight=cl_ult).ultimate_ xp = cl_ult.get_array_module() assert xp.allclose(cl_ult.values, bf_ult.values, atol=1e-5)
def test_misaligned_index(prism): prism = prism['Paid'] model = cl.Chainladder().fit( cl.Development(groupby=['Line', 'Type']).fit_transform(prism)) a = model.ultimate_.loc[prism.index.iloc[:10]].sum().sum() b = model.predict(prism.iloc[:10]).ultimate_.sum().sum() assert abs(a - b) < 1e-5
def test_voting_ultimate(triangle_data, estimators, weights): bcl_ult = cl.Chainladder().fit( triangle_data["CumPaidLoss"].sum(), ).ultimate_ bf_ult = cl.BornhuetterFerguson().fit( triangle_data["CumPaidLoss"].sum(), sample_weight=triangle_data["EarnedPremDIR"].sum( ).latest_diagonal).ultimate_ cc_ult = cl.CapeCod().fit(triangle_data["CumPaidLoss"].sum(), sample_weight=triangle_data["EarnedPremDIR"].sum( ).latest_diagonal).ultimate_ vot_ult = cl.VotingChainladder( estimators=estimators, weights=weights, default_weighting=(1, 2, 3)).fit( triangle_data["CumPaidLoss"].sum(), sample_weight=triangle_data["EarnedPremDIR"].sum().latest_diagonal, ).ultimate_ direct_weight = np.array([[1, 2, 3]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) direct_weight = direct_weight[..., np.newaxis] assert abs(( (bcl_ult * direct_weight[..., 0, :] + bf_ult * direct_weight[..., 1, :] + cc_ult * direct_weight[..., 2, :]) / direct_weight.sum(axis=-2)).sum() - vot_ult.sum()) < 1
def test_weight_broadcasting(): clrd = cl.load_sample("clrd")[["CumPaidLoss", "EarnedPremDIR"]] clrd = clrd[clrd["LOB"] == "wkcomp"] bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] min_dim_weights = np.array([[1, 2, 3]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) mid_dim_weights = np.array( [[[1, 2, 3]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3] * 1) max_dim_weights = np.array( [[[[1, 2, 3]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3] * 1] * 132) min_dim_ult = cl.VotingChainladder( estimators=estimators, weights=min_dim_weights).fit( clrd['CumPaidLoss'], sample_weight=clrd["EarnedPremDIR"].latest_diagonal, ).ultimate_.sum() mid_dim_ult = cl.VotingChainladder( estimators=estimators, weights=mid_dim_weights).fit( clrd['CumPaidLoss'], sample_weight=clrd["EarnedPremDIR"].latest_diagonal, ).ultimate_.sum() max_dim_ult = cl.VotingChainladder( estimators=estimators, weights=max_dim_weights).fit( clrd['CumPaidLoss'], sample_weight=clrd["EarnedPremDIR"].latest_diagonal, ).ultimate_.sum() assert (abs(min_dim_ult - mid_dim_ult - max_dim_ult) < 1)
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_benktander_to_chainladder(data, atol): tri = cl.load_sample(data) a = cl.Chainladder().fit(tri).ibnr_ b = cl.Benktander(apriori=.8, n_iters=255).fit(tri, sample_weight=a).ibnr_ xp = tri.get_array_module() assert xp.allclose(xp.nan_to_num(a.values), xp.nan_to_num(b.values), atol=atol)
def test_basic_case_outstanding(): tri = cl.load_sample('usauto') m = cl.CaseOutstanding(paid_to_incurred=('paid', 'incurred')).fit(tri) out = cl.Chainladder().fit(m.fit_transform(tri)) a = (out.full_triangle_['incurred'] - out.full_triangle_['paid']).iloc[..., -1, :9] * m.paid_ldf_.values b = (out.full_triangle_['paid'].cum_to_incr().iloc[..., -1, 1:10]).values assert (a - b).max() < 1e-6
def estimators(): bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] return estimators
def test_misaligned_index2(clrd): clrd = clrd['CumPaidLoss'] w = cl.load_sample('clrd')['EarnedPremDIR'].latest_diagonal bcl = cl.Chainladder().fit( cl.Development(groupby=['LOB']).fit_transform(clrd)) bbk = cl.Benktander().fit( cl.Development(groupby=['LOB']).fit_transform(clrd), sample_weight=w) bcc = cl.CapeCod().fit(cl.Development(groupby=['LOB']).fit_transform(clrd), sample_weight=w) a = bcl.ultimate_.iloc[:10].sum().sum() b = bcl.predict(clrd.iloc[:10]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bbk.ultimate_.iloc[:10].sum().sum() b = bbk.predict(clrd.iloc[:10], sample_weight=w.iloc[:10]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcc.ultimate_.iloc[:10].sum().sum() b = bcc.predict(clrd.iloc[:10], sample_weight=w.iloc[:10]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcl.ultimate_.iloc[150:153].sum().sum() b = bcl.predict(clrd.iloc[150:153]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bbk.ultimate_.iloc[150:153].sum().sum() b = bbk.predict(clrd.iloc[150:153], sample_weight=w.iloc[150:153]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcc.ultimate_.iloc[150:153].sum().sum() b = bcc.predict(clrd.iloc[150:153], sample_weight=w.iloc[150:153]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcl.ultimate_.iloc[150:152].sum().sum() b = bcl.predict(clrd.iloc[150:152]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bbk.ultimate_.iloc[150:152].sum().sum() b = bbk.predict(clrd.iloc[150:152], sample_weight=w.iloc[150:152]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcc.ultimate_.iloc[150:152].sum().sum() b = bcc.predict(clrd.iloc[150:152], sample_weight=w.iloc[150:152]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcl.ultimate_.iloc[150].sum().sum() b = bcl.predict(clrd.iloc[150]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bbk.ultimate_.iloc[150].sum().sum() b = bbk.predict(clrd.iloc[150], sample_weight=w.iloc[150]).ultimate_.sum().sum() assert abs(a - b) < 1e-5 a = bcc.ultimate_.iloc[150].sum().sum() b = bcc.predict(clrd.iloc[150], sample_weight=w.iloc[150]).ultimate_.sum().sum() assert abs(a - b) < 1e-5
def test_commutative(): tri = cl.load_dataset('quarterly') xp = cp.get_array_module(tri.values) full = cl.Chainladder().fit(tri).full_expectation_ assert tri.grain('OYDY').val_to_dev() == tri.val_to_dev().grain('OYDY') assert tri.cum_to_incr().grain('OYDY').val_to_dev() == tri.val_to_dev().cum_to_incr().grain('OYDY') assert tri.grain('OYDY').cum_to_incr().val_to_dev().incr_to_cum() == tri.val_to_dev().grain('OYDY') assert full.grain('OYDY').val_to_dev() == full.val_to_dev().grain('OYDY') assert full.cum_to_incr().grain('OYDY').val_to_dev() == full.val_to_dev().cum_to_incr().grain('OYDY') assert xp.allclose(xp.nan_to_num(full.grain('OYDY').cum_to_incr().val_to_dev().incr_to_cum().values), xp.nan_to_num(full.val_to_dev().grain('OYDY').values), atol=1e-5)
def get_triangle_projections(triangles, average_methods=None, n_periods=None, grain='OYDY'): """ Generates the main kpis such as ultimate loss, ibnr, loss development factors Arguments --> A dictionnary of triangles or a single triangle, the methods to derive the LDF (simple or volume average) defined as a list if there are several ultimate triangles to produce, the number of periods to look at (-1 means all periods by default) the origin/development pattern ('OxDy' with x and y in (Y, M, Q)) Returns --> a dictionnary storing the triangles and other kpis the dict keys are 'ldf' for loss development factors, 'cdf' for the cumulative ones, 'fit' to get the fitted model and 'full_triangle' to get the full triangle produced """ triangles_values = triangles.values() if isinstance(triangles, dict) else [triangles] triangles_keys = triangles.keys() if isinstance(triangles, dict) else [1] selected_average_methods = ['volume'] * len(triangles_keys) if average_methods is None else \ average_methods if isinstance(average_methods, list) else [average_methods] selected_n_periods = [-1] * len(triangles_keys) if n_periods is None else \ n_periods if isinstance(n_periods, list) else [n_periods] # Gets the different types of figures we are studying (asif cost, cost excl LL, count, etc.) triangles_names = [triangle.columns[0] for triangle in triangles_values] # Builds the triangle transformer with development attributes ; loops through the triangles triangles_dev = [ cl.Pipeline([('dev', cl.Development(average=selected_average_methods[index], n_periods=selected_n_periods[index])) ]).fit_transform(triangle.grain(grain)) for index, triangle in enumerate(triangles_values) ] # Loops through the triangles_dev to derive the ldfs, cdfs and the fit method triangles_model = [(triangle_dev.ldf_, triangle_dev.cdf_, cl.Chainladder().fit(triangle_dev)) for triangle_dev in triangles_dev] # Loops through the triangles_model to build a dict with the name of the figures (claims cost, count, etc.) # as primary key and the main triangle characteristics as second keys return {value: { 'ldf': triangles_model[index][0], 'cdf': triangles_model[index][1], 'fit': triangles_model[index][2], 'full_triangle': pd.concat([triangles_model[index][2].full_triangle_.to_frame(), triangles_model[index][2].ibnr_.to_frame()] \ , axis=1).rename(columns={9999: 'Ultimates', value: 'IBNR'}) } for index, value in enumerate(triangles_names)}
def test_voting_predict(): bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] weights = np.array([[1, 2, 3]] * 3 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) vot = cl.VotingChainladder(estimators=estimators, weights=weights).fit( raa_1989, sample_weight=apriori_1989, ) vot.predict(raa_1990, sample_weight=apriori_1990)
def test_commutative(qtr, atol): xp = qtr.get_array_module() full = cl.Chainladder().fit(qtr).full_expectation_ assert qtr.grain("OYDY").val_to_dev() == qtr.val_to_dev().grain("OYDY") assert qtr.cum_to_incr().grain( "OYDY").val_to_dev() == qtr.val_to_dev().cum_to_incr().grain("OYDY") assert qtr.grain("OYDY").cum_to_incr().val_to_dev().incr_to_cum( ) == qtr.val_to_dev().grain("OYDY") assert full.grain("OYDY").val_to_dev() == full.val_to_dev().grain("OYDY") assert full.cum_to_incr().grain( "OYDY").val_to_dev() == full.val_to_dev().cum_to_incr().grain("OYDY") a = full.grain("OYDY").cum_to_incr().val_to_dev().incr_to_cum() b = full.val_to_dev().grain("OYDY") assert abs(a - b).max().max().max() < atol
def test_different_backends(): clrd = cl.load_sample("clrd")[["CumPaidLoss", "EarnedPremDIR"]] clrd = clrd[clrd["LOB"] == "wkcomp"] bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] weights = np.array([[1, 2, 3]] * 4 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) model = cl.VotingChainladder(estimators=estimators, weights=weights).fit( clrd["CumPaidLoss"].sum().set_backend("numpy"), sample_weight=clrd["EarnedPremDIR"].sum().latest_diagonal.set_backend( "numpy"), ) assert (abs( (model.predict(clrd["CumPaidLoss"].sum().set_backend("sparse"), sample_weight=clrd["EarnedPremDIR"].sum(). latest_diagonal.set_backend("sparse")).ultimate_.sum() - model.ultimate_.sum())) < 1)
def test_commutative(): assert qtr == qtr_gt xp = qtr.get_array_module() full = cl.Chainladder().fit(qtr).full_expectation_ assert qtr.grain("OYDY").val_to_dev() == qtr.val_to_dev().grain("OYDY") assert qtr.cum_to_incr().grain( "OYDY" ).val_to_dev() == qtr.val_to_dev().cum_to_incr().grain("OYDY") assert qtr.grain( "OYDY" ).cum_to_incr().val_to_dev().incr_to_cum() == qtr.val_to_dev().grain("OYDY") assert full.grain("OYDY").val_to_dev() == full.val_to_dev().grain("OYDY") assert full.cum_to_incr().grain( "OYDY" ).val_to_dev() == full.val_to_dev().cum_to_incr().grain("OYDY") assert xp.allclose( xp.nan_to_num( full.grain("OYDY").cum_to_incr().val_to_dev().incr_to_cum().values ), xp.nan_to_num(full.val_to_dev().grain("OYDY").values), atol=1e-5, )
""" ====================== Value at Risk example ====================== This example uses the `BootstrapODPSample` to simulate new triangles that are then used to simulate an IBNR distribution from which we can do Value at Risk percentile lookups. """ import chainladder as cl import seaborn as sns sns.set_style('whitegrid') # Load triangle triangle = cl.load_dataset('genins') # Create 1000 bootstrap samples of the triangle resampled_triangles = cl.BootstrapODPSample().fit_transform(triangle) # Create 1000 IBNR estimates sim_ibnr = cl.Chainladder().fit(resampled_triangles).ibnr_.sum('origin') # X - mu sim_ibnr = (sim_ibnr - sim_ibnr.mean()).to_frame().sort_values() # Plot data sim_ibnr.index = [item / 1000 for item in range(1000)] sim_ibnr.loc[0.90:].plot(title='Bootstrap VaR (90% and above)', color='red').set(xlabel='VaR')
=========================== 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 test_dask_backend(raa): raa1 = cl.Chainladder().fit(raa.set_backend('dask')).ultimate_ raa2 = cl.Chainladder().fit(raa).ultimate_ assert (raa1 == raa2).compute()
def test_basic_odp_cl(genins): assert abs( (cl.Chainladder().fit(genins).ultimate_ - cl.Chainladder().fit(cl.TweedieGLM().fit_transform(genins)).ultimate_) / genins.latest_diagonal).max()< 1e-2
def test_create_full_triangle(raa): a = cl.Chainladder().fit(raa).full_triangle_ b = cl.Triangle( a.to_frame(keepdims=True, implicit_axis=True), origin='origin', development='valuation', columns='values') assert a == b
IBNR Runoff ============ All IBNR models spin off several results triangles including `inbr_`, `ultimate_`, `full_expectation`, and `full_triangle_`. These can be manipulated into a variety of formats. This example demonstrates how to create a calendar year runoff of IBNR. """ import chainladder as cl # Create a triangle triangle = cl.load_sample('GenIns') # Fit a model model = cl.Chainladder().fit(triangle) # Develop IBNR runoff triangle runoff = (model.full_triangle_.cum_to_incr() - triangle.cum_to_incr()) # Convert to calendar period and aggregate across all accident years cal_yr_runoff = runoff.dev_to_val().dropna().sum(axis='origin') # Plot results cal_yr_runoff.T.plot(kind='bar', legend=False, color='red', grid=True, title='GenIns: IBNR Run-off', alpha=0.7).set(xlabel='Calendar Year', ylabel='IBNR')
def test_valdev7(): assert qtr == qtr_gt xp = qtr.get_array_module() x = cl.Chainladder().fit(qtr).full_expectation_ assert xp.sum(x.dev_to_val().val_to_dev().values - x.values) < 1e-5
def test_bf_eq_cl_when_using_cl_apriori(): cl_ult = cl.Chainladder().fit(cl.load_dataset('quarterly')).ultimate_ cl_ult.rename('development', ['apriori']) bf_ult = cl.BornhuetterFerguson().fit(cl.load_dataset('quarterly'), sample_weight=cl_ult).ultimate_ assert_allclose(cl_ult.triangle, bf_ult.triangle, atol=1e-5)
def test_benktander_to_chainladder(data, atol): tri = cl.load_dataset(data) a = cl.Chainladder().fit(tri).ibnr_ b = cl.Benktander(apriori=.8, n_iters=255).fit(tri, sample_weight=a).ibnr_ assert_allclose(a.triangle, b.triangle, atol=atol)
import numpy as np import chainladder as cl raa = cl.load_sample("RAA") raa_1989 = raa[raa.valuation < raa.valuation_date] raa_1990 = raa[raa.origin > "1981"] raa_1990 = raa_1990[raa_1990.valuation <= raa_1990.valuation_date] cl_ult = cl.Chainladder().fit(raa).ultimate_ # Chainladder Ultimate apriori = cl_ult * 0 + (float(cl_ult.sum()) / 10) # Mean Chainladder Ultimate apriori_1989 = apriori[apriori.origin < "1990"] apriori_1990 = apriori[apriori.origin > "1981"] def test_voting_predict(): bcl = cl.Chainladder() bf = cl.BornhuetterFerguson() cc = cl.CapeCod() estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] weights = np.array([[1, 2, 3]] * 3 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) vot = cl.VotingChainladder(estimators=estimators, weights=weights).fit( raa_1989, sample_weight=apriori_1989, ) vot.predict(raa_1990, sample_weight=apriori_1990)
using the ``reported_count_estimator``. The disposal rates of the latest diagonal are then used to infer adjustments to the inner diagonals of both the closed claim triangle as well as the paid amount triangle. """ import chainladder as cl import matplotlib.pyplot as plt # Load data triangle = cl.load_sample('berqsherm').loc['Auto'] # Specify Berquist-Sherman model berq = cl.BerquistSherman( paid_amount='Paid', incurred_amount='Incurred', reported_count='Reported', closed_count='Closed', reported_count_estimator=cl.Chainladder()) # Adjust our triangle data berq_triangle = berq.fit_transform(triangle) berq_cdf = cl.Development().fit(berq_triangle).cdf_ orig_cdf = cl.Development().fit(triangle).cdf_ # Plot data fig, ((ax0, ax1)) = plt.subplots(ncols=2, figsize=(15,5)) (berq_cdf['Paid'] / orig_cdf['Paid']).T.plot( kind='bar', grid=True, legend=False, ax=ax0, title='Berquist Sherman Paid to Unadjusted Paid').set( xlabel='Age to Ultimate', ylabel='Paid CDF Adjustment'); (berq_cdf['Closed'] / orig_cdf['Closed']).T.plot( kind='bar', grid=True, legend=False, ax=ax1,