def fit(self, X, y=None, sample_weight=None): if (type(X.ddims) != np.ndarray): raise ValueError( 'Triangle must be expressed with development lags') obj = copy.copy(X) self.w_ = X.nan_triangle() if not self.drop else self._drop(X) lag = {'M': 1, 'Q': 3, 'Y': 12}[X.development_grain] if type(self.drop) is not list and self.drop is not None: self.drop = [self.drop] drop = [(item[0], item[1] - lag) for item in self.drop] else: drop = self.drop obj = Development(n_periods=self.n_periods, drop=drop).fit_transform(obj) 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 = np.nan_to_num(exp_incr_triangle) * \ obj.X_.nan_triangle() self.design_matrix_ = self._get_design_matrix(X) self.hat_ = self._get_hat(X, exp_incr_triangle) self.resampled_triangles_, self.scale_ = \ self._get_simulation(X, exp_incr_triangle) n_obs = np.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 = np.sqrt(n_obs / deg_free) return self
def fit(self, X, y=None, sample_weight=None): obj = copy.deepcopy(X) obj = Development(n_periods=self.n_periods).fit_transform(obj) obj = Chainladder().fit(obj) # Works for only a single triangle - can we generalize this exp_incr_triangle = obj.full_expectation_ \ .cum_to_incr().triangle[0, 0, :, :X.shape[-1]] exp_incr_triangle = np.nan_to_num(exp_incr_triangle) * \ obj.X_.nan_triangle() self.design_matrix_ = self._get_design_matrix(X) self.hat_ = self._get_hat(X, exp_incr_triangle) self.resampled_triangles_, self.scale_ = \ self._get_simulation(X, exp_incr_triangle) 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): xp = cp.get_array_module(X.values) 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') obj = copy.copy(X) 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(obj) 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 = np.sqrt(n_obs / deg_free) return self
def fit(self, X, y=None, sample_weight=None): backend = X.array_backend if backend == 'sparse': obj = X.set_backend('numpy') else: obj = copy.deepcopy(X) xp = obj.get_array_module() if not (self.paid_amount in X.columns and self.incurred_amount in X.columns and self.reported_count in X.columns and self.closed_count in X.columns): raise ValueError( 'Must enter values valid columns for paid_amount, incurred_amount, reported_count and closed_count' ) paid_amount = self.paid_amount incurred_amount = self.incurred_amount reported_count = self.reported_count closed_count = self.closed_count reported_count_estimator = self.reported_count_estimator # Case reserve adequacy adjustment open_count = obj[reported_count] - obj[closed_count] avg_case = (obj[incurred_amount] - obj[paid_amount]) / open_count adj_avg_case = ( avg_case.trend(1 / (1 + self.trend) - 1, axis='valuation') / avg_case * avg_case[avg_case.valuation == avg_case.valuation_date].sum('origin')) adj_incurred_amount = adj_avg_case * open_count + obj[paid_amount] # Paid and closed claim adjustments if reported_count_estimator is None: reported_count_estimator = Chainladder() reported_count_estimator.fit(obj[reported_count]) if reported_count_estimator.__class__.__name__ == 'Pipeline': rep_cnt_ult = reported_count_estimator.named_steps[ reported_count_estimator.steps[-1][0]].ultimate_ else: rep_cnt_ult = reported_count_estimator.ultimate_ disposal_rate = (obj[closed_count] / rep_cnt_ult) adj_closed_clm = ((disposal_rate * 0 + 1) * disposal_rate[ disposal_rate.valuation == X.valuation_date].mean('origin') * rep_cnt_ult) adj_closed_clm.valuation_date = disposal_rate.valuation_date = X.valuation_date # Two-period exponential regression y = obj[paid_amount] x = obj[closed_count] x0 = x.values[..., :-1] x1 = x.values[..., 1:] y0 = xp.log(y.values[..., :-1]) y1 = xp.log(y.values[..., 1:]) b = ((x0*y0+x1*y1)/2-(x0+x1)/2*(y0+y1)/2) / \ ((x0**2+x1**2)/2-((x0+x1)/2)**2) a = np.exp((y0 + y1) / 2 - b * (x0 + x1) / 2) # Need to Ignore warnings for NaN cells with warnings.catch_warnings(): warnings.simplefilter("ignore") lookup = np.maximum( xp.concatenate( [(adj_closed_clm.values[..., i:i + 1] > obj[closed_count].values).sum(axis=-1, keepdims=True) for i in range(adj_closed_clm.shape[-1])], axis=-1), 1) - 1 a = xp.concatenate( [a[..., i, lookup[0, 0, i:i+1, :]] for i in range(lookup.shape[-2])], -2) * \ adj_closed_clm.nan_triangle[None,None,...] b = xp.concatenate( [b[..., i, lookup[0, 0, i:i+1, :]] for i in range(lookup.shape[-2])], -2) * \ adj_closed_clm.nan_triangle[None,None,...] # Adjust paids adj_paid_claims = adj_closed_clm * 0 + xp.exp( adj_closed_clm.values * b) * a adj_paid_claims = ( y[y.valuation == y.valuation_date] + adj_paid_claims[ adj_paid_claims.valuation < adj_paid_claims.valuation_date]) adjusted_triangle_ = copy.deepcopy(obj) adjusted_triangle_[paid_amount] = adj_paid_claims adjusted_triangle_[incurred_amount] = adj_incurred_amount adjusted_triangle_[closed_count] = adj_closed_clm adjusted_triangle_ = adjusted_triangle_[ adjusted_triangle_.valuation <= obj.valuation_date] self.adjusted_triangle_ = adjusted_triangle_.set_backend(backend) self.disposal_rate_ = disposal_rate.set_backend(backend) self.a_ = a self.b_ = b 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