diff --git a/chainladder/__init__.py b/chainladder/__init__.py index 960e26c2..c14cb3aa 100644 --- a/chainladder/__init__.py +++ b/chainladder/__init__.py @@ -11,4 +11,4 @@ def array_backend(array_backend='numpy'): from chainladder.methods import * # noqa (API Import) from chainladder.workflow import * # noqa (API Import) -__version__ = '0.6.3' +__version__ = '0.7.0' diff --git a/chainladder/tails/base.py b/chainladder/tails/base.py index 40d964cd..c44f86b4 100644 --- a/chainladder/tails/base.py +++ b/chainladder/tails/base.py @@ -116,10 +116,13 @@ def _get_initial_ldf(self, xp, tail): c = -xp.log(tail) return (-b+xp.sqrt(b**2-4*a*c))/(2*a) - def _apply_decay(self, X, tail): - ''' Created Tail vector with decay over time ''' + def _apply_decay(self, X, tail, attach_idx=None): + ''' Created Tail vector with decay over time. ''' xp = cp.get_array_module(X.values) - decay_range = self.ldf_.shape[-1]-X.shape[-1]+1 + if attach_idx: + decay_range = self.ldf_.shape[-1] - attach_idx + else: + decay_range = self.ldf_.shape[-1]-X.shape[-1]+1 if xp.max(tail) == 1.0: ldfs = 1 + 0*(self.decay**xp.arange(1000)) else: @@ -127,7 +130,7 @@ def _apply_decay(self, X, tail): ldfs = ldfs[..., :decay_range] ldfs[..., -1:] = tail/xp.prod(ldfs[..., :-1], axis=-1, keepdims=True) self.ldf_.values[..., -decay_range:] = \ - self.ldf_.values[..., -decay_range:]*ldfs + (self.ldf_.values[..., -decay_range:]*0+1)*ldfs self.cdf_ = DevelopmentBase._get_cdf(self) return self @@ -165,11 +168,9 @@ def _get_tail_weighted_time_period(self, X): def _tail_(self): df = self.cdf_[self.cdf_.development== self.cdf_.development.iloc[-1-self._ave_period[0]]] - if np.all(df.values.min(axis=-1) == df.values.max(axis=-1)): + if np.all(df.values.min(axis=2) == df.values.max(axis=2)): idx = self.cdf_._idx_table() - df = df.T.drop_duplicates().T - df.index = idx.index - df.columns = idx.columns + df = df[df.origin==df.origin.min()].to_frame() return df @property diff --git a/chainladder/tails/bondy.py b/chainladder/tails/bondy.py index fedc6c97..65156d42 100644 --- a/chainladder/tails/bondy.py +++ b/chainladder/tails/bondy.py @@ -3,6 +3,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. import numpy as np import pandas as pd +import copy from scipy.optimize import least_squares from chainladder.utils.cupy import cp from chainladder.tails import TailBase @@ -19,15 +20,23 @@ class TailBondy(TailBase): The earliest age from which the Bondy exponent is to be calculated. Defaults to earliest available in the Triangle. Any available development age can be used. + attachment_age: int (default=None) + The age at which to attach the fitted curve. If None, then the latest + age is used. Measures of variability from original `ldf_` are retained + when being used in conjunction with the MackChainladder method. Attributes ---------- - b_ : DataFrame - The Bondy exponent ldf_ : Triangle ldf with tail applied. cdf_ : Triangle cdf with tail applied. + tail_ : DataFrame + Point estimate of tail at latest maturity available in the Triangle. + b_ : DataFrame + The Bondy exponent + earliest_ldf_ : DataFrame + The LDF associated with the ``earliest_age`` pick. sigma_ : Triangle sigma with tail factor applied. std_err_ : Triangle @@ -41,8 +50,9 @@ class TailBondy(TailBase): TailCurve """ - def __init__(self, earliest_age=None): + def __init__(self, earliest_age=None, attachment_age=None): self.earliest_age = earliest_age + self.attachment_age = attachment_age def fit(self, X, y=None, sample_weight=None): """Fit the model with X. @@ -59,44 +69,57 @@ def fit(self, X, y=None, sample_weight=None): self : object Returns the instance itself. """ + if self.attachment_age and self.attachment_age < self.earliest_age: + raise ValueError('attachment_age must not be before earliest_age.') super().fit(X, y, sample_weight) xp = cp.get_array_module(X.values) - if self.earliest_age is None: earliest_age = X.ddims[0] else: earliest_age = X.ddims[int( self.earliest_age / ({'Y': 12,'Q': 3,'M': 1}[X.development_grain])) - 1] + attachment_age = self.attachment_age if self.attachment_age else X.ddims[-2] obj = Development().fit_transform(X) if 'ldf_' not in X else X b_optimized = [] initial = xp.where(obj.ddims==earliest_age)[0][0] if earliest_age else 0 for num in range(len(obj.vdims)): - b0 = xp.ones(obj.shape[0])*.5 - data = xp.log(obj.ldf_.values[:, num, 0,initial:]) + b0 = (xp.ones(obj.shape[0])*.5)[:, None] + data = xp.log(obj.ldf_.values[:, num, 0, initial:]) + b0 = xp.concatenate((b0, data[..., 0:1]), axis=1) b_optimized.append(least_squares( - TailBondy.solver, x0=b0, kwargs={'data': data}).x[:, None]) - self.b_ = xp.concatenate(b_optimized, axis=1)[..., None, None] - tail = xp.exp(xp.log(obj.ldf_.values[..., 0:1, initial:initial+1]) * \ - self.b_**(len(obj.ldf_.ddims)-1)) + TailBondy._solver, x0=b0.flatten(), kwargs={'data': data}).x) + self.b_ = xp.concatenate( + [item.reshape(-1,2)[:, 0:1] + for item in b_optimized], axis=1)[..., None, None] + self.earliest_ldf_ = xp.exp(xp.concatenate( + [item.reshape(-1,2)[:, 1:2] + for item in b_optimized], axis=1)[..., None, None]) + if sum(X.ddims>earliest_age) > 1: + tail = xp.exp(self.earliest_ldf_ * self.b_**(len(obj.ldf_.ddims)-1)) + else: + tail = self.ldf_.values[..., 0, initial] tail = (tail**(self.b_/(1-self.b_))) - self.decay = self.b_ - self = self._apply_decay(obj, tail) - sigma, std_err = self._get_tail_stats(obj) - self.sigma_.values[..., -1] = sigma[..., -1] - self.std_err_.values[..., -1] = std_err[..., -1] + f0 = self.ldf_.values[..., 0:1, initial:initial+1] + fitted = (f0**(self.b_**(np.arange(sum(X.ddims>=earliest_age))[None, None, None, :]))) + fitted = xp.concatenate((fitted, fitted[..., -1:]**(self.b_/(1-self.b_))), axis=-1) + fitted = xp.repeat(fitted, self.ldf_.shape[2], axis=2) idx = X._idx_table() self.b_ = pd.DataFrame( self.b_[..., 0, 0], index=idx.index, columns=idx.columns) self.earliest_ldf_ = pd.DataFrame( - self.ldf_.values[..., 0, initial], index=idx.index, columns=idx.columns) + self.earliest_ldf_[..., 0, 0], index=idx.index, columns=idx.columns) + self.ldf_.values = xp.concatenate( + (self.ldf_.values[..., :sum(X.ddims<=attachment_age)], + fitted[..., -sum(X.ddims>=attachment_age):]), + axis=-1) + self.cdf_ = DevelopmentBase._get_cdf(self) + sigma, std_err = self._get_tail_stats(obj) + self.sigma_.values[..., -1] = sigma[..., -1] + self.std_err_.values[..., -1] = std_err[..., -1] return self def transform(self, X): """Transform X. - The predicted class of an input sample is a vote by the trees in - the forest, weighted by their probability estimates. That is, - the predicted class is the one with highest mean probability - estimate across the trees. Parameters ---------- @@ -109,16 +132,15 @@ def transform(self, X): New Triangle with tail factor applied to its development attributes. """ - X.std_err_ = self.std_err_ - X.cdf_ = self.cdf_ - X.ldf_ = self.ldf_ - X.sigma_ = self.sigma_ - X.b_ = self.b_ - return X + X_new = super().transform(X) + X_new.b_ = self.b_ + X_new.earliest_ldf_ = self.earliest_ldf_ + return X_new @staticmethod - def solver(b, data): + def _solver(b, data): + b = b.reshape(-1, 2) xp = cp.get_array_module(data) arange = xp.repeat(xp.arange(data.shape[-1])[None, :], data.shape[0], 0) - out = data - (data[:, 0])[:, None]*b**(arange) + out = data - (b[:, 1:2])*b[:, 0:1]**(arange) return out.flatten() diff --git a/chainladder/tails/clark.py b/chainladder/tails/clark.py index 64d089ef..72be118c 100644 --- a/chainladder/tails/clark.py +++ b/chainladder/tails/clark.py @@ -18,6 +18,10 @@ class TailClark(TailBase): growth : {'loglogistic', 'weibull'} The growth function to be used in curve fitting development patterns. Options are 'loglogistic' and 'weibull' + attachment_age: int (default=None) + The age at which to attach the fitted curve. If None, then the latest + age is used. Measures of variability from original `ldf_` are retained + when being used in conjunction with the MackChainladder method. Attributes ---------- @@ -25,10 +29,23 @@ class TailClark(TailBase): ldf with tail applied. cdf_ : cdf with tail applied. - + tail_ : DataFrame + Point estimate of tail at latest maturity available in the Triangle. + theta_ : DataFrame + Estimates of the theta parameter of the growth curve. + omega_ : DataFrame + Estimates of the omega parameter of the growth curve. + elr_ : DataFrame + The Expected Loss Ratio parameter. This only exists when a `sample_weight` + is provided to the Estimator. + scale_ : DataFrame + The scale parameter of the model. + norm_resid_ : Triangle + The "Normalized" Residuals of the model according to Clark. """ - def __init__(self, growth='loglogistic'): + def __init__(self, growth='loglogistic', attachment_age=None): self.growth = growth + self.attachment_age = attachment_age def fit(self, X, y=None, sample_weight=None): """Fit the model with X. @@ -50,22 +67,35 @@ def fit(self, X, y=None, sample_weight=None): model = ClarkLDF(growth=self.growth).fit(X, sample_weight=sample_weight) xp = cp.get_array_module(X.values) age_offset = {'Y':6., 'Q':1.5, 'M':0.5}[X.development_grain] - tail = 1/model.G_(xp.array( - [item*self._ave_period[1]+X.ddims[-1]-age_offset - for item in range(self._ave_period[0]+1)])) - tail = xp.concatenate((tail.values[..., :-1]/tail.values[..., -1], - tail.values[..., -1:]), -1) + fitted = 1/model.G_(xp.array( + [self._ave_period[1]+X.ddims-age_offset + for item in range(self._ave_period[0]+2)])[0]) + fitted = xp.concatenate( + (fitted.values[..., :-1]/fitted.values[..., -1:], + fitted.values[..., -1:]), -1) + fitted = xp.repeat(fitted, self.ldf_.values.shape[2], 2) + attachment_age = self.attachment_age if self.attachment_age else X.ddims[-2] + print(self.ldf_.values[..., :sum(X.ddims<=attachment_age)].shape, fitted[..., -sum(X.ddims>=attachment_age):].shape) self.ldf_.values = xp.concatenate( - (X.ldf_.values, xp.repeat(tail, X.shape[2], 2)), -1) + (self.ldf_.values[..., :sum(X.ddims<=attachment_age)], + fitted[..., -sum(X.ddims>=attachment_age):]), + axis=-1) + + self.cdf_ = DevelopmentBase._get_cdf(self) + self.omega_ = model.omega_ + self.theta_ = model.theta_ + self.G_ = model.G_ + self.scale_ = model.scale_ + self._G = model._G + self.incremental_fits_ = model.incremental_fits_ + if hasattr(model, 'elr_'): + self.elr_ = model.elr_ + self.norm_resid_ = model.norm_resid_ return self def transform(self, X): """Transform X. - The predicted class of an input sample is a vote by the trees in - the forest, weighted by their probability estimates. That is, - the predicted class is the one with highest mean probability - estimate across the trees. Parameters ---------- @@ -78,7 +108,12 @@ def transform(self, X): New Triangle with tail factor applied to its development attributes. """ - - X.cdf_ = self.cdf_ - X.ldf_ = self.ldf_ - return X + X_new = super().transform(X) + triangles = ['omega_', 'theta_', 'incremental_fits_', 'G_', '_G', + 'growth', 'scale_'] + for item in triangles: + setattr(X_new, item, getattr(self, item)) + X_new._set_slicers() + if hasattr(self, 'elr_'): + X_new.elr_ = self.elr_ + return X_new diff --git a/chainladder/tails/constant.py b/chainladder/tails/constant.py index a5f5652d..2919d2b3 100644 --- a/chainladder/tails/constant.py +++ b/chainladder/tails/constant.py @@ -5,6 +5,7 @@ from chainladder.utils.cupy import cp from chainladder.tails import TailBase from chainladder.development import DevelopmentBase, Development +import copy class TailConstant(TailBase): """Allows for the entry of a constant tail factor to LDFs. @@ -17,6 +18,10 @@ class TailConstant(TailBase): An exponential decay constant that allows for decay over future development periods. A decay rate of 0.5 sets the development portion of each successive LDF to 50% of the previous LDF. + attachment_age: int (default=None) + The age at which to attach the fitted curve. If None, then the latest + age is used. Measures of variability from original `ldf_` are retained + when being used in conjunction with the MackChainladder method. Attributes ---------- @@ -24,6 +29,8 @@ class TailConstant(TailBase): ldf with tail applied. cdf_ : cdf with tail applied. + tail_ : DataFrame + Point estimate of tail at latest maturity available in the Triangle. sigma_ : sigma with tail factor applied. std_err_ : @@ -40,9 +47,10 @@ class TailConstant(TailBase): TailCurve """ - def __init__(self, tail=1.0, decay=0.5): + def __init__(self, tail=1.0, decay=0.5, attachment_age=None): self.tail = tail self.decay = decay + self.attachment_age = attachment_age def fit(self, X, y=None, sample_weight=None): """Fit the model with X. @@ -60,8 +68,13 @@ def fit(self, X, y=None, sample_weight=None): Returns the instance itself. """ super().fit(X, y, sample_weight) + xp = cp.get_array_module(X.values) tail = self.tail - self = self._apply_decay(X, tail) + if self.attachment_age: + attach_idx = xp.min(xp.where(X.ddims>=self.attachment_age)) + else: + attach_idx = len(X.ddims) - 1 + self = self._apply_decay(X, tail, attach_idx) obj = Development().fit_transform(X) if 'ldf_' not in X else X xp = cp.get_array_module(X.values) if xp.max(self.tail) != 1.0: @@ -69,27 +82,3 @@ def fit(self, X, y=None, sample_weight=None): self.sigma_.values[..., -1] = sigma[..., -1] self.std_err_.values[..., -1] = std_err[..., -1] return self - - def transform(self, X): - """Transform X. - The predicted class of an input sample is a vote by the trees in - the forest, weighted by their probability estimates. That is, - the predicted class is the one with highest mean probability - estimate across the trees. - - Parameters - ---------- - X : Triangle - Triangle must contain the ``ldf_`` development attribute. - - Returns - ------- - X_new : Triangle - New Triangle with tail factor applied to its development - attributes. - """ - X.std_err_ = self.std_err_ - X.cdf_ = self.cdf_ - X.ldf_ = self.ldf_ - X.sigma_ = self.sigma_ - return X diff --git a/chainladder/tails/curve.py b/chainladder/tails/curve.py index 702f85c5..71f7dbaa 100644 --- a/chainladder/tails/curve.py +++ b/chainladder/tails/curve.py @@ -5,7 +5,10 @@ from chainladder.utils import WeightedRegression from chainladder.development import DevelopmentBase, Development import numpy as np +import pandas as pd from chainladder.utils.cupy import cp +import copy +import warnings class TailCurve(TailBase): @@ -15,9 +18,10 @@ class TailCurve(TailBase): ---------- curve : str ('exponential', 'inverse_power') The type of curve extrapolation you'd like to use - fit_period : slice - A slice object representing the range (by index) of ldfs to use in - the curve fit. + fit_period : tuple (start, stop) + A tuple representing the range of ldfs to use in the curve fit. + The use of ``None`` will use the edge of the triangle. For example, + (48, None) will use development factors for age 48 and beyond. extrap_periods : int Then number of development periods from attachment point to extrapolate the fit. @@ -32,16 +36,22 @@ class TailCurve(TailBase): Attributes ---------- - ldf_ : + ldf_ : Triangle ldf with tail applied. - cdf_ : + cdf_ : Triangle cdf with tail applied. - sigma_ : + tail_ : DataFrame + Point estimate of tail at latest maturity available in the Triangle. + slope_ : DataFrame + Slope parameter of the curve fit. + intercept : DataFrame + Intercept parameter of the curve fit. + sigma_ : Triangle sigma with tail factor applied. - std_err_ : + std_err_ : Triangle std_err with tail factor applied """ - def __init__(self, curve='exponential', fit_period=slice(None, None, None), + def __init__(self, curve='exponential', fit_period=(None, None), extrap_periods=100, errors='ignore', attachment_age=None): self.curve = curve self.fit_period = fit_period @@ -64,31 +74,36 @@ def fit(self, X, y=None, sample_weight=None): self : object Returns the instance itself. """ + + if type(self.fit_period) == slice: + warnings.warn("Slicing for fit_period is deprecated and will be removed. Please use a tuple (start_age, end_age).") + fit_period = self.fit_period + else: + grain = {'Y': 12, 'Q': 3, 'M': 1}[X.development_grain] + start = self.fit_period[0] if self.fit_period[0] is None else int(self.fit_period[0] / grain - 1) + end = self.fit_period[1] if self.fit_period[1] is None else int(self.fit_period[1] / grain - 1) + fit_period = slice(start, end, None) super().fit(X, y, sample_weight) xp = cp.get_array_module(self.ldf_.values) _y = self.ldf_.values[..., :X.shape[-1]-1].copy() _w = xp.zeros(_y.shape) - if type(self.fit_period) is not slice: - raise TypeError('fit_period must be slice.') - else: - _w[..., self.fit_period] = 1.0 + _w[..., fit_period] = 1.0 if self.errors == 'ignore': _w[_y <= 1.0] = 0 _y[_y <= 1.0] = 1.01 elif self.errors == 'raise' and xp.any(y < 1.0): - raise ZeroDivisionError('Tail fit requires all LDFs to be' + - ' greater than 1.0') + raise ZeroDivisionError('Tail fit requires all LDFs to be greater than 1.0') _y = xp.log(_y - 1) n_obs = X.shape[-1]-1 k, v = X.shape[:2] _x = self._get_x(_w, _y) # Get LDFs coefs = WeightedRegression(axis=3).fit(_x, _y, _w) - slope, intercept = coefs.slope_, coefs.intercept_ + self._slope_, self._intercept_ = coefs.slope_, coefs.intercept_ extrapolate = xp.cumsum( xp.ones(tuple(list(_y.shape)[:-1] + [self.extrap_periods + n_obs])), -1) - tail = self._predict_tail(slope, intercept, extrapolate) + tail = self._predict_tail(extrapolate) if self.attachment_age: attach_idx = xp.min(xp.where(X.ddims>=self.attachment_age)) else: @@ -99,8 +114,6 @@ def fit(self, X, y=None, sample_weight=None): sigma, std_err = self._get_tail_stats(obj) self.sigma_.values[..., -1] = sigma[..., -1] self.std_err_.values[..., -1] = std_err[..., -1] - self.slope_ = slope - self.intercept_ = intercept self.cdf_ = DevelopmentBase._get_cdf(self) return self @@ -113,10 +126,24 @@ def _get_x(self, w, y): xp = cp.get_array_module(reg.x) return xp.log(reg.x) - def _predict_tail(self, slope, intercept, extrapolate): + def _predict_tail(self, extrapolate): xp = cp.get_array_module(extrapolate) if self.curve == 'exponential': - tail_ldf = xp.exp(slope*extrapolate + intercept) + tail_ldf = xp.exp(self._slope_*extrapolate + self._intercept_) if self.curve == 'inverse_power': - tail_ldf = xp.exp(intercept)*(extrapolate**slope) + tail_ldf = xp.exp(self._intercept_)*(extrapolate**self._slope_) return self._get_tail_prediction(tail_ldf) + + @property + def slope_(self): + """ Does not work with munich """ + idx = self.cdf_._idx_table() + return pd.DataFrame(self._slope_[..., 0, 0], + index=idx.index, columns=idx.columns) + + @property + def intercept_(self): + """ Does not work with munich """ + idx = self.cdf_._idx_table() + return pd.DataFrame(self._intercept_[..., 0, 0], + index=idx.index, columns=idx.columns) diff --git a/docs/auto_examples/auto_examples_jupyter.zip b/docs/auto_examples/auto_examples_jupyter.zip index 5a169380..50191c84 100644 Binary files a/docs/auto_examples/auto_examples_jupyter.zip and b/docs/auto_examples/auto_examples_jupyter.zip differ diff --git a/docs/auto_examples/auto_examples_python.zip b/docs/auto_examples/auto_examples_python.zip index d5a5710d..f2f43263 100644 Binary files a/docs/auto_examples/auto_examples_python.zip and b/docs/auto_examples/auto_examples_python.zip differ diff --git a/docs/auto_examples/images/sphx_glr_plot_bondy_sensitivity_001.png b/docs/auto_examples/images/sphx_glr_plot_bondy_sensitivity_001.png index ee1f4dee..e40d5a25 100644 Binary files a/docs/auto_examples/images/sphx_glr_plot_bondy_sensitivity_001.png and b/docs/auto_examples/images/sphx_glr_plot_bondy_sensitivity_001.png differ diff --git a/docs/auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png b/docs/auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png index 29e97063..91b38172 100644 Binary files a/docs/auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png and b/docs/auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png differ diff --git a/docs/auto_examples/plot_bondy_sensitivity.ipynb b/docs/auto_examples/plot_bondy_sensitivity.ipynb index 625c8449..e040fe90 100644 --- a/docs/auto_examples/plot_bondy_sensitivity.ipynb +++ b/docs/auto_examples/plot_bondy_sensitivity.ipynb @@ -15,7 +15,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n# Testing Sensitivity of Bondy Tail Assumptions\n\n\nThis example demonstrates the usage of the `TailBondy` estimator as well as\npassing multiple scoring functions to `GridSearch`.\n\n" + "\n# Testing Sensitivity of Bondy Tail Assumptions\n\n\nThis example demonstrates the usage of the `TailBondy` estimator as well as\npassing multiple scoring functions to `GridSearch`. When the `earliest_age`\nis set to the last available in the Triangle, the estimator reverts to the\ntraditional Bondy method.\n\n" ] }, { @@ -26,7 +26,7 @@ }, "outputs": [], "source": [ - "import chainladder as cl\n\n# Fit basic development to a triangle\ntri = cl.load_sample('tail_sample')['paid']\ndev = cl.Development(average='simple').fit_transform(tri)\n\n\n# Return both the tail factor and the Bondy exponent in the scoring function\nscoring = {\n 'tail_factor': lambda x: x.cdf_[x.cdf_.development=='120-9999'].to_frame().values[0,0],\n 'bondy_exponent': lambda x : x.b_[0,0]}\n\n# Vary the 'earliest_age' assumption in GridSearch\nparam_grid=dict(earliest_age=list(range(12, 120, 12)))\ngrid = cl.GridSearch(cl.TailBondy(), param_grid, scoring)\nresults = grid.fit(dev).results_\n\nax = results.plot(x='earliest_age', y='bondy_exponent',\n title='Bondy Assumption Sensitivity', marker='o')\nresults.plot(x='earliest_age', y='tail_factor', grid=True,\n secondary_y=True, ax=ax, marker='o');" + "import chainladder as cl\n\n# Fit basic development to a triangle\ntri = cl.load_sample('tail_sample')['paid']\ndev = cl.Development(average='simple').fit_transform(tri)\n\n# Return both the tail factor and the Bondy exponent in the scoring function\nscoring = {\n 'tail_factor': lambda x: x.tail_.values[0,0],\n 'bondy_exponent': lambda x : x.b_.values[0,0]}\n\n# Vary the 'earliest_age' assumption in GridSearch\nparam_grid=dict(earliest_age=list(range(12, 120, 12)))\ngrid = cl.GridSearch(cl.TailBondy(), param_grid, scoring)\nresults = grid.fit(dev).results_\n\nax = results.plot(x='earliest_age', y='bondy_exponent',\n title='Bondy Assumption Sensitivity', marker='o')\nresults.plot(x='earliest_age', y='tail_factor', grid=True,\n secondary_y=True, ax=ax, marker='o');" ] } ], diff --git a/docs/auto_examples/plot_bondy_sensitivity.py b/docs/auto_examples/plot_bondy_sensitivity.py index a52de021..952c62f3 100644 --- a/docs/auto_examples/plot_bondy_sensitivity.py +++ b/docs/auto_examples/plot_bondy_sensitivity.py @@ -4,7 +4,9 @@ =============================================== This example demonstrates the usage of the `TailBondy` estimator as well as -passing multiple scoring functions to `GridSearch`. +passing multiple scoring functions to `GridSearch`. When the `earliest_age` +is set to the last available in the Triangle, the estimator reverts to the +traditional Bondy method. """ import chainladder as cl @@ -13,11 +15,10 @@ tri = cl.load_sample('tail_sample')['paid'] dev = cl.Development(average='simple').fit_transform(tri) - # Return both the tail factor and the Bondy exponent in the scoring function scoring = { - 'tail_factor': lambda x: x.cdf_[x.cdf_.development=='120-9999'].to_frame().values[0,0], - 'bondy_exponent': lambda x : x.b_[0,0]} + 'tail_factor': lambda x: x.tail_.values[0,0], + 'bondy_exponent': lambda x : x.b_.values[0,0]} # Vary the 'earliest_age' assumption in GridSearch param_grid=dict(earliest_age=list(range(12, 120, 12))) diff --git a/docs/auto_examples/plot_bondy_sensitivity.py.md5 b/docs/auto_examples/plot_bondy_sensitivity.py.md5 index 1143960b..d2834877 100644 --- a/docs/auto_examples/plot_bondy_sensitivity.py.md5 +++ b/docs/auto_examples/plot_bondy_sensitivity.py.md5 @@ -1 +1 @@ -dacdf27446a7ddaa1bca04f6b3d4225d \ No newline at end of file +9da70733f0eb47f24423433a8e3779a8 \ No newline at end of file diff --git a/docs/auto_examples/plot_bondy_sensitivity.rst b/docs/auto_examples/plot_bondy_sensitivity.rst index 2fbda261..1e4fdb94 100644 --- a/docs/auto_examples/plot_bondy_sensitivity.rst +++ b/docs/auto_examples/plot_bondy_sensitivity.rst @@ -14,7 +14,9 @@ Testing Sensitivity of Bondy Tail Assumptions =============================================== This example demonstrates the usage of the `TailBondy` estimator as well as -passing multiple scoring functions to `GridSearch`. +passing multiple scoring functions to `GridSearch`. When the `earliest_age` +is set to the last available in the Triangle, the estimator reverts to the +traditional Bondy method. @@ -30,7 +32,7 @@ passing multiple scoring functions to `GridSearch`. .. code-block:: none - + @@ -48,11 +50,10 @@ passing multiple scoring functions to `GridSearch`. tri = cl.load_sample('tail_sample')['paid'] dev = cl.Development(average='simple').fit_transform(tri) - # Return both the tail factor and the Bondy exponent in the scoring function scoring = { - 'tail_factor': lambda x: x.cdf_[x.cdf_.development=='120-9999'].to_frame().values[0,0], - 'bondy_exponent': lambda x : x.b_[0,0]} + 'tail_factor': lambda x: x.tail_.values[0,0], + 'bondy_exponent': lambda x : x.b_.values[0,0]} # Vary the 'earliest_age' assumption in GridSearch param_grid=dict(earliest_age=list(range(12, 120, 12))) @@ -67,7 +68,7 @@ passing multiple scoring functions to `GridSearch`. .. rst-class:: sphx-glr-timing - **Total running time of the script:** ( 0 minutes 1.008 seconds) + **Total running time of the script:** ( 0 minutes 0.586 seconds) .. _sphx_glr_download_auto_examples_plot_bondy_sensitivity.py: diff --git a/docs/auto_examples/plot_bondy_sensitivity_codeobj.pickle b/docs/auto_examples/plot_bondy_sensitivity_codeobj.pickle index 0f1785b3..ae377e1d 100644 Binary files a/docs/auto_examples/plot_bondy_sensitivity_codeobj.pickle and b/docs/auto_examples/plot_bondy_sensitivity_codeobj.pickle differ diff --git a/docs/auto_examples/sg_execution_times.rst b/docs/auto_examples/sg_execution_times.rst index 1239a040..c326f597 100644 --- a/docs/auto_examples/sg_execution_times.rst +++ b/docs/auto_examples/sg_execution_times.rst @@ -5,10 +5,10 @@ Computation times ================= -**00:04.047** total execution time for **auto_examples** files: +**00:00.586** total execution time for **auto_examples** files: +-----------------------------------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_auto_examples_plot_extrap_period.py` (``plot_extrap_period.py``) | 00:04.047 | 0.0 MB | +| :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` (``plot_bondy_sensitivity.py``) | 00:00.586 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_ave_analysis.py` (``plot_ave_analysis.py``) | 00:00.000 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ @@ -16,8 +16,6 @@ Computation times +-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_bf_apriori_from_cl.py` (``plot_bf_apriori_from_cl.py``) | 00:00.000 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` (``plot_bondy_sensitivity.py``) | 00:00.000 | 0.0 MB | -+-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_bootstrap.py` (``plot_bootstrap.py``) | 00:00.000 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_bootstrap_comparison.py` (``plot_bootstrap_comparison.py``) | 00:00.000 | 0.0 MB | @@ -34,6 +32,8 @@ Computation times +-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_exposure_triangle.py` (``plot_exposure_triangle.py``) | 00:00.000 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_plot_extrap_period.py` (``plot_extrap_period.py``) | 00:00.000 | 0.0 MB | ++-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_ibnr_runoff.py` (``plot_ibnr_runoff.py``) | 00:00.000 | 0.0 MB | +-----------------------------------------------------------------------------------------------+-----------+--------+ | :ref:`sphx_glr_auto_examples_plot_industry_to_company.py` (``plot_industry_to_company.py``) | 00:00.000 | 0.0 MB | diff --git a/docs/modules/development.rst b/docs/modules/development.rst index 236a38c0..66a5f8be 100644 --- a/docs/modules/development.rst +++ b/docs/modules/development.rst @@ -187,8 +187,10 @@ method: .. [SM2016] `M Shapland, "Using the ODP Bootstrap Model: A Practitioner's Guide", CAS Monograph No.4 `__ -LDF Curve Fitting -================= +.. _clarkldf: + +Growth Curve Fitting +==================== :class:`ClarkLDF` is an application of Maximum Likelihood Estimation (MLE) theory for modeling the distribution of loss development. This model is used to estimate future loss emergence, and the variability around that estimate. The value of diff --git a/docs/modules/generated/chainladder.Development.examples b/docs/modules/generated/chainladder.Development.examples index 5d9e070b..7c417a7c 100644 --- a/docs/modules/generated/chainladder.Development.examples +++ b/docs/modules/generated/chainladder.Development.examples @@ -3,44 +3,6 @@ Examples using ``chainladder.Development`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_mack_thumb.png - :alt: Mack Chainladder Example - - :ref:`sphx_glr_auto_examples_plot_mack.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_mack.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_development_periods_thumb.png - :alt: Basic Assumption Tuning with Pipeline and Gridsearch - - :ref:`sphx_glr_auto_examples_plot_development_periods.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_development_periods.py` - .. raw:: html
@@ -59,79 +21,3 @@ Examples using ``chainladder.Development`` .. only:: not html * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_benktander_thumb.png - :alt: Benktander: Relationship between Chainladder and BornhuetterFerguson - - :ref:`sphx_glr_auto_examples_plot_benktander.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_benktander.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bootstrap_thumb.png - :alt: ODP Bootstrap Example - - :ref:`sphx_glr_auto_examples_plot_bootstrap.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_bootstrap.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_munich_thumb.png - :alt: Munich Adjustment Example - - :ref:`sphx_glr_auto_examples_plot_munich.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_munich.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_exhibits_thumb.png - :alt: Sample Excel Exhibit functionality - - :ref:`sphx_glr_auto_examples_plot_exhibits.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_exhibits.py` diff --git a/docs/modules/generated/chainladder.DevelopmentBase.examples b/docs/modules/generated/chainladder.DevelopmentBase.examples index db3fa5a7..dfbbba0a 100644 --- a/docs/modules/generated/chainladder.DevelopmentBase.examples +++ b/docs/modules/generated/chainladder.DevelopmentBase.examples @@ -3,82 +3,6 @@ Examples using ``chainladder.DevelopmentBase`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_clarkldf_thumb.png - :alt: Clark Growth Curves - - :ref:`sphx_glr_auto_examples_plot_clarkldf.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_clarkldf.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_value_at_risk_thumb.png - :alt: Value at Risk example - - :ref:`sphx_glr_auto_examples_plot_value_at_risk.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_value_at_risk.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_mack_thumb.png - :alt: Mack Chainladder Example - - :ref:`sphx_glr_auto_examples_plot_mack.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_mack.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_development_periods_thumb.png - :alt: Basic Assumption Tuning with Pipeline and Gridsearch - - :ref:`sphx_glr_auto_examples_plot_development_periods.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_development_periods.py` - .. raw:: html
@@ -97,136 +21,3 @@ Examples using ``chainladder.DevelopmentBase`` .. only:: not html * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bootstrap_comparison_thumb.png - :alt: ODP Bootstrap Comparison - - :ref:`sphx_glr_auto_examples_plot_bootstrap_comparison.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_bootstrap_comparison.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_stochastic_bornferg_thumb.png - :alt: Stochastic Bornhuetter Ferguson - - :ref:`sphx_glr_auto_examples_plot_stochastic_bornferg.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_stochastic_bornferg.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_benktander_thumb.png - :alt: Benktander: Relationship between Chainladder and BornhuetterFerguson - - :ref:`sphx_glr_auto_examples_plot_benktander.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_benktander.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_clarkldf_resid_thumb.png - :alt: Clark Residual Plots - - :ref:`sphx_glr_auto_examples_plot_clarkldf_resid.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_clarkldf_resid.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bootstrap_thumb.png - :alt: ODP Bootstrap Example - - :ref:`sphx_glr_auto_examples_plot_bootstrap.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_bootstrap.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_munich_thumb.png - :alt: Munich Adjustment Example - - :ref:`sphx_glr_auto_examples_plot_munich.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_munich.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_exhibits_thumb.png - :alt: Sample Excel Exhibit functionality - - :ref:`sphx_glr_auto_examples_plot_exhibits.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_exhibits.py` diff --git a/docs/modules/generated/chainladder.EstimatorIO.examples b/docs/modules/generated/chainladder.EstimatorIO.examples index d257a924..666baf37 100644 --- a/docs/modules/generated/chainladder.EstimatorIO.examples +++ b/docs/modules/generated/chainladder.EstimatorIO.examples @@ -5,14 +5,14 @@ Examples using ``chainladder.EstimatorIO`` .. raw:: html -
+
.. only:: html - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_extrap_period_thumb.png - :alt: Extrapolation Period Sensitivity + .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png + :alt: Testing Sensitivity of Bondy Tail Assumptions - :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` .. raw:: html @@ -20,4 +20,4 @@ Examples using ``chainladder.EstimatorIO`` .. only:: not html - * :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` diff --git a/docs/modules/generated/chainladder.GridSearch.examples b/docs/modules/generated/chainladder.GridSearch.examples index 3ac7ccc1..365a3aa1 100644 --- a/docs/modules/generated/chainladder.GridSearch.examples +++ b/docs/modules/generated/chainladder.GridSearch.examples @@ -5,14 +5,14 @@ Examples using ``chainladder.GridSearch`` .. raw:: html -
+
.. only:: html - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_extrap_period_thumb.png - :alt: Extrapolation Period Sensitivity + .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png + :alt: Testing Sensitivity of Bondy Tail Assumptions - :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` .. raw:: html @@ -20,4 +20,4 @@ Examples using ``chainladder.GridSearch`` .. only:: not html - * :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` diff --git a/docs/modules/generated/chainladder.GridSearch.fit.examples b/docs/modules/generated/chainladder.GridSearch.fit.examples index 9909421a..91740567 100644 --- a/docs/modules/generated/chainladder.GridSearch.fit.examples +++ b/docs/modules/generated/chainladder.GridSearch.fit.examples @@ -21,22 +21,3 @@ Examples using ``chainladder.GridSearch.fit`` .. only:: not html * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` - -.. raw:: html - -
- -.. only:: html - - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_benktander_thumb.png - :alt: Benktander: Relationship between Chainladder and BornhuetterFerguson - - :ref:`sphx_glr_auto_examples_plot_benktander.py` - -.. raw:: html - -
- -.. only:: not html - - * :ref:`sphx_glr_auto_examples_plot_benktander.py` diff --git a/docs/modules/generated/chainladder.TailBase.examples b/docs/modules/generated/chainladder.TailBase.examples index f06135b9..6dc10bb1 100644 --- a/docs/modules/generated/chainladder.TailBase.examples +++ b/docs/modules/generated/chainladder.TailBase.examples @@ -5,14 +5,14 @@ Examples using ``chainladder.TailBase`` .. raw:: html -
+
.. only:: html - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_extrap_period_thumb.png - :alt: Extrapolation Period Sensitivity + .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png + :alt: Testing Sensitivity of Bondy Tail Assumptions - :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` .. raw:: html @@ -20,4 +20,4 @@ Examples using ``chainladder.TailBase`` .. only:: not html - * :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` diff --git a/docs/modules/generated/chainladder.TailCurve.intercept_.examples b/docs/modules/generated/chainladder.TailCurve.intercept_.examples new file mode 100644 index 00000000..e69de29b diff --git a/docs/modules/generated/chainladder.TailCurve.slope_.examples b/docs/modules/generated/chainladder.TailCurve.slope_.examples new file mode 100644 index 00000000..e69de29b diff --git a/docs/modules/generated/chainladder.Triangle.examples b/docs/modules/generated/chainladder.Triangle.examples index b4d0b8f1..c8bca002 100644 --- a/docs/modules/generated/chainladder.Triangle.examples +++ b/docs/modules/generated/chainladder.Triangle.examples @@ -5,14 +5,14 @@ Examples using ``chainladder.Triangle`` .. raw:: html -
+
.. only:: html - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_extrap_period_thumb.png - :alt: Extrapolation Period Sensitivity + .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png + :alt: Testing Sensitivity of Bondy Tail Assumptions - :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` .. raw:: html @@ -20,4 +20,4 @@ Examples using ``chainladder.Triangle`` .. only:: not html - * :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` diff --git a/docs/modules/generated/chainladder.load_sample.examples b/docs/modules/generated/chainladder.load_sample.examples index ebbdf9bc..d6653438 100644 --- a/docs/modules/generated/chainladder.load_sample.examples +++ b/docs/modules/generated/chainladder.load_sample.examples @@ -5,14 +5,14 @@ Examples using ``chainladder.load_sample`` .. raw:: html -
+
.. only:: html - .. figure:: /auto_examples/images/thumb/sphx_glr_plot_extrap_period_thumb.png - :alt: Extrapolation Period Sensitivity + .. figure:: /auto_examples/images/thumb/sphx_glr_plot_bondy_sensitivity_thumb.png + :alt: Testing Sensitivity of Bondy Tail Assumptions - :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` .. raw:: html @@ -20,4 +20,4 @@ Examples using ``chainladder.load_sample`` .. only:: not html - * :ref:`sphx_glr_auto_examples_plot_extrap_period.py` + * :ref:`sphx_glr_auto_examples_plot_bondy_sensitivity.py` diff --git a/docs/modules/tails.rst b/docs/modules/tails.rst index c11c07bb..37c5e43e 100644 --- a/docs/modules/tails.rst +++ b/docs/modules/tails.rst @@ -5,31 +5,61 @@ =============== Tail Estimators =============== + +The Basics +========== + The Tails module provides a variety of tail transformers that allow for the extrapolation of development patterns beyond the end of the triangle. Tail factors are used extensively in commercial lines of business. +Every tail estimator produced a ``tail_`` attribute which represents the point +estimate of the tail of the Triangle. + +Run Off +------- +In addition to point estimates, tail estimators support run-off analysis. The +``ldf_`` and ``cdf_`` attribute splits the tail point estimate into enough +development patterns to allow for tail run-off to be examined for at least another +valuation year. For an annual ``development_grain`` grain, the development pattterns +include two additional patterns. + + >>> development.cdf_ + 12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult + (All) 8.920234 2.974047 1.831848 1.441392 1.230198 1.104917 1.060448 1.026309 1.009217 + >>> tail.cdf_ + 12-Ult 24-Ult 36-Ult 48-Ult 60-Ult 72-Ult 84-Ult 96-Ult 108-Ult 120-Ult 132-Ult + (All) 9.004403 3.002109 1.849133 1.454993 1.241806 1.115343 1.070454 1.035993 1.018739 1.009436 1.005006 + +For quarterly grain, there are five additional development patterns and for monthly +there are thirteen. + +Attachment Age +-------------- +By default, tail estimators attach to the oldest ``development`` age of the ``Triangle``. +In practice, the last several known development factors of a ``Triangle`` can be +unreliable and attaching the tail earlier and using it as a smoothing mechanism +is preferred. All tail estimators have an ``attachment_age`` parameter that +allows you to select the development age to which the tail will attach. +Regardless of ``attachment_age``, the tail development factor vector will +always include patterns at least one year beyond the known data in the ``Triangle``. + + + .. _constant: External Data ============= :class:`TailConstant` allows you to input a tail factor as a constant. This is useful when relying on tail selections from an external source like industry data. -.. warning:: - The chainlader package does not currently support any stochastic - paramters for :class:`TailConstant`. This precludes the compatibility of - this estimator with stochastic methods such as :class:`MackChainladder`. - The tail factor supplied applies to all individual triangles contained within the Triangle object. If this is not the desired outcome, slicing individual triangles and applying :class:`TailConstant` separately to each can be done. Decay ----- -An exponential ``decay`` parameter is also available to describe the run-off nature of -the tail. Although tail factors are generally specified as scalar value, the -``chainladder`` package will expand it over a single future calendar year so that -run-off analyses can be performed for the upcoming year. +An exponential ``decay`` parameter is also available to facilitate the run off +analysis described above. >>> import chainladder as cl >>> abc = cl.Development().fit_transform(cl.load_sample('abc')) @@ -55,7 +85,12 @@ are supported. :scale: 50% In general, ``inverse_power`` fit produce more conservative tail estimates than -the ``exponential`` fit. +the ``exponential`` fit. Both are fit using basic OLS regression on transformed +`X` and `y` values. + +For exponential curve fitting, we use fit a regression to the natural log of +the development portion of a link-ratio, `f`. + Extrapolation Period --------------------- @@ -69,11 +104,42 @@ asymptotic value. :align: center :scale: 50% +Regression parameters +---------------------- +Underlying the curve fit is an OLS regression which generates both a `slope_` +and `intercept_` term. + +For the `exponential` curve fit with slope, b and intercept a, the tail factor +is: + +.. math:: + \prod_{i}^{N}1+exp(\beta X+\alpha ) + +For the `inverse_power` curve, the tail factor is: + +.. math:: + \prod_{i}^{N}1+exp(\alpha) X^{\beta} + + +Deriving the `tail_` factor manually: + + >>> import chainladder as cl + >>> import numpy as np + >>> raa = cl.load_sample('raa') + >>> tail = cl.TailCurve('exponential').fit(raa) + >>> np.prod((1+np.exp(np.arange(10, 10+tail.extrap_periods)*tail.slope_.values+tail.intercept_.values))) + 1.0094357515812302 + >>> tail = cl.TailCurve('inverse_power').fit(raa) + >>> np.prod(1+np.exp(tail.intercept_.values)*(np.arange(10, 10+tail.extrap_periods)**tail.slope_.values)) + 1.1014821181181595 + .. topic:: References .. [CAS2013] `CAS Tail Factor Working Party, "The Estimation of Loss Development Tail Factors: A Summary Report", Casualty Actuarial Society E-Forum `__ + + .. _bondy: The Bondy Tail ============== @@ -87,8 +153,10 @@ More formally, the tail factor is defined as: .. math:: F(n)=f(n-1)^{B}f(n-1)^{B^{2}}...=f(n-1)^{\frac{B}{1-B}} -Rather than using the last known development factor explicitely, we estimate the +Rather than using the last known development factor explicitly, we estimate the fitted LDF using the formula above along with the ``earliest_age`` parameter. +The following example shows how the :class:`TailBondy` estimator develops a +tail factor from ``b_``, the Bondy exponent. >>> import chainladder as cl >>> # Data and initial development patterns @@ -119,9 +187,9 @@ fitted LDF using the formula above along with the ``earliest_age`` parameter. Growth Curve Extrapolation ========================== :class:`TailClark` is a continuation of the :class:`ClarkLDF` model. Familiarity -with :class:`ClarkLDF` will aid in understanding this Estimator. The growth -curve approach used by Clark produces development patterns for any age including -ages beyond the edge of the Triangle. +with :ref:`Growth Curve Fitting` will aid in understanding this Estimator. +The growth curve approach used by Clark produces development patterns for any +age including ages beyond the edge of the Triangle. An example completing Clark's model: diff --git a/docs/modules/triangle.rst b/docs/modules/triangle.rst index 56a48d5e..513f6261 100644 --- a/docs/modules/triangle.rst +++ b/docs/modules/triangle.rst @@ -1,6 +1,7 @@ .. _triangle: .. currentmodule:: chainladder + ======== Triangle ======== diff --git a/docs/tutorials/template-tutorial.ipynb b/docs/tutorials/template-tutorial.ipynb index 00479046..390a15ae 100644 --- a/docs/tutorials/template-tutorial.ipynb +++ b/docs/tutorials/template-tutorial.ipynb @@ -17,7 +17,7 @@ { "data": { "text/plain": [ - "'0.6.1'" + "'0.6.3'" ] }, "execution_count": 1, diff --git a/examples/plot_bondy_sensitivity.py b/examples/plot_bondy_sensitivity.py index a52de021..952c62f3 100644 --- a/examples/plot_bondy_sensitivity.py +++ b/examples/plot_bondy_sensitivity.py @@ -4,7 +4,9 @@ =============================================== This example demonstrates the usage of the `TailBondy` estimator as well as -passing multiple scoring functions to `GridSearch`. +passing multiple scoring functions to `GridSearch`. When the `earliest_age` +is set to the last available in the Triangle, the estimator reverts to the +traditional Bondy method. """ import chainladder as cl @@ -13,11 +15,10 @@ tri = cl.load_sample('tail_sample')['paid'] dev = cl.Development(average='simple').fit_transform(tri) - # Return both the tail factor and the Bondy exponent in the scoring function scoring = { - 'tail_factor': lambda x: x.cdf_[x.cdf_.development=='120-9999'].to_frame().values[0,0], - 'bondy_exponent': lambda x : x.b_[0,0]} + 'tail_factor': lambda x: x.tail_.values[0,0], + 'bondy_exponent': lambda x : x.b_.values[0,0]} # Vary the 'earliest_age' assumption in GridSearch param_grid=dict(earliest_age=list(range(12, 120, 12))) diff --git a/setup.py b/setup.py index 16e183a4..4b24cd61 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ descr = "Chainladder Package - P&C Loss Reserving package " name = 'chainladder' url = 'https://github.com/casact/chainladder-python' -version='0.6.3' # Put this in __init__.py +version='0.7.0' # Put this in __init__.py data_path = '' setup(