Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions chainladder/tails/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
""" tails should store all tail methodologies
"""
"""tails should store all tail methodologies"""

from chainladder.tails.base import TailBase # noqa (API import)
from chainladder.tails.constant import TailConstant # noqa (API import)
from chainladder.tails.curve import TailCurve # noqa (API import)
from chainladder.tails.bondy import TailBondy # noqa (API import)
from chainladder.tails.clark import TailClark # noqa (API import)


__all__ = [
"TailBase",
"TailConstant",
Expand Down
9 changes: 6 additions & 3 deletions chainladder/tails/bondy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@


class TailBondy(TailBase):
"""Estimator for the Generalized Bondy tail factor.
"""
Estimator for the Generalized Bondy tail factor.

.. versionadded:: 0.6.0

Expand Down Expand Up @@ -121,7 +122,8 @@ def __init__(self, earliest_age=None, attachment_age=None, projection_period=12)
self.projection_period = projection_period

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
"""
Fit the model with X.

Parameters
----------
Expand Down Expand Up @@ -207,7 +209,8 @@ def fit(self, X, y=None, sample_weight=None):
return self

def transform(self, X):
"""Transform X.
"""
Transform X.

Parameters
----------
Expand Down
35 changes: 24 additions & 11 deletions chainladder/tails/clark.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@


class TailClark(TailBase):
"""Allows for extraploation of LDFs to form a tail factor.
"""
Allows for extraploation of LDFs to form a tail factor.

.. versionadded:: 0.6.4

Expand Down Expand Up @@ -112,15 +113,21 @@ class TailClark(TailBase):

"""

def __init__(self, growth="loglogistic", truncation_age=None,
attachment_age=None, projection_period=12):
def __init__(
self,
growth="loglogistic",
truncation_age=None,
attachment_age=None,
projection_period=12,
):
self.growth = growth
self.truncation_age = truncation_age
self.attachment_age = attachment_age
self.projection_period = projection_period

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
"""
Fit the model with X.

Parameters
----------
Expand Down Expand Up @@ -151,14 +158,17 @@ def fit(self, X, y=None, sample_weight=None):
fitted.values[..., :-1] / fitted.values[..., 1:],
fitted.values[..., -1:],
),
-1,
axis=-1,
)
fitted = xp.repeat(fitted, self.ldf_.values.shape[2], 2)
attachment_age = self.attachment_age if self.attachment_age else X.ddims[-2]
self.ldf_.values = xp.concatenate((
self.ldf_.values[..., : sum(self.ldf_.ddims < attachment_age)],
fitted[..., -sum(self.ldf_.ddims >= attachment_age) :],),
axis=-1,)
self.ldf_.values = xp.concatenate(
(
self.ldf_.values[..., : sum(self.ldf_.ddims < attachment_age)],
fitted[..., -sum(self.ldf_.ddims >= attachment_age) :],
),
axis=-1,
)
self.omega_ = model.omega_
self.theta_ = model.theta_
self.G_ = model.G_
Expand All @@ -169,14 +179,17 @@ def fit(self, X, y=None, sample_weight=None):
self.elr_ = model.elr_
self.norm_resid_ = model.norm_resid_
if self.truncation_age:
self.ldf_.values[..., -1:] = self.ldf_.values[..., -1:] * self.G_(self.truncation_age).values
self.ldf_.values[..., -1:] = (
self.ldf_.values[..., -1:] * self.G_(self.truncation_age).values
)
# self._get_tail_stats(self)
if backend == "cupy":
self = self.set_backend("cupy", inplace=True)
return self

def transform(self, X):
"""Transform X.
"""
Transform X.

Parameters
----------
Expand Down
6 changes: 4 additions & 2 deletions chainladder/tails/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@


class TailConstant(TailBase):
"""Allows for the entry of a constant tail factor to LDFs.
"""
Allows for the entry of a constant tail factor to LDFs.

Parameters
----------
Expand Down Expand Up @@ -129,7 +130,8 @@ def __init__(self, tail=1.0, decay=0.5, attachment_age=None, projection_period=1
self.projection_period = projection_period

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
"""
Fit the model with X.

Parameters
----------
Expand Down
6 changes: 4 additions & 2 deletions chainladder/tails/curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@


class TailCurve(TailBase):
"""Allows for extraploation of LDFs to form a tail factor.
"""
Allows for extraploation of LDFs to form a tail factor.

Parameters
----------
Expand Down Expand Up @@ -158,7 +159,8 @@ def __init__(
self.projection_period = projection_period

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
"""
Fit the model with X.

Parameters
----------
Expand Down
16 changes: 13 additions & 3 deletions chainladder/tails/tests/test_bondy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
from __future__ import annotations

import chainladder as cl

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from chainladder import Triangle


def test_bondy1():
tri = cl.load_sample("tail_sample")["paid"]
def test_bondy1(tail_sample: Triangle) -> None:
tri = tail_sample["paid"]
dev = cl.Development(average="simple").fit_transform(tri)
assert round(float(cl.TailBondy(earliest_age=12).fit(dev).cdf_.values[0, 0, 0, -2]), 3) == 1.028
assert (
round(float(cl.TailBondy(earliest_age=12).fit(dev).cdf_.values[0, 0, 0, -2]), 3)
== 1.028
)
23 changes: 23 additions & 0 deletions chainladder/tails/tests/test_clark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

import numpy as np
import chainladder as cl

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from chainladder import Triangle


def test_truncation_age(genins: Triangle, atol: float) -> None:
"""
Validate that sufficiently distant truncation age is equivalent to
not truncating
"""
long_truncation = (
cl.TailClark(truncation_age=99999).fit(cl.ClarkLDF().fit_transform(genins)).cdf_
)
no_truncation = cl.TailClark().fit(cl.ClarkLDF().fit_transform(genins)).cdf_
assert np.allclose(
long_truncation.values[..., -1], no_truncation.values[..., -1], atol=atol
)
Comment thread
cursor[bot] marked this conversation as resolved.
4 changes: 3 additions & 1 deletion chainladder/tails/tests/test_constant.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import chainladder as cl


def test_constant_balances(qtr):
xp = qtr.get_array_module()
assert (
round(
float(
xp.prod(
cl.TailConstant(1.05, decay=0.8)
cl
.TailConstant(1.05, decay=0.8)
.fit(qtr)
.ldf_.iloc[0, 1]
.values[0, 0, 0, -5:]
Expand Down
29 changes: 15 additions & 14 deletions chainladder/tails/tests/test_exponential.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
from __future__ import annotations

import chainladder as cl
import pytest

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from chainladder import Triangle


def test_fit_period():
tri = cl.load_sample("tail_sample")
dev = cl.Development(average="simple").fit_transform(tri)
def test_fit_period(tail_sample: Triangle) -> None:
dev = cl.Development(average="simple").fit_transform(tail_sample)
assert (
round(
cl.TailCurve(fit_period=(tri.ddims[-7], None), extrap_periods=10)
cl
.TailCurve(fit_period=(tail_sample.ddims[-7], None), extrap_periods=10)
.fit(dev)
.cdf_["paid"]
.set_backend("numpy", inplace=True)
Expand All @@ -18,24 +25,18 @@ def test_fit_period():
)


def test_curve_validation():
def test_curve_validation(tail_sample: Triangle) -> None:
"""
Test validation of the curve parameter. Should raise a value error if an incorrect argument is supplied.
"""

with pytest.raises(ValueError):
tri = cl.load_sample('tail_sample')
cl.TailCurve(
curve='Exponential'
).fit_transform(tri)
cl.TailCurve(curve="Exponential").fit_transform(tail_sample)


def test_errors_validation():
def test_errors_validation(tail_sample: Triangle) -> None:
"""
Test validation of the errors parameter. Should raise a value error if an incorrect argument is supplied.
"""
with pytest.raises(ValueError):
tri = cl.load_sample('tail_sample')
cl.TailCurve(
errors='Ignore'
).fit_transform(tri)
cl.TailCurve(errors="Ignore").fit_transform(tail_sample)
9 changes: 9 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def pytest_generate_tests(metafunc):
)
if "prism" in metafunc.fixturenames:
metafunc.parametrize("prism", ["sparse_only_run"], indirect=True)
if "tail_sample" in metafunc.fixturenames:
metafunc.parametrize(
"tail_sample", ["normal_run", "sparse_only_run"], indirect=True
)
if "xyz" in metafunc.fixturenames:
metafunc.parametrize("xyz", ["normal_run", "sparse_only_run"], indirect=True)

Expand Down Expand Up @@ -116,6 +120,11 @@ def monthly(request):
yield from _sample_fixture(request, "prism", transform=lambda t: t.sum())


@pytest.fixture
def tail_sample(request):
yield from _sample_fixture(request, "tail_sample")


@pytest.fixture
def xyz(request):
yield from _sample_fixture(request, "xyz")
Expand Down
Loading