Skip to content

Commit

Permalink
aligned with pandas on the .values property
Browse files Browse the repository at this point in the history
  • Loading branch information
jbogaardt committed Feb 23, 2019
1 parent c87fbd2 commit 6450022
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 212 deletions.
146 changes: 74 additions & 72 deletions chainladder/core/base.py

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions chainladder/core/tests/test_triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ def test_slice_by_loc_iloc():


def test_link_ratio():
np.testing.assert_allclose(cl.load_dataset('RAA').link_ratio.triangle*cl.load_dataset('RAA').triangle[:,:,:-1,:-1],
cl.load_dataset('RAA').triangle[:,:,:-1,1:], atol=1e-5)
np.testing.assert_allclose(cl.load_dataset('RAA').link_ratio.values*cl.load_dataset('RAA').values[:,:,:-1,:-1],
cl.load_dataset('RAA').values[:,:,:-1,1:], atol=1e-5)


def test_incr_to_cum():
np.testing.assert_equal(tri.cum_to_incr().incr_to_cum().triangle, tri.triangle)
np.testing.assert_equal(tri.cum_to_incr().incr_to_cum().values, tri.values)


def test_create_new_value():
Expand All @@ -54,8 +54,8 @@ def test_multilevel_index_groupby_sum2():


def test_boolean_groupby_eq_groupby_loc():
np.testing.assert_equal(tri[tri['LOB']=='ppauto'].sum().triangle,
tri.groupby('LOB').sum().loc['ppauto'].triangle)
np.testing.assert_equal(tri[tri['LOB']=='ppauto'].sum().values,
tri.groupby('LOB').sum().loc['ppauto'].values)


def test_latest_diagonal_two_routes():
Expand All @@ -72,10 +72,10 @@ def test_append():

def test_arithmetic_across_keys():
x = cl.load_dataset('auto')
np.testing.assert_equal((x.sum()-x.iloc[0]).triangle, x.iloc[1].triangle)
np.testing.assert_equal((x.sum()-x.iloc[0]).values, x.iloc[1].values)

def test_grain():
actual = qtr.iloc[0,0].grain('OYDY').triangle[0,0,:,:]
actual = qtr.iloc[0,0].grain('OYDY').values[0,0,:,:]
expected = np.array([[ 44., 621., 950., 1020., 1070., 1069., 1089., 1094., 1097.,
1099., 1100., 1100.],
[ 42., 541., 1052., 1169., 1238., 1249., 1266., 1269., 1296.,
Expand Down Expand Up @@ -129,12 +129,12 @@ def test_arithmetic_2():

def test_shift():
x = cl.load_dataset('quarterly').iloc[0,0]
np.testing.assert_equal(x[x.valuation<=x.valuation_date].triangle, x.triangle)
np.testing.assert_equal(x[x.valuation<=x.valuation_date].values, x.values)

def test_quantile_vs_median():
clrd = cl.load_dataset('clrd')
np.testing.assert_equal(clrd.quantile(.5)['CumPaidLoss'].triangle,
clrd.median()['CumPaidLoss'].triangle)
np.testing.assert_equal(clrd.quantile(.5)['CumPaidLoss'].values,
clrd.median()['CumPaidLoss'].values)


def test_grain_returns_valid_tri():
Expand Down
16 changes: 8 additions & 8 deletions chainladder/development/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def cdf_(self):
return
else:
obj = copy.deepcopy(self.ldf_)
cdf_ = np.flip(np.cumprod(np.flip(obj.triangle, -1), -1), -1)
obj.triangle = cdf_
cdf_ = np.flip(np.cumprod(np.flip(obj.values, -1), -1), -1)
obj.values = cdf_
return obj


Expand Down Expand Up @@ -77,8 +77,8 @@ def _assign_n_periods_weight(self, X):
if type(self.n_periods) is int:
return self._assign_n_periods_weight_int(X, self.n_periods)[..., :-1]
elif type(self.n_periods) is list:
if len(self.n_periods) != X.triangle.shape[-1]-1:
raise ValueError('n_periods list must be of lenth {}.'.format(X.triangle.shape[-1]-1))
if len(self.n_periods) != X.values.shape[-1]-1:
raise ValueError('n_periods list must be of lenth {}.'.format(X.values.shape[-1]-1))
else:
return self._assign_n_periods_weight_list(X)
else:
Expand All @@ -96,9 +96,9 @@ def _assign_n_periods_weight_int(self, X, n_periods):
Only works for type(n_periods) == int
'''
if n_periods < 1 or n_periods >= X.shape[-2] - 1:
return X.triangle*0+1
return X.values*0+1
else:
flip_nan = np.nan_to_num(X.triangle*0+1)
flip_nan = np.nan_to_num(X.values*0+1)
k, v, o, d = flip_nan.shape
w = np.concatenate((1-flip_nan[..., -(o-n_periods-1):, :],
np.ones((k, v, n_periods+1, d))), 2)*flip_nan
Expand All @@ -120,7 +120,7 @@ def fit(self, X, y=None, sample_weight=None):
Returns the instance itself.
"""

tri_array = X.triangle.copy()
tri_array = X.values.copy()
tri_array[tri_array == 0] = np.nan
if type(self.average) is str:
average = [self.average] * (tri_array.shape[-1] - 1)
Expand Down Expand Up @@ -182,7 +182,7 @@ def transform(self, X):

def _param_property(self, X, params, idx):
obj = copy.deepcopy(X)
obj.triangle = np.ones(X.shape)[..., :-1]*params[..., idx:idx+1, :]
obj.values = np.ones(X.shape)[..., :-1]*params[..., idx:idx+1, :]
obj.ddims = X.link_ratio.ddims
obj.valuation = obj._valuation_triangle(obj.ddims)
obj.nan_override = True
Expand Down
8 changes: 4 additions & 4 deletions chainladder/development/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def fit(self, X, y=None, sample_weight=None):
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]]
.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)
Expand All @@ -71,7 +71,7 @@ def _get_simulation(self, X, exp_incr_triangle):
raise ValueError('Only single index/column triangles are ',
'supported')
unscaled_residuals = \
((X.cum_to_incr().triangle - exp_incr_triangle) /
((X.cum_to_incr().values - exp_incr_triangle) /
np.sqrt(np.abs(exp_incr_triangle**k_value)))[0, 0, ...]
standardized_residuals = self.hat_ * unscaled_residuals
pearson_chi_sq = sum(sum(np.nan_to_num(unscaled_residuals)**2))
Expand Down Expand Up @@ -100,7 +100,7 @@ def _get_simulation(self, X, exp_incr_triangle):
resampled_triangles = np.swapaxes(np.expand_dims(resampled_triangles, 0), 0, 1)
obj = copy.deepcopy(X)
obj.kdims = np.arange(self.n_sims)
obj.triangle = resampled_triangles
obj.values = resampled_triangles
return obj, scale_phi

# Shapland cites Verral and England 2002 in using gamma as a proxy for
Expand All @@ -110,7 +110,7 @@ def _get_simulation(self, X, exp_incr_triangle):
# Process variance adjustment
#lower_diagonal = (obj.nan_triangle()*0)
#lower_diagonal[np.isnan(lower_diagonal)]=1
#obj.triangle = Chainladder().fit(Development().fit_transform(obj)).full_expectation_.triangle[...,:-1]*lower_diagonal
#obj.values = Chainladder().fit(Development().fit_transform(obj)).full_expectation_.values[...,:-1]*lower_diagonal
#obj.nan_override = True
#if self.process_dist == 'gamma':
# process_triangle = np.nan_to_num(np.array([random_state.gamma(shape=abs(item/scale_phi),scale=scale_phi)*np.sign(np.nan_to_num(item)) for item in sim_exp_incr_triangle]))
Expand Down
16 changes: 8 additions & 8 deletions chainladder/development/incremental.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,20 @@ def fit(self, X, y=None, sample_weight=None):
w_ = np.concatenate((w_, (w_[..., -1:]*x.nan_triangle())[..., -1:]),
axis=-1)
if self.average == 'simple':
y_ = np.nanmean(w_*x.triangle, axis=-2)
y_ = np.nanmean(w_*x.values, axis=-2)
if self.average == 'volume':
y_ = np.nansum(w_*x.triangle*sample_weight.triangle, axis=-2)
y_ = y_ / np.nansum(w_*sample_weight.triangle, axis=-2)
y_ = np.nansum(w_*x.values*sample_weight.values, axis=-2)
y_ = y_ / np.nansum(w_*sample_weight.values, axis=-2)
y_ = np.repeat(np.expand_dims(y_, -2), len(x.odims), -2)
obj = copy.deepcopy(x)
keeps = 1-np.nan_to_num(x.nan_triangle()) + \
np.nan_to_num(x.get_latest_diagonal(compress=False).triangle[0,0,...]*0+1)
np.nan_to_num(x.get_latest_diagonal(compress=False).values[0,0,...]*0+1)

obj.triangle = (1+self.trend)**np.flip((np.abs(np.expand_dims(np.arange(obj.shape[-2]), 0).T -
obj.values = (1+self.trend)**np.flip((np.abs(np.expand_dims(np.arange(obj.shape[-2]), 0).T -
np.expand_dims(np.arange(obj.shape[-2]), 0))),0)*y_*keeps
obj.triangle = obj.triangle*(x.expand_dims(1-np.nan_to_num(x.nan_triangle()))) + \
np.nan_to_num((X.cum_to_incr()/sample_weight).triangle)
obj.triangle[obj.triangle == 0] = np.nan
obj.values = obj.values*(x.expand_dims(1-np.nan_to_num(x.nan_triangle()))) + \
np.nan_to_num((X.cum_to_incr()/sample_weight).values)
obj.values[obj.values == 0] = np.nan
obj.nan_override = True
self.incremental_ = obj*sample_weight
self.ldf_ = obj.incr_to_cum().link_ratio
Expand Down
14 changes: 7 additions & 7 deletions chainladder/development/munich.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ def _get_p_to_i_object(self, obj):
incurred = obj[list(self.paid_to_incurred.values())[0]]
for item in list(self.paid_to_incurred.values())[1:]:
incurred[item] = obj[item]
paid = np.expand_dims(paid.triangle, 0)
incurred = np.expand_dims(incurred.triangle, 0)
paid = np.expand_dims(paid.values, 0)
incurred = np.expand_dims(incurred.values, 0)
return np.concatenate((paid, incurred), axis=0)

def _p_to_i_concate(self, obj_p, obj_i):
Expand Down Expand Up @@ -123,7 +123,7 @@ def _get_MCL_residuals(self, X):
p_to_i_sigma[0]*np.sqrt(paid[..., :-1, :-1])
residualI = (p_to_i_ata[1]-p_to_i_ldf[1]) / \
p_to_i_sigma[1]*np.sqrt(incurred[..., :-1, :-1])
nans = (X-X[X.valuation == X.valuation_date]).triangle[0, 0]*0+1
nans = (X-X[X.valuation == X.valuation_date]).values[0, 0]*0+1
q_resid = (paid/incurred - self.q_f_[1]) / \
self.rho_sigma_[1]*np.sqrt(incurred)*nans
q_inv_resid = (incurred/paid - 1/self.q_f_[1]) / \
Expand Down Expand Up @@ -185,19 +185,19 @@ def _get_cdf(self, X):
paid = list(self.paid_to_incurred.keys())
for n, item in enumerate(paid):
idx = np.where(X.cdf_.vdims == item)[0][0]
obj.triangle[:, idx:idx+1, ...] = cdf_triangle[0, :, n:n+1, ...]
obj.values[:, idx:idx+1, ...] = cdf_triangle[0, :, n:n+1, ...]
incurred = list(self.paid_to_incurred.values())
for n, item in enumerate(incurred):
idx = np.where(X.cdf_.vdims == item)[0][0]
obj.triangle[:, idx:idx+1, ...] = cdf_triangle[1, :, n:n+1, ...]
obj.values[:, idx:idx+1, ...] = cdf_triangle[1, :, n:n+1, ...]
obj.nan_override = True
return obj

@property
def ldf_(self):
ldf_tri = self.cdf_.triangle.copy()
ldf_tri = self.cdf_.values.copy()
ldf_tri = np.concatenate((ldf_tri, np.ones(ldf_tri.shape)[..., -1:]), -1)
ldf_tri = ldf_tri[..., :-1]/ldf_tri[..., 1:]
obj = copy.deepcopy(self.cdf_)
obj.triangle = ldf_tri
obj.values = ldf_tri
return obj
2 changes: 1 addition & 1 deletion chainladder/development/tests/test_boostrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ def test_bs_sample():
tri = cl.load_dataset('raa')
a = cl.Development().fit(cl.BootstrapODPSample(n_sims=40000).fit_transform(tri).mean()).ldf_
b = cl.Development().fit_transform(tri).ldf_
assert np.all(abs(((a-b)/b).triangle)<.005)
assert np.all(abs(((a-b)/b).values)<.005)
8 changes: 4 additions & 4 deletions chainladder/development/tests/test_development.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_full_slice2():
def test_n_periods():
d = cl.load_dataset('usauto')['incurred']
return np.all(np.round(np.unique(
cl.Development(n_periods=3, average='volume').fit(d).ldf_.triangle,
cl.Development(n_periods=3, average='volume').fit(d).ldf_.values,
axis=-2), 3).flatten() ==
np.array([1.164, 1.056, 1.027, 1.012, 1.005, 1.003, 1.002, 1.001, 1.0]))

Expand All @@ -48,7 +48,7 @@ def test_n_periods():
@pytest.mark.parametrize('est_sigma', est_sigma)
def test_mack_ldf(data, averages, est_sigma, atol):
r = np.array(mack_r(data, averages[1], est_sigma[1]).rx('f'))[:, :-1]
p = mack_p(data, averages[0], est_sigma[0]).ldf_.triangle[0, 0, :, :]
p = mack_p(data, averages[0], est_sigma[0]).ldf_.values[0, 0, :, :]
p = np.unique(p, axis=-2)
assert_allclose(r, p, atol=atol)

Expand All @@ -58,7 +58,7 @@ def test_mack_ldf(data, averages, est_sigma, atol):
@pytest.mark.parametrize('est_sigma', est_sigma)
def test_mack_sigma(data, averages, est_sigma, atol):
r = np.array(mack_r(data, averages[1], est_sigma[1]).rx('sigma'))
p = mack_p(data, averages[0], est_sigma[0]).sigma_.triangle[0, 0, :, :]
p = mack_p(data, averages[0], est_sigma[0]).sigma_.values[0, 0, :, :]
p = np.unique(p, axis=-2)
assert_allclose(r, p, atol=atol)

Expand All @@ -68,6 +68,6 @@ def test_mack_sigma(data, averages, est_sigma, atol):
@pytest.mark.parametrize('est_sigma', est_sigma)
def test_mack_std_err(data, averages, est_sigma, atol):
r = np.array(mack_r(data, averages[1], est_sigma[1]).rx('f.se'))
p = mack_p(data, averages[0], est_sigma[0]).std_err_.triangle[0, 0, :, :]
p = mack_p(data, averages[0], est_sigma[0]).std_err_.values[0, 0, :, :]
p = np.unique(p, axis=-2)
assert_allclose(r, p, atol=atol)
2 changes: 1 addition & 1 deletion chainladder/development/tests/test_incremental.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def test_schmidt():
ia = cl.IncrementalAdditive()
answer = ia.fit_transform(tri.iloc[0, 1],
sample_weight=tri.iloc[0, 0].latest_diagonal)
answer = answer.incremental_.incr_to_cum().triangle[0, 0, :, -1]
answer = answer.incremental_.incr_to_cum().values[0, 0, :, -1]
check = np.array([3483., 4007.84795031, 4654.36196862, 5492.00685523,
6198.10197128, 7152.82539296])
assert_allclose(answer, check, atol=1e-5)
20 changes: 10 additions & 10 deletions chainladder/methods/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def predict(self, X):
@property
def full_expectation_(self):
obj = copy.deepcopy(self.X_)
obj.triangle = self.ultimate_.triangle / self.cdf_.triangle
obj.triangle = \
np.concatenate((obj.triangle, self.ultimate_.triangle), -1)
obj.values = self.ultimate_.values / self.cdf_.values
obj.values = \
np.concatenate((obj.values, self.ultimate_.values), -1)
ddims = [int(item[item.find('-')+1:]) for item in self.cdf_.ddims]
obj.ddims = np.array([obj.ddims[0]]+ddims)
obj.valuation = obj._valuation_triangle(obj.ddims)
Expand All @@ -73,7 +73,7 @@ def full_expectation_(self):
@property
def ibnr_(self):
obj = copy.deepcopy(self.ultimate_)
obj.triangle = self.ultimate_.triangle-self.X_.latest_diagonal.triangle
obj.values = self.ultimate_.values-self.X_.latest_diagonal.values
obj.ddims = ['IBNR']
return obj

Expand All @@ -84,15 +84,15 @@ def _get_full_triangle_(self):
ones = np.ones((w.shape[-2], extend))
w = np.concatenate((w, ones), -1)
obj.nan_override = True
e_tri = np.repeat(self.ultimate_.triangle,
self.cdf_.triangle.shape[3], 3)/self.cdf_.triangle
e_tri = np.repeat(self.ultimate_.values,
self.cdf_.values.shape[3], 3)/self.cdf_.values
e_tri = e_tri * w
zeros = obj.expand_dims(ones - ones)
properties = self.full_expectation_
obj.valuation = properties.valuation
obj.ddims = properties.ddims
obj.triangle = \
np.concatenate((np.nan_to_num(obj.triangle), zeros), -1) + e_tri
obj.triangle = np.concatenate((obj.triangle,
self.ultimate_.triangle), 3)
obj.values = \
np.concatenate((np.nan_to_num(obj.values), zeros), -1) + e_tri
obj.values = np.concatenate((obj.values,
self.ultimate_.values), 3)
return obj
12 changes: 6 additions & 6 deletions chainladder/methods/benktander.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ def fit(self, X, y=None, sample_weight=None):

super().fit(X, y, sample_weight)
self.sample_weight_ = sample_weight
latest = self.X_.latest_diagonal.triangle
apriori = sample_weight.triangle * self.apriori
latest = self.X_.latest_diagonal.values
apriori = sample_weight.values * self.apriori
obj = copy.deepcopy(self.X_)
obj.triangle = \
self.X_.cdf_.triangle[..., :obj.shape[-1]]*(obj.triangle*0+1)
cdf = obj.latest_diagonal.triangle
obj.values = \
self.X_.cdf_.values[..., :obj.shape[-1]]*(obj.values*0+1)
cdf = obj.latest_diagonal.values
cdf = np.expand_dims(1-1/cdf, 0)
exponents = np.arange(self.n_iters+1)
exponents = np.reshape(exponents, tuple([len(exponents)]+[1]*4))
cdf = cdf**exponents
obj.triangle = np.sum(cdf[:-1, ...], 0)*latest+cdf[-1, ...]*apriori
obj.values = np.sum(cdf[:-1, ...], 0)*latest+cdf[-1, ...]*apriori
obj.ddims = ['Ultimate']
obj.valuation = pd.DatetimeIndex([pd.to_datetime('2262-04-11')]*obj.shape[-2])
self.ultimate_ = obj
Expand Down
16 changes: 8 additions & 8 deletions chainladder/methods/capecod.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ def fit(self, X, y=None, sample_weight=None):
"""
super().fit(X, y, sample_weight)
self.sample_weight_ = sample_weight
latest = self.X_.latest_diagonal.triangle
latest = self.X_.latest_diagonal.values
obj = copy.deepcopy(self.X_)
obj.triangle = \
self.X_.cdf_.triangle[..., :obj.shape[-1]]*(obj.triangle*0+1)
cdf = obj.latest_diagonal.triangle
exposure = sample_weight.triangle
obj.values = \
self.X_.cdf_.values[..., :obj.shape[-1]]*(obj.values*0+1)
cdf = obj.latest_diagonal.values
exposure = sample_weight.values
reported_exposure = exposure/cdf
trend_exponent = exposure.shape[-2]-np.arange(exposure.shape[-2])-1
trend_array = (1+self.trend)**(trend_exponent)
Expand All @@ -77,14 +77,14 @@ def fit(self, X, y=None, sample_weight=None):
reported_exposure, -1, -2)
apriori = np.sum(weighted_exposure*trended_ultimate, -1) / \
np.sum(weighted_exposure, -1)
obj.triangle = np.expand_dims(apriori, -1)
obj.values = np.expand_dims(apriori, -1)
obj.ddims = ['Apriori']
self.apriori_ = copy.deepcopy(obj)
detrended_ultimate = self.apriori_.triangle/trend_array
detrended_ultimate = self.apriori_.values/trend_array
self.detrended_apriori_ = copy.deepcopy(obj)
self.detrended_apriori_.tiangle = detrended_ultimate
ibnr = detrended_ultimate*(1-1/cdf)*exposure
obj.triangle = latest + ibnr
obj.values = latest + ibnr
obj.ddims = ['Ultimate']
obj.valuation = pd.DatetimeIndex([pd.to_datetime('2262-04-11')]*obj.shape[-2])
self.ultimate_ = obj
Expand Down
8 changes: 4 additions & 4 deletions chainladder/methods/chainladder.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ def fit(self, X, y=None, sample_weight=None):
@property
def ultimate_(self):
obj = copy.deepcopy(self.X_)
obj.triangle = np.repeat(self.X_.latest_diagonal.triangle,
obj.values = np.repeat(self.X_.latest_diagonal.values,
self.cdf_.shape[3], 3)
cdf = self.cdf_.triangle[..., :self.X_.nan_triangle().shape[-1]]
obj_tri = obj.triangle[..., :self.X_.nan_triangle().shape[-1]]
obj.triangle = (cdf*obj_tri)*self.X_.nan_triangle()
cdf = self.cdf_.values[..., :self.X_.nan_triangle().shape[-1]]
obj_tri = obj.values[..., :self.X_.nan_triangle().shape[-1]]
obj.values = (cdf*obj_tri)*self.X_.nan_triangle()
obj = obj.latest_diagonal
obj.ddims = ['Ultimate']
obj.valuation = obj._valuation_triangle()
Expand Down
Loading

0 comments on commit 6450022

Please sign in to comment.