Skip to content

Commit

Permalink
resolves #98
Browse files Browse the repository at this point in the history
  • Loading branch information
jbogaardt committed Oct 26, 2020
1 parent dcd0cd8 commit 8bb507a
Show file tree
Hide file tree
Showing 57 changed files with 644 additions and 433 deletions.
15 changes: 10 additions & 5 deletions chainladder/methods/capecod.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from chainladder.methods import MethodBase

import warnings

class CapeCod(MethodBase):
"""Applies the CapeCod technique to triangle **X**
Parameters
----------
trend : float (default=0.0)
The cape cod trend assumption
The cape cod trend assumption. Any Trend transformer on X will override
this argument.
decay : float (defaut=1.0)
The cape cod decay assumption
Expand Down Expand Up @@ -65,9 +66,12 @@ def _get_ultimate(self, X, sample_weight):
cdf = self._align_cdf(ult, sample_weight)
exposure = sample_weight.values
reported_exposure = exposure / cdf
trend_exponent = len_orig - xp.arange(len_orig) - 1
trend_array = (1 + self.trend) ** (trend_exponent)
trend_array = trend_array[..., None]
if hasattr(X, 'trend_'):
if self.trend != 0:
warnings.warn("CapeCod Trend assumption is ignored when X has a trend_ property.")
trend_array = X.trend_.latest_diagonal.values
else:
trend_array = (X.trend(self.trend) / X).latest_diagonal.values
if hasattr(sample_weight, 'olf_'):
sw_olf_array = sample_weight.olf_.values
else:
Expand All @@ -90,6 +94,7 @@ def _get_ultimate(self, X, sample_weight):
detrended_ultimate = apriori_.values / trend_array / X_olf_array * sw_olf_array
detrended_apriori_ = ult.copy()
detrended_apriori_.values = detrended_ultimate

ult.values = latest + detrended_ultimate * (1 - 1 / cdf) * exposure

ult = self._set_ult_attr(ult)
Expand Down
1 change: 1 addition & 0 deletions chainladder/workflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from chainladder.workflow.bootstrap import BootstrapODPSample # noqa (API import)
from chainladder.workflow.berqsherm import BerquistSherman # noqa (API import)
from chainladder.workflow.parallelogram import ParallelogramOLF # noqa (API import)
from chainladder.workflow.trend import Trend # noqa (API import)
80 changes: 80 additions & 0 deletions chainladder/workflow/trend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

from sklearn.base import BaseEstimator, TransformerMixin
from chainladder.core.io import EstimatorIO
import pandas as pd


class Trend(BaseEstimator, TransformerMixin, EstimatorIO):
"""
Estimator to create and apply trend factors to a Triangle object. This
is commonly used for estimators like `CapeCod`.
Parameters
----------
trend : list-like
The list containing the annual trends expressed as a decimal. For example,
5% decrease should be stated as -0.05
date : str
A list-like set of dates at which trends start. If None then the valuation
date of the triangle is assumed.
axis : str (options: [‘origin’, ‘valuation’])
The axis on which to apply the trend
Attributes
----------
trend_ :
A triangle representation of the trend factors
"""

def __init__(self, trend=0.0, end_date=None, axis='origin'):
self.trend = trend if type(trend) is list else [trend]
self.end_date = end_date if type(end_date) is list else [end_date]
self.axis = axis

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
Parameters
----------
X : Triangle-like
Data to which the model will be applied.
y : Ignored
sample_weight : Ignored
Returns
-------
self : object
Returns the instance itself.
"""
date = [item if item else X.valuation_date.strftime('%Y-%m-%d') for item in self.end_date]
date = pd.to_datetime(date).tolist()
self.trend_ = X.copy()
for i, trend in enumerate(self.trend):
self.trend_ = self.trend_.trend(trend, self.axis, date[i])
self.trend_ = self.trend_ / X
return self

def transform(self, X, y=None, sample_weight=None):
""" If X and self are of different shapes, align self to X, else
return self.
Parameters
----------
X : Triangle
The triangle to be transformed
Returns
-------
X_new : New triangle with transformed attributes.
"""
X_new = X.copy()
triangles = ["trend_"]
for item in triangles:
setattr(X_new, item, getattr(self, item))
X_new._set_slicers()
return X_new
Binary file modified docs/auto_examples/auto_examples_jupyter.zip
Binary file not shown.
Binary file modified docs/auto_examples/auto_examples_python.zip
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions docs/auto_examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,27 @@ Examples

/auto_examples/plot_capecod

.. raw:: html

<div class="sphx-glr-thumbcontainer" tooltip="This example demonstrates how to incorporate on-leveling into the CapeCod estimator. The on-le...">

.. only:: html

.. figure:: /auto_examples/images/thumb/sphx_glr_plot_capecod_onlevel_thumb.png
:alt: Include On-leveling into Cape Cod

:ref:`sphx_glr_auto_examples_plot_capecod_onlevel.py`

.. raw:: html

</div>


.. toctree::
:hidden:

/auto_examples/plot_capecod_onlevel

.. raw:: html

<div class="sphx-glr-thumbcontainer" tooltip="This example demonstrates some of the flexibility of the Excel outputs. It creates an Excel fil...">
Expand Down
54 changes: 54 additions & 0 deletions docs/auto_examples/plot_capecod_onlevel.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n# Include On-leveling into Cape Cod\n\n\nThis example demonstrates how to incorporate on-leveling into the `CapeCod`\nestimator. The on-level approach emulates the approach taken by Friedland in\n\"Estimating Unpaid Claims Using Basic Techniques\" Chapter 10. The `ParallelogramOLF`\nestimator is new in chainladder 0.7.9 as is the `xyz` triangle.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import chainladder as cl\nimport pandas as pd\n\n# Grab a triangle\nxyz = cl.load_sample('xyz')\n\n# Premium on-leveling factors\nrate_history = pd.DataFrame({\n 'date': ['1/1/1999', '1/1/2000', '1/1/2001', '1/1/2002', '1/1/2003',\n '1/1/2004', '1/1/2005', '1/1/2006', '1/1/2007', '1/1/2008'],\n 'rate_change': [.02, .02, .02, .02, .05, .075, .15, .1, -.2, -.2]\n})\n\n# Loss on-leveling factors\ntort_reform = pd.DataFrame({\n 'date': ['1/1/2006', '1/1/2007'],\n 'rate_change': [-0.1067, -.25]\n})\n\n# In addition to development, include onlevel estimator in pipeline for loss\npipe = cl.Pipeline(steps=[\n ('olf', cl.ParallelogramOLF(tort_reform, change_col='rate_change', date_col='date', vertical_line=True)),\n ('dev', cl.Development(n_periods=2)),\n ('model', cl.CapeCod(trend=0.034))\n])\n\n# Define X\nX = cl.load_sample('xyz')['Incurred']\n\n# Separately apply on-level factors for premium\nsample_weight = cl.ParallelogramOLF(\n rate_history, change_col='rate_change', date_col='date',\n vertical_line=True).fit_transform(xyz['Premium'].latest_diagonal)\n\n# Fit Cod Estimator\npipe.fit(X, sample_weight=sample_weight).named_steps.model.ultimate_\n\n# Create a Cape Cod pipeline without onleveling\npipe2 = cl.Pipeline(steps=[\n ('dev', cl.Development(n_periods=2)),\n ('model', cl.CapeCod(trend=0.034))\n])\n\n\n# Finally fit Cod Estimator without on-leveling\npipe2.fit(X, sample_weight=xyz['Premium'].latest_diagonal).named_steps.model.ultimate_\n\n# Plot results\ncl.concat((\n pipe.named_steps.model.ultimate_.rename('columns', ['With On-level']),\n pipe2.named_steps.model.ultimate_.rename('columns', ['Without On-level'])), 1).T.plot(\n title='Cape Cod sensitivity to on-leveling', grid=True);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
63 changes: 63 additions & 0 deletions docs/auto_examples/plot_capecod_onlevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
=====================================
Include On-leveling into Cape Cod
=====================================
This example demonstrates how to incorporate on-leveling into the `CapeCod`
estimator. The on-level approach emulates the approach taken by Friedland in
"Estimating Unpaid Claims Using Basic Techniques" Chapter 10. The `ParallelogramOLF`
estimator is new in chainladder 0.7.9 as is the `xyz` triangle.
"""

import chainladder as cl
import pandas as pd

# Grab a triangle
xyz = cl.load_sample('xyz')

# Premium on-leveling factors
rate_history = pd.DataFrame({
'date': ['1/1/1999', '1/1/2000', '1/1/2001', '1/1/2002', '1/1/2003',
'1/1/2004', '1/1/2005', '1/1/2006', '1/1/2007', '1/1/2008'],
'rate_change': [.02, .02, .02, .02, .05, .075, .15, .1, -.2, -.2]
})

# Loss on-leveling factors
tort_reform = pd.DataFrame({
'date': ['1/1/2006', '1/1/2007'],
'rate_change': [-0.1067, -.25]
})

# In addition to development, include onlevel estimator in pipeline for loss
pipe = cl.Pipeline(steps=[
('olf', cl.ParallelogramOLF(tort_reform, change_col='rate_change', date_col='date', vertical_line=True)),
('dev', cl.Development(n_periods=2)),
('model', cl.CapeCod(trend=0.034))
])

# Define X
X = cl.load_sample('xyz')['Incurred']

# Separately apply on-level factors for premium
sample_weight = cl.ParallelogramOLF(
rate_history, change_col='rate_change', date_col='date',
vertical_line=True).fit_transform(xyz['Premium'].latest_diagonal)

# Fit Cod Estimator
pipe.fit(X, sample_weight=sample_weight).named_steps.model.ultimate_

# Create a Cape Cod pipeline without onleveling
pipe2 = cl.Pipeline(steps=[
('dev', cl.Development(n_periods=2)),
('model', cl.CapeCod(trend=0.034))
])


# Finally fit Cod Estimator without on-leveling
pipe2.fit(X, sample_weight=xyz['Premium'].latest_diagonal).named_steps.model.ultimate_

# Plot results
cl.concat((
pipe.named_steps.model.ultimate_.rename('columns', ['With On-level']),
pipe2.named_steps.model.ultimate_.rename('columns', ['Without On-level'])), 1).T.plot(
title='Cape Cod sensitivity to on-leveling', grid=True);
1 change: 1 addition & 0 deletions docs/auto_examples/plot_capecod_onlevel.py.md5
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6c44959848b170466aae812e7d76378e
Loading

0 comments on commit 8bb507a

Please sign in to comment.