def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle Set of LDFs to which the munich adjustment will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ backend = "cupy" if X.array_backend == "cupy" else "numpy" self.X_ = X.copy() self.paid_w_ = Development(n_periods=self.paid_n_periods).fit( self.X_.sum(0).sum(1)).w_ self.case_w_ = Development(n_periods=self.case_n_periods).fit( self.X_.sum(0).sum(1)).w_ self.case_ldf_ = self.case_to_prior_case_.mean(2) self.paid_ldf_ = self.paid_to_prior_case_.mean(2) self.ldf_ = self._set_ldf(self.X_).set_backend(backend) return self
def transform(self, X): """ 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 = copy.copy(X) if 'ldf_' not in X_new: X_new = Development().fit_transform(X_new) X_new.p_to_i_X_ = self._get_p_to_i_object(X_new) X_new.p_to_i_ldf_ = self._get_p_to_i_object(X_new.ldf_) X_new.p_to_i_sigma_ = self._get_p_to_i_object(X_new.sigma_) X_new.q_f_, X_new.rho_sigma_ = self._get_MCL_model(X_new) X_new.munich_full_triangle_ = self._get_munich_full_triangle_( X_new.p_to_i_X_, X_new.p_to_i_ldf_, X_new.p_to_i_sigma_, self.lambda_coef_, X_new.rho_sigma_, X_new.q_f_) X_new.ldf_ = self._set_ldf(X_new, self._get_mcl_cdf(X_new, X_new.munich_full_triangle_)) triangles = ['rho_', 'lambda_', 'lambda_coef_'] for item in triangles: setattr(X_new, item, getattr(self, item)) X_new._set_slicers() return X_new
def transform(self, X): if 'ldf_' not in X: X_new = Development().fit_transform(X) else: X_new = copy.deepcopy(X) xp = X_new.ldf_.get_array_module( ) if X_new.ldf_.array_backend != 'sparse' else np X_new.std_err_.values = xp.concatenate( (X_new.std_err_.values, self.std_err_.values[..., -1:]), -1) extend = self.cdf_.values[..., -self._ave_period[0] - 1:] * 0 + 1 X_new.cdf_.values = xp.concatenate((X_new.cdf_.values, extend), -1) X_new.cdf_.values = X_new.cdf_.values * \ self.cdf_.values[..., -self._ave_period[0]-1:-self._ave_period[0]] X_new.cdf_.values = xp.concatenate( (X_new.cdf_.values[..., :-1], self.cdf_.values[..., -1:]), axis=-1) X_new.ldf_.values = xp.concatenate( (X_new.ldf_.values, self.ldf_.values[..., -self._ave_period[0] - 1:]), -1) X_new.sigma_.values = xp.concatenate( (X_new.sigma_.values, self.sigma_.values[..., -1:]), -1) X_new.cdf_.ddims = X_new.ldf_.ddims = self.ldf_.ddims X_new.sigma_.ddims = X_new.std_err_.ddims = self.sigma_.ddims X_new.sigma_._set_slicers() X_new.ldf_._set_slicers() X_new.cdf_._set_slicers() X_new.std_err_._set_slicers() X_new._ave_period = self._ave_period X_new.tail_ = TailBase._tail_(X_new) return X_new
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ super().fit(X, y, sample_weight) xp = self.ldf_.get_array_module() tail = self.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 if xp.max(xp.array(self.tail)) != 1.0: sigma, std_err = self._get_tail_stats(obj) self.sigma_.values = xp.concatenate( (self.sigma_.values[..., :-1], sigma[..., -1:]), axis=-1) self.std_err_.values = xp.concatenate( (self.std_err_.values[..., :-1], std_err[..., -1:]), axis=-1) return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the munich adjustment will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ if (type(X.ddims) != np.ndarray): raise ValueError( 'Triangle must be expressed with development lags') obj = copy.deepcopy(X) if obj.__dict__.get('ldf_', None) is None: obj = Development().fit_transform(obj) self.p_to_i_X_ = self._get_p_to_i_object(obj) self.p_to_i_ldf_ = self._get_p_to_i_object(obj.ldf_) self.p_to_i_sigma_ = self._get_p_to_i_object(obj.sigma_) self.q_f_, self.rho_sigma_ = self._get_MCL_model(obj) self.residual_, self.q_resid_ = self._get_MCL_residuals(obj) self.lambda_coef_ = self._get_MCL_lambda() self.cdf_ = self._get_cdf(obj) return self
def predict(self, X, sample_weight=None): """Predicts the chainladder ultimate on a new triangle **X** Parameters ---------- X : Triangle The data used to compute the mean and standard deviation used for later scaling along the features axis. sample_weight : Triangle For exposure-based methods, the exposure to be used for predictions Returns ------- X_new: Triangle """ obj = copy.deepcopy(self) obj.X_ = copy.deepcopy(X) obj.sample_weight = sample_weight if np.unique(self.cdf_.values, axis=-2).shape[-2] == 1: obj.cdf_.values = np.repeat(np.unique(self.cdf_.values, axis=-2), len(X.odims), -2) obj.ldf_.values = np.repeat(np.unique(self.ldf_.values, axis=-2), len(X.odims), -2) obj.cdf_.odims = obj.ldf_.odims = obj.X_.odims obj.cdf_.valuation = obj.ldf_.valuation = \ Development().fit(X).cdf_.valuation return obj
def validate_X(self, X): obj = X.copy() if "ldf_" not in obj: obj = Development().fit_transform(obj) if len(obj.ddims) - len(obj.ldf_.ddims) == 1: obj = TailConstant().fit_transform(obj) return obj
def validate_X(self, X): obj = copy.deepcopy(X) if 'ldf_' not in obj: obj = Development().fit_transform(obj) if len(obj.ddims) - len(obj.ldf_.ddims) == 1: obj = TailConstant().fit_transform(obj) return obj
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ super().fit(X, y, sample_weight) tail = self.tail self = self._apply_decay(X, tail) 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: 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 fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the munich adjustment will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ from chainladder import ULT_VAL if self.paid_to_incurred is None: raise ValueError("Must enter valid value for paid_to_incurred.") if X.array_backend == "sparse": obj = X.set_backend("numpy") else: obj = X.copy() xp = obj.get_array_module() self.xp = xp missing = xp.nan_to_num(obj.values) * obj.nan_triangle == 0 self.rho_ = obj[obj.origin == obj.origin.min()] if len(xp.where(missing)[0]) > 0: if self.fillna: from chainladder.methods import Chainladder filler = Chainladder().fit(obj).full_expectation_ filler = filler[filler.valuation <= obj.valuation_date].values obj.values = xp.where(missing, filler, obj.values) else: raise ValueError( "MunichAdjustment cannot be performed when P/I or I/P " + "ratios cannot be computed. Use `fillna=True` to impute zero" + " values of the triangle with simple chainladder expectation." ) if "ldf_" not in obj: obj = Development().fit_transform(obj) self.p_to_i_X_ = self._get_p_to_i_object(obj) self.p_to_i_ldf_ = self._get_p_to_i_object(obj.ldf_) self.p_to_i_sigma_ = self._get_p_to_i_object(obj.sigma_) self.q_f_, self.rho_sigma_ = self._get_MCL_model(obj) self.residual_, self.q_resid_ = self._get_MCL_resids(obj) self.lambda_coef_ = self._get_MCL_lambda(obj) self.ldf_ = self._set_ldf( obj, self._get_mcl_cdf(obj, self.munich_full_triangle_)) self.ldf_.is_cumulative = False self.ldf_.valuation_date = pd.to_datetime(ULT_VAL) self._map = { (list(X.columns).index(x)): (num % 2, num // 2) for num, x in enumerate(np.array(self.paid_to_incurred).flatten()) } self.rho_.values = self._reshape("rho_sigma_") return self
def fit(self, X, y=None, sample_weight=None): obj = copy.copy(X) if 'ldf_'not in obj: obj = Development().fit_transform(obj) self._ave_period = {'Y': (1, 12), 'Q': (4, 3), 'M': (12, 1)}[obj.development_grain] ddims = np.concatenate( (obj.ddims, [(item+1)*self._ave_period[1] + obj.ddims[-1] for item in range(self._ave_period[0])], [9999]), 0) self.ldf_ = copy.copy(obj.ldf_) tail = np.ones(self.ldf_.shape)[..., -1:] tail = np.repeat(tail, self._ave_period[0]+1, -1) self.ldf_.values = np.concatenate((self.ldf_.values, tail), -1) self.ldf_.ddims = np.array(['{}-{}'.format(ddims[i], ddims[i+1]) for i in range(len(ddims)-1)]) self.ldf_.valuation = self.ldf_._valuation_triangle() self.sigma_ = copy.copy(getattr(obj, 'sigma_', obj.cdf_*0)) self.std_err_ = copy.copy(getattr(obj, 'std_err_', obj.cdf_*0)) zeros = tail[..., -1:]*0 self.sigma_.values = np.concatenate( (self.sigma_.values, zeros), -1) self.std_err_.values = np.concatenate( (self.std_err_.values, zeros), -1) self.sigma_.ddims = self.std_err_.ddims = \ np.append(obj.ldf_.ddims, ['{}-9999'.format(int(obj.ddims[-1]))]) val_array = self.sigma_._valuation_triangle(self.sigma_.ddims) self.sigma_.valuation = self.std_err_.valuation = val_array self.cdf_ = DevelopmentBase._get_cdf(self) self.cdf_.set_slicers() self.ldf_.set_slicers() self.sigma_.set_slicers() self.std_err_.set_slicers() return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ super().fit(X, y, sample_weight) xp = self.ldf_.get_array_module() tail = self.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 self._get_tail_stats(obj) return self
def validate_X(self, X): obj = copy.copy(X) if 'ldf_' not in obj: obj = Development().fit_transform(obj) if len(obj.ddims) - len(obj.ldf_.ddims) == 1: obj = TailConstant().fit_transform(obj) for item in ['cdf_', 'ldf_', 'average_']: setattr(self, item, getattr(obj, item, None)) return obj
def validate_X(self, X): obj = copy.deepcopy(X) if obj.__dict__.get('ldf_', None) is None: obj = Development().fit_transform(obj) if len(obj.ddims) - len(obj.ldf_.ddims) == 1: obj = TailConstant().fit_transform(obj) self.cdf_ = obj.__dict__.get('cdf_', None) self.ldf_ = obj.__dict__.get('ldf_', None) self.average_ = obj.__dict__.get('average_', None) return obj
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ 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 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') _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_ extrapolate = xp.cumsum( xp.ones(tuple(list(_y.shape)[:-1] + [self.extrap_periods + n_obs])), -1) tail = self._predict_tail(slope, intercept, extrapolate) if self.attachment_age: attach_idx = xp.min(xp.where(X.ddims >= self.attachment_age)) else: attach_idx = len(X.ddims) - 1 self.ldf_.values = xp.concatenate( (self.ldf_.values[..., :attach_idx], tail[..., attach_idx:]), -1) obj = Development().fit_transform(X) if 'ldf_' not in X else X 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
def fit(self, X, y=None, sample_weight=None): if X.array_backend == "sparse": obj = X.set_backend("numpy") xp = np else: xp = X.get_array_module() obj = X.copy() if "ldf_" not in obj: obj = Development().fit_transform(obj) self._ave_period = { "Y": (1, 12), "Q": (4, 3), "M": (12, 1) }[obj.development_grain] ddims = np.concatenate( ( obj.ddims, [(item + 1) * self._ave_period[1] + obj.ddims[-1] for item in range(self._ave_period[0])], [9999], ), 0, ) self.ldf_ = obj.ldf_.copy() tail = xp.ones(self.ldf_.shape)[..., -1:] tail = xp.repeat(tail, self._ave_period[0] + 1, -1) self.ldf_.values = xp.concatenate((self.ldf_.values, tail), -1) self.ldf_.ddims = np.array([ "{}-{}".format(ddims[i], ddims[i + 1]) for i in range(len(ddims) - 1) ]) if hasattr(obj, "sigma_"): self.sigma_ = getattr(obj, "sigma_").copy() else: self.sigma_ = obj.ldf_ - obj.ldf_ if hasattr(obj, "std_err_"): self.std_err_ = getattr(obj, "std_err_").copy() else: self.std_err_ = obj.ldf_ - obj.ldf_ zeros = tail[..., 0:1, -1:] * 0 self.sigma_.values = xp.concatenate((self.sigma_.values, zeros), -1) self.std_err_.values = xp.concatenate((self.std_err_.values, zeros), -1) self.sigma_.ddims = self.std_err_.ddims = np.concatenate( (obj.ldf_.ddims, np.array(["{}-9999".format(int(obj.ddims[-1]))]))) if hasattr(obj, "average_"): self.average_ = obj.average_ else: self.average_ = None self.ldf_._set_slicers() self.sigma_._set_slicers() self.std_err_._set_slicers() return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ super().fit(X, y, sample_weight) _y = self.ldf_.values[..., :-1].copy() _w = np.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 if self.errors == 'ignore': _w[_y <= 1.0] = 0 _y[_y <= 1.0] = 1.01 elif self.errors == 'raise' and np.any(y < 1.0): raise ZeroDivisionError('Tail fit requires all LDFs to be \ greater than 1.0') _y = np.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_ extrapolate = np.cumsum( np.ones(tuple(list(_y.shape)[:-1] + [self.extrap_periods])), -1) + n_obs tail = self._predict_tail(slope, intercept, extrapolate) self.ldf_.values = self.ldf_.values[..., :-tail.shape[-1]] self.ldf_.values = np.concatenate((self.ldf_.values, tail), -1) if X.__dict__.get('ldf_', None) is None: obj = Development().fit_transform(X) else: obj = X 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
def fit(self, X, y=None, sample_weight=None): backend = X.array_backend if backend == "sparse": X = X.set_backend("numpy") else: X = X.copy() xp = X.get_array_module() if X.shape[:2] != (1, 1): raise ValueError( "Only single index/column triangles are supported") if type(X.ddims) != np.ndarray: raise ValueError( "Triangle must be expressed with development lags") lag = {"M": 1, "Q": 3, "Y": 12}[X.development_grain] obj = Development( n_periods=self.n_periods, drop=self.drop, drop_high=self.drop_high, drop_low=self.drop_low, drop_valuation=self.drop_valuation, ).fit_transform(X) self.w_ = obj.w_ obj = Chainladder().fit(obj) # Works for only a single triangle - can we generalize this exp_incr_triangle = obj.full_expectation_.cum_to_incr().values[ 0, 0, :, :X.shape[-1]] exp_incr_triangle = xp.nan_to_num( exp_incr_triangle) * obj.X_.nan_triangle self.design_matrix_ = self._get_design_matrix(X) if self.hat_adj: try: self.hat_ = self._get_hat(X, exp_incr_triangle) except: warn("Could not compute hat matrix. Setting hat_adj to False") self.had_adj = False self.hat_ = None else: self.hat_ = None self.resampled_triangles_, self.scale_ = self._get_simulation( X, exp_incr_triangle) n_obs = xp.nansum(self.w_) n_origin_params = X.shape[2] n_dev_params = X.shape[3] - 1 deg_free = n_obs - n_origin_params - n_dev_params deg_free_adj_fctr = xp.sqrt(n_obs / deg_free) return self
def fit(self, X, y=None, sample_weight=None): if X.array_backend == 'sparse': obj = X.set_backend('numpy') xp = np else: xp = X.get_array_module() obj = copy.copy(X) if 'ldf_' not in obj: obj = Development().fit_transform(obj) self._ave_period = { 'Y': (1, 12), 'Q': (4, 3), 'M': (12, 1) }[obj.development_grain] ddims = np.concatenate( (obj.ddims, [(item + 1) * self._ave_period[1] + obj.ddims[-1] for item in range(self._ave_period[0])], [9999]), 0) self.ldf_ = copy.copy(obj.ldf_) tail = xp.ones(self.ldf_.shape)[..., -1:] tail = xp.repeat(tail, self._ave_period[0] + 1, -1) self.ldf_.values = xp.concatenate((self.ldf_.values, tail), -1) self.ldf_.ddims = np.array([ '{}-{}'.format(ddims[i], ddims[i + 1]) for i in range(len(ddims) - 1) ]) if hasattr(obj, 'sigma_'): self.sigma_ = copy.copy(getattr(obj, 'sigma_')) else: self.sigma_ = obj.ldf_ - obj.ldf_ if hasattr(obj, 'std_err_'): self.std_err_ = copy.copy(getattr(obj, 'std_err_')) else: self.std_err_ = obj.ldf_ - obj.ldf_ zeros = tail[..., 0:1, -1:] * 0 self.sigma_.values = xp.concatenate((self.sigma_.values, zeros), -1) self.std_err_.values = xp.concatenate((self.std_err_.values, zeros), -1) self.sigma_.ddims = self.std_err_.ddims = \ np.concatenate( (obj.ldf_.ddims, np.array(['{}-9999'.format(int(obj.ddims[-1]))]))) self.ldf_._set_slicers() self.sigma_._set_slicers() self.std_err_._set_slicers() return self
def fit_transform(self, X, y=None, sample_weight=None): """ Equivalent to fit(X).transform(X) Parameters ---------- X : Triangle-like Set of LDFs based on the model. y : Ignored sample_weight : Ignored Returns ------- X_new : New triangle with transformed attributes. """ self.fit(X) if 'std_err_' not in X: X = Development().fit_transform(X) return self.transform(X)
def fit(self, X, y=None, sample_weight=None): obj = X.copy() if "ldf_" not in obj: obj = Development().fit_transform(obj) xp = obj.ldf_.get_array_module() m = int(self.projection_period / 12) self._ave_period = { "Y": (1 * m, 12), "Q": (4 * m, 3), "M": (12 * m, 1) }[obj.development_grain] t_ddims = [(item + 1) * self._ave_period[1] + obj.ldf_.ddims[-1] for item in range(self._ave_period[0] + 1)] ddims = np.concatenate( (obj.ldf_.ddims, t_ddims), 0, ) self.ldf_ = obj.ldf_.copy() tail = xp.ones(self.ldf_.shape)[..., -1:] tail = xp.repeat(tail, self._ave_period[0] + 1, -1) self.ldf_.values = xp.concatenate((self.ldf_.values, tail), -1) self.ldf_.ddims = ddims if hasattr(obj, "sigma_"): zeros = (obj.sigma_.iloc[..., -1:] * 0).values self.sigma_ = getattr(obj, "sigma_").copy() self.sigma_.values = xp.concatenate((self.sigma_.values, zeros), -1) self.std_err_ = getattr(obj, "std_err_").copy() self.std_err_.values = xp.concatenate( (self.std_err_.values, zeros), -1) self.sigma_.ddims = self.std_err_.ddims = self.ldf_.ddims[:obj. shape[2]] self.sigma_._set_slicers() self.std_err_._set_slicers() if hasattr(obj, "average_"): self.average_ = obj.average_ else: self.average_ = None self.ldf_._set_slicers() return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the munich adjustment will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ obj = copy.deepcopy(X) xp = cp.get_array_module(obj.values) if 'ldf_' not in obj: obj = Development().fit_transform(obj) self.p_to_i_X_ = self._get_p_to_i_object(obj) self.p_to_i_ldf_ = self._get_p_to_i_object(obj.ldf_) self.p_to_i_sigma_ = self._get_p_to_i_object(obj.sigma_) self.q_f_, self.rho_sigma_ = self._get_MCL_model(obj) self.residual_, self.q_resid_ = self._get_MCL_resids(obj) self.lambda_coef_ = self._get_MCL_lambda() self.cdf_ = self._get_cdf(obj) self.ldf_ = self._set_ldf(X) self._map = { (list(X.columns).index(x)): (num % 2, num // 2) for num, x in enumerate(np.array(self.paid_to_incurred).flatten()) } self.rho_ = copy.deepcopy(X) self.rho_.odims = ['(All)'] self.rho_.values = self._reshape('rho_sigma_') return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ super().fit(X, y, sample_weight) xp = cp.get_array_module(X.values) obj = Development().fit_transform(X) if 'ldf_' not in X else X b_optimized = [] initial = xp.where( obj.ddims == self.earliest_age)[0][0] if self.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:]) b_optimized.append( least_squares(TailBondy.solver, x0=b0, kwargs={ 'data': data }).x[:, None]) self.b_ = xp.concatenate(b_optimized, 1) tail = xp.exp(xp.log(obj.ldf_.values[..., 0:1, initial:initial+1]) * \ self.b_**(len(obj.ldf_.ddims)-1)) tail = (tail**(self.b_ / (1 - self.b_))) * tail 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] return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Triangle to which the incremental method is applied. Triangle must be cumulative. y : None Ignored sample_weight : Exposure used in the method. Returns ------- self : object Returns the instance itself. """ from chainladder.utils.utility_functions import num_to_nan if type(X.ddims) != np.ndarray: raise ValueError("Triangle must be expressed with development lags") if X.array_backend == "sparse": X = X.set_backend("numpy") else: X = X.copy() if sample_weight.array_backend == "sparse": sample_weight = sample_weight.set_backend("numpy") else: sample_weight = sample_weight.copy() xp = X.get_array_module() sample_weight.is_cumulative = False obj = X.cum_to_incr() / sample_weight.values if hasattr(X, "trend_"): if self.trend != 0: warnings.warn( "IncrementalAdditive Trend assumption is ignored when X has a trend_ property." ) x = obj * obj.trend_.values else: x = obj.trend(self.trend, axis='valuation') w_ = Development( n_periods=self.n_periods - 1, drop=self.drop, drop_high=self.drop_high, drop_low=self.drop_low, drop_valuation=self.drop_valuation).fit(x).w_ # This will miss drops on the latest diagonal w_ = num_to_nan(w_) w_ = xp.concatenate((w_, (w_[..., -1:] * x.nan_triangle)[..., -1:]), axis=-1) if self.average == "simple": y_ = xp.nanmean(w_ * x.values, axis=-2) if self.average == "volume": y_ = xp.nansum(w_ * x.values * sample_weight.values, axis=-2) y_ = y_ / xp.nansum(w_ * sample_weight.values, axis=-2) self.zeta_ = X.iloc[..., -1:, :] self.zeta_.values = y_[:, :, None, :] y_ = xp.repeat(y_[..., None, :], len(x.odims), -2) obj = x.copy() keeps = ( 1 - xp.nan_to_num(x.nan_triangle) + xp.nan_to_num( x[x.valuation == x.valuation_date].values[0, 0, ...] * 0 + 1 ) ) obj.values = y_ * keeps obj.valuation_date = obj.valuation.max() obj.values = obj.values * (1 - xp.nan_to_num(x.nan_triangle)) + xp.nan_to_num( (X.cum_to_incr().values / sample_weight.values) ) obj.values[obj.values == 0] = xp.nan obj._set_slicers() obj.valuation_date = obj.valuation.max() future_trend = self.trend if not self.future_trend else self.future_trend self.incremental_ = obj * sample_weight.values self.incremental_ = self.incremental_.trend( 1/(1+future_trend)-1, axis='valuation', start=X.valuation_date, end=self.incremental_.valuation_date) self.ldf_ = obj.incr_to_cum().link_ratio return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- 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)[:, 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.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_))) 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.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) 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 fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- 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 = None if self.fit_period[0] is None else int( self.fit_period[0] / grain - 1) end = None 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) _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') _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) 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(extrapolate) if self.attachment_age: attach_idx = xp.min(xp.where(X.ddims >= self.attachment_age)) else: attach_idx = len(X.ddims) - 1 self.ldf_.values = xp.concatenate( (self.ldf_.values[..., :attach_idx], tail[..., attach_idx:]), -1) obj = Development().fit_transform(X) if 'ldf_' not in X else X 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): """ 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. """ backend = X.array_backend if backend == 'sparse': X_new = X.set_backend('numpy') else: X_new = copy.deepcopy(X) xp = X_new.get_array_module() self.xp = xp if 'ldf_' not in X_new: X_new = Development().fit_transform(X_new) self.xp = X_new.get_array_module() X_new.p_to_i_X_ = self._get_p_to_i_object(X_new) X_new.p_to_i_ldf_ = self._get_p_to_i_object(X_new.ldf_) X_new.p_to_i_sigma_ = self._get_p_to_i_object(X_new.sigma_) X_new.q_f_, X_new.rho_sigma_ = self._get_MCL_model(X_new) X_new.munich_full_triangle_ = self._get_munich_full_triangle_( X_new.p_to_i_X_, X_new.p_to_i_ldf_, X_new.p_to_i_sigma_, self.lambda_coef_, X_new.rho_sigma_, X_new.q_f_) X_new.ldf_ = self._set_ldf(X_new, self._get_mcl_cdf(X_new, X_new.munich_full_triangle_)) del self.xp triangles = ['rho_', 'lambda_', 'lambda_coef_'] for item in triangles: setattr(X_new, item, getattr(self, item)) X_new._set_slicers() return X_new
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- 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.") backend = X.array_backend if X.array_backend == "cupy": X = X.set_backend("numpy", deep=True) else: X = X.set_backend("numpy") xp = X.get_array_module() super().fit(X, y, sample_weight) if self.earliest_age is None: earliest_age = X.ddims[-2] 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]) * 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.flatten(), kwargs={ "data": data, "xp": xp }).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_)) 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) rows = X.index.set_index(X.key_labels).index self.b_ = pd.DataFrame(self.b_[..., 0, 0], index=rows, columns=X.vdims) self.earliest_ldf_ = pd.DataFrame(self.earliest_ldf_[..., 0, 0], index=rows, columns=X.vdims) self.ldf_.values = xp.concatenate( ( self.ldf_.values[..., :sum(X.ddims <= attachment_age)], fitted[..., -sum(X.ddims >= attachment_age):], ), axis=-1, ) self._get_tail_stats(obj) if backend == "cupy": self = self.set_backend(backend, inplace=True, deep=True) return self
def fit(self, X, y=None, sample_weight=None): """Fit the model with X. Parameters ---------- X : Triangle-like Set of LDFs to which the tail will be applied. y : Ignored sample_weight : Ignored Returns ------- self : object Returns the instance itself. """ from chainladder.utils.utility_functions import num_to_nan if X.array_backend == "sparse": X = X.set_backend("numpy") else: X = X.copy() xp = X.get_array_module() 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 = (None if self.fit_period[0] is None else int(self.fit_period[0] / grain - 1)) end = (None 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 = self.ldf_.get_array_module() _y = self.ldf_.values[..., :X.shape[-1] - 1].copy() _w = xp.zeros(_y.shape) _w[..., fit_period] = 1.0 if self.reg_threshold[0] is None: warnings.warn("Lower threshold for ldfs not set. Lower threshold will be set to 1.0 to ensure" \ "valid inputs for regression.") lower_threshold = 1 elif self.reg_threshold[0] < 1: warnings.warn("Lower threshold for ldfs set too low (<1). Lower threshold will be set to 1.0 to ensure" \ "valid inputs for regression.") lower_threshold = 1 else: lower_threshold = self.reg_threshold[0] if self.reg_threshold[1] is not None: if self.reg_threshold[1] <= lower_threshold: warnings.warn( "Can't set upper threshold for ldfs below lower threshold. Upper threshold will be set to 'None'." ) upper_threshold = None else: upper_threshold = self.reg_threshold[1] else: upper_threshold = self.reg_threshold[1] if self.errors == "ignore": if upper_threshold is None: _w[_y <= lower_threshold] = 0 _y[_y <= lower_threshold] = 1.01 else: _w[(_y <= lower_threshold) | (_y > upper_threshold)] = 0 _y[(_y <= lower_threshold) | (_y > upper_threshold)] = 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") _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, xp=xp).fit(_x, _y, _w) 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(extrapolate) if self.attachment_age: attach_idx = xp.min(xp.where(X.ddims >= self.attachment_age)) else: attach_idx = len(X.ddims) - 1 self.ldf_.values = xp.concatenate( (self.ldf_.values[..., :attach_idx], tail[..., attach_idx:]), -1) obj = Development().fit_transform(X) if "ldf_" not in X else X self._get_tail_stats(obj) return self
def fit(self, X, y=None, sample_weight=None): if X.shape[1] > 1: from chainladder.utils.utility_functions import concat out = [ BootstrapODPSample(**self.get_params()).fit(X.iloc[:, i]) for i in range(X.shape[1]) ] xp = X.get_array_module(out[0].design_matrix_) self.design_matrix_ = xp.concatenate( [i.design_matrix_[None] for i in out], axis=0) self.hat_ = xp.concatenate([i.hat_[None] for i in out], axis=0) self.resampled_triangles_ = concat( [i.resampled_triangles_ for i in out], axis=1) self.scale_ = xp.array([i.scale_ for i in out]) self.w_ = out[0].w_ else: backend = X.array_backend if backend == "sparse": X = X.set_backend("numpy") else: X = X.copy() xp = X.get_array_module() if len(X) != 1: raise ValueError("Only single index triangles are supported") if type(X.ddims) != np.ndarray: raise ValueError( "Triangle must be expressed with development lags") lag = {"M": 1, "Q": 3, "Y": 12}[X.development_grain] obj = Development( n_periods=self.n_periods, drop=self.drop, drop_high=self.drop_high, drop_low=self.drop_low, drop_valuation=self.drop_valuation, ).fit_transform(X) self.w_ = obj.w_ obj = Chainladder().fit(obj) # Works for only a single triangle - can we generalize this exp_incr_triangle = obj.full_expectation_.cum_to_incr().values[ 0, 0, :, :X.shape[-1]] exp_incr_triangle = xp.nan_to_num( exp_incr_triangle) * obj.X_.nan_triangle self.design_matrix_ = self._get_design_matrix(X) if self.hat_adj: try: self.hat_ = self._get_hat(X, exp_incr_triangle) except: warn( "Could not compute hat matrix. Setting hat_adj to False" ) self.had_adj = False self.hat_ = None else: self.hat_ = None self.resampled_triangles_, self.scale_ = self._get_simulation( X, exp_incr_triangle) n_obs = xp.nansum(self.w_) n_origin_params = X.shape[2] n_dev_params = X.shape[3] - 1 deg_free = n_obs - n_origin_params - n_dev_params deg_free_adj_fctr = xp.sqrt(n_obs / deg_free) return self