diff --git a/chainladder/development/outstanding.py b/chainladder/development/outstanding.py index 184a19fd3..2cbb57311 100644 --- a/chainladder/development/outstanding.py +++ b/chainladder/development/outstanding.py @@ -12,6 +12,7 @@ import numpy as np import pandas as pd +import warnings if TYPE_CHECKING: from chainladder.core import Triangle @@ -168,17 +169,28 @@ def fit(self, X, y=None, sample_weight=None): Development(n_periods=self.case_n_periods).fit(self.X_.sum(0).sum(1)).w_ ) - case_to_prior_case_naned = self.case_to_prior_case_ * self.case_w_ - case_to_prior_case_naned.values = num_to_nan(case_to_prior_case_naned.values) - - self.case_ldf_ = self.case_to_prior_case_.mean(2) # this has the wrong value - self.case_ldf_.values = case_to_prior_case_naned.mean(axis=2).values - - paid_to_prior_case_naned = self.paid_to_prior_case_ * self.paid_w_ - paid_to_prior_case_naned.values = num_to_nan(paid_to_prior_case_naned.values) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + category=RuntimeWarning, + message=".*empty slice.*", + ) + warnings.filterwarnings( + "ignore", + category=RuntimeWarning, + message=".*All-NaN slice.*", + ) + case_to_prior_case_naned = self.case_to_prior_case_ * self.case_w_ + case_to_prior_case_naned.values = num_to_nan( + case_to_prior_case_naned.values + ) + self.case_ldf_ = case_to_prior_case_naned.mean(axis=2).fillna(1.0) - self.paid_ldf_ = self.paid_to_prior_case_.mean(2) # this has the wrong value - self.paid_ldf_.values = paid_to_prior_case_naned.mean(axis=2).values + paid_to_prior_case_naned = self.paid_to_prior_case_ * self.paid_w_ + paid_to_prior_case_naned.values = num_to_nan( + paid_to_prior_case_naned.values + ) + self.paid_ldf_ = paid_to_prior_case_naned.mean(axis=2).fillzero() self.ldf_ = self._set_ldf(self.X_).set_backend(backend) @@ -271,9 +283,8 @@ def case_to_prior_case_(self): paid_tri = paid_tri.groupby(self.groupby).sum() incurred_tri = incurred_tri.groupby(self.groupby).sum() - out = (incurred_tri - paid_tri).iloc[..., 1:] / (incurred_tri - paid_tri).iloc[ - ..., :-1 - ].values + prior_case = num_to_nan((incurred_tri - paid_tri).iloc[..., :-1].values) + out = (incurred_tri - paid_tri).iloc[..., 1:] / prior_case out.is_pattern = True out.is_cumulative = False @@ -288,10 +299,8 @@ def paid_to_prior_case_(self): paid_tri = paid_tri.groupby(self.groupby).sum() incurred_tri = incurred_tri.groupby(self.groupby).sum() - out = ( - paid_tri.cum_to_incr().iloc[..., 1:] - / (incurred_tri - paid_tri).iloc[..., :-1].values - ) + prior_case = num_to_nan((incurred_tri - paid_tri).iloc[..., :-1].values) + out = paid_tri.cum_to_incr().iloc[..., 1:] / prior_case out.is_pattern = True return out diff --git a/chainladder/development/tests/test_outstanding.py b/chainladder/development/tests/test_outstanding.py index 656c75c0f..0a3ecfb17 100644 --- a/chainladder/development/tests/test_outstanding.py +++ b/chainladder/development/tests/test_outstanding.py @@ -345,3 +345,33 @@ def test_unweighted_ratios_preserved_when_n_periods_set(): assert np.allclose( m.case_to_prior_case_.values, m_full.case_to_prior_case_.values, equal_nan=True ) + + +def test_case_outstanding_zero_case_reserve_warning(): + import warnings + + xyz = cl.load_sample("friedland_xyz_auto_bi") + with warnings.catch_warnings(): + warnings.filterwarnings("error", category=RuntimeWarning) + model = cl.CaseOutstanding( + paid_to_incurred=("Paid Claims", "Reported Claims") + ).fit(xyz) + assert model.paid_ldf_ is not None + assert not model.case_ldf_.to_frame().isna().any().any() + + +def test_case_outstanding_synthetic_zero_prior_case_warning(): + import warnings + + p = cl.load_sample("genins") + p.columns = ["paid"] + i = p.copy() + i.columns = ["incurred"] + i.values = p.values + 100 + # Force case to zero at earlier development maturity + i.values[..., 2] = p.values[..., 2] + tri = cl.concat((p, i), 1) + with warnings.catch_warnings(): + warnings.filterwarnings("error", category=RuntimeWarning) + m_zero = cl.CaseOutstanding(paid_to_incurred=("paid", "incurred")).fit(tri) + assert not m_zero.case_ldf_.to_frame().isna().any().any()