diff --git a/README.rst b/README.rst index cf2f4575..6483b448 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,8 @@ Available Estimators +------------------------------+------------------+-------------------------+-----------------------+-----------------------+ | `DevelopmentML`_ | | | | | +------------------------------+------------------+-------------------------+-----------------------+-----------------------+ +| `BarnettZehnwirth`_ | | | | | ++------------------------------+------------------+-------------------------+-----------------------+-----------------------+ Documentation ------------- @@ -91,6 +93,7 @@ code documentation. .. _CaseOutstanding: https://chainladder-python.readthedocs.io/en/latest/modules/development.html#caseoutstanding .. _TweedieGLM: https://chainladder-python.readthedocs.io/en/latest/modules/development.html#tweedieglm .. _DevelopmentML: https://chainladder-python.readthedocs.io/en/latest/modules/development.html#developmentml +.. _BarnettZehnwirth: https://chainladder-python.readthedocs.io/en/latest/modules/development.html#barnettzehnwirth .. _Documentation: https://chainladder-python.readthedocs.io/en/latest/ Getting Started Tutorials diff --git a/chainladder/core/triangle.py b/chainladder/core/triangle.py index 279436dd..05b71fa9 100644 --- a/chainladder/core/triangle.py +++ b/chainladder/core/triangle.py @@ -331,21 +331,23 @@ def dev_to_val(self, inplace=False): else: return self.copy() is_cumulative = self.is_cumulative - if self.is_ultimate: + if self.is_full: if is_cumulative: obj = self.cum_to_incr(inplace=inplace) else: obj = self.copy() - ultimate = obj.iloc[..., -1:] - obj = obj.iloc[..., :-1] + if self.is_ultimate: + ultimate = obj.iloc[..., -1:] + obj = obj.iloc[..., :-1] else: obj = self obj = obj._val_dev(1, inplace) ddims = obj.valuation[obj.valuation <= obj.valuation_date] obj.ddims = ddims.drop_duplicates().sort_values() - if self.is_ultimate: - ultimate.ddims = pd.DatetimeIndex(ultimate.valuation[0:1]) - obj = concat((obj, ultimate), -1) + if self.is_full: + if self.is_ultimate: + ultimate.ddims = pd.DatetimeIndex(ultimate.valuation[0:1]) + obj = concat((obj, ultimate), -1) if is_cumulative: obj = obj.incr_to_cum(inplace=inplace) return obj @@ -427,6 +429,10 @@ def grain(self, grain="", trailing=False, inplace=False): ) if valid["M"].index(ograin_new) > valid["M"].index(dgrain_new): raise ValueError("Origin grain must be coarser than development grain") + if self.is_full and not self.is_ultimate and not self.is_val_tri: + warnings.warn('Triangle includes extraneous development lags') + else: + d_limit = None obj = self.dev_to_val() if ograin_new != ograin_old: if trailing: diff --git a/chainladder/development/barnzehn.py b/chainladder/development/barnzehn.py index b71750f5..ba075980 100644 --- a/chainladder/development/barnzehn.py +++ b/chainladder/development/barnzehn.py @@ -38,7 +38,7 @@ def fit(self, X, y=None, sample_weight=None): response = X.columns[0] if not self.response else self.response self.model_ = DevelopmentML(Pipeline(steps=[ ('design_matrix', PatsyFormula(self.formula)), - ('model', LinearRegression(fit_intercept=True))]), + ('model', LinearRegression(fit_intercept=False))]), y_ml=response, fit_incrementals=False).fit(tri) resid = tri - self.model_.triangle_ml_[ self.model_.triangle_ml_.valuation <= tri.valuation_date] diff --git a/chainladder/development/base.py b/chainladder/development/base.py index 7db3883f..edfdc2b4 100644 --- a/chainladder/development/base.py +++ b/chainladder/development/base.py @@ -297,8 +297,9 @@ def fit(self, X, y=None, sample_weight=None): self.sigma_ = self._param_property(X, params, 1) self.std_err_ = self._param_property(X, params, 2) - resid = -X.iloc[..., :-1]*self.ldf_.values+X.iloc[..., 1:].values - std = xp.sqrt((1/w)*(self.sigma_**2).values) + resid = -X.iloc[..., :-1] * self.ldf_.values + X.iloc[..., 1:].values + + std = xp.sqrt((1/num_to_nan(w))*(self.sigma_**2).values) resid = resid/std self.std_residuals_ = resid[resid.valuation < X.valuation_date] return self diff --git a/chainladder/development/learning.py b/chainladder/development/learning.py index 1298aa86..a61b23c8 100644 --- a/chainladder/development/learning.py +++ b/chainladder/development/learning.py @@ -204,7 +204,8 @@ def transform(self, X): X_ml = self._prep_X_ml(X) y_ml=self.estimator_ml.predict(X_ml) triangle_ml = self._get_triangle_ml(X_ml, y_ml) - X_new.ldf_ = triangle_ml.incr_to_cum().link_ratio + backend = "cupy" if X.array_backend == "cupy" else "numpy" + X_new.ldf_ = triangle_ml.incr_to_cum().link_ratio.set_backend(backend) X_new.ldf_.valuation_date = pd.to_datetime(ULT_VAL) X_new._set_slicers() return X_new diff --git a/chainladder/development/outstanding.py b/chainladder/development/outstanding.py index 0c4b43f2..aef2aff0 100644 --- a/chainladder/development/outstanding.py +++ b/chainladder/development/outstanding.py @@ -66,6 +66,7 @@ def fit(self, X, y=None, sample_weight=None): self : object Returns the instance itself. """ + backend = "cupy" if X.array_backend == "cupy" else "numpy" self.X_ = X.copy() paid_tri = self.X_[self.paid_to_incurred[0]] incurred_tri = self.X_[self.paid_to_incurred[1]] @@ -104,7 +105,8 @@ def fit(self, X, y=None, sample_weight=None): dev.ddims = self.X_.link_ratio.ddims dev.is_pattern=True dev.is_cumulative=True - self.ldf_ = dev.cum_to_incr() + + self.ldf_ = dev.cum_to_incr().set_backend(backend) return self @property diff --git a/chainladder/methods/mack.py b/chainladder/methods/mack.py index 09ea7ab7..2d7da69f 100644 --- a/chainladder/methods/mack.py +++ b/chainladder/methods/mack.py @@ -141,7 +141,7 @@ def _mack_recursion(self, est, X=None): extend = X.ldf_.shape[-1] - X.shape[-1] + 1 ldf = X.ldf_.values[..., : len(X.ddims) - 1] tail = X.cdf_.values[..., -extend : -extend + 1] - ldf = X.ldf_.get_array_module().concatenate((ldf, tail), -1) + ldf = xp.array(X.ldf_.get_array_module().concatenate((ldf, tail), -1)) # Recursive Mack Formula for i in range(t1_t.shape[-1]): t1 = t1_t[..., i : i + 1] ** 2 diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 55eea84c..6888a632 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -8,8 +8,8 @@ class WeightedRegression(BaseEstimator): """ Helper class that fits a system of regression equations - simultaneously on a multi-dimensional array. Look into - SUR as a replacement. + as a closed-form solution. This greatly speeds up + the implementation of the Mack stochastic properties. """ def __init__(self, axis=None, thru_orig=False, xp=None):