Skip to content

Commit 88a2f1c

Browse files
497 (#501)
* Added labels * Tracing * Investigating the bug * Added approximation_grain * Clean up debugger * Added test cases * Added assert in some test cases * Able to print an empty triangle * Added test cases * Fixed display issues * Update base.py * Addressed a calculation bug * Added debugger * Modified test * Removed debugger --------- Co-authored-by: John S Bogaardt <[email protected]>
1 parent bf9a630 commit 88a2f1c

File tree

9 files changed

+512
-220
lines changed

9 files changed

+512
-220
lines changed

chainladder/adjustments/parallelogram.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ class ParallelogramOLF(BaseEstimator, TransformerMixin, EstimatorIO):
3636
"""
3737

3838
def __init__(
39-
self, rate_history=None, change_col="", date_col="", vertical_line=False
39+
self,
40+
rate_history=None,
41+
change_col="",
42+
date_col="",
43+
approximation_grain="M",
44+
vertical_line=False,
4045
):
4146
self.rate_history = rate_history
4247
self.change_col = change_col
4348
self.date_col = date_col
49+
self.approximation_grain = approximation_grain
4450
self.vertical_line = vertical_line
4551

4652
def fit(self, X, y=None, sample_weight=None):
@@ -77,6 +83,7 @@ def fit(self, X, y=None, sample_weight=None):
7783
end_date=X.origin[-1].to_timestamp(how="e"),
7884
grain=X.origin_grain,
7985
vertical_line=self.vertical_line,
86+
approximation_grain=self.approximation_grain,
8087
)
8188

8289
if len(groups) > 0:
@@ -105,7 +112,7 @@ def fit(self, X, y=None, sample_weight=None):
105112
return self
106113

107114
def transform(self, X, y=None, sample_weight=None):
108-
""" If X and self are of different shapes, align self to X, else
115+
"""If X and self are of different shapes, align self to X, else
109116
return self.
110117
111118
Parameters

chainladder/core/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ def _to_datetime(data, fields, period_end=False, format=None):
258258
def _development_lag(origin, valuation):
259259
"""For tabular format, this will convert the origin/valuation
260260
difference to a development lag"""
261-
return ((valuation - origin) / (365.25/12)).round('1d').dt.days
261+
return ((valuation - origin) / (365.25/12)).dt.round('1d').dt.days
262262

263263

264264
@staticmethod

chainladder/core/display.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
class TriangleDisplay:
1515
def __repr__(self):
16+
try:
17+
self.values
18+
except:
19+
print("Triangle is empty")
20+
return
21+
1622
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
1723
data = self._repr_format()
1824
return data.to_string()
@@ -33,7 +39,7 @@ def _summary_frame(self):
3339
).to_frame()
3440

3541
def _repr_html_(self):
36-
""" Jupyter/Ipython HTML representation """
42+
"""Jupyter/Ipython HTML representation"""
3743
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
3844
data = self._repr_format()
3945
fmt_str = self._get_format_str(data)
@@ -66,7 +72,7 @@ def _get_format_str(self, data):
6672
def _repr_format(self, origin_as_datetime=False):
6773
out = self.compute().set_backend("numpy").values[0, 0]
6874
if origin_as_datetime and not self.is_pattern:
69-
origin = self.origin.to_timestamp(how='s')
75+
origin = self.origin.to_timestamp(how="s")
7076
else:
7177
origin = self.origin.copy()
7278
origin.name = None
@@ -85,7 +91,7 @@ def _repr_format(self, origin_as_datetime=False):
8591
return pd.DataFrame(out, index=origin, columns=development)
8692

8793
def heatmap(self, cmap="coolwarm", low=0, high=0, axis=0, subset=None):
88-
""" Color the background in a gradient according to the data in each
94+
"""Color the background in a gradient according to the data in each
8995
column (optionally row). Requires matplotlib
9096
9197
Parameters
@@ -134,7 +140,12 @@ def heatmap(self, cmap="coolwarm", low=0, high=0, axis=0, subset=None):
134140
else:
135141
default_output = (
136142
data.style.format(fmt_str)
137-
.background_gradient(cmap=cmap, low=low, high=high, axis=axis,)
143+
.background_gradient(
144+
cmap=cmap,
145+
low=low,
146+
high=high,
147+
axis=axis,
148+
)
138149
.render()
139150
)
140151
output_xnan = re.sub("<td.*nan.*td>", "<td></td>", default_output)

chainladder/core/tests/test_display.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33

44

55
def test_heatmap_render(raa):
6-
""" The heatmap method should render correctly given the sample."""
7-
return raa.heatmap()
6+
"""The heatmap method should render correctly given the sample."""
7+
assert raa.heatmap()
88

99

10-
def test_to_frame(raa):
11-
try:
12-
cl.Chainladder().fit(raa).cdf_.to_frame()
13-
cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=False)
14-
cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=True)
15-
cl.Chainladder().fit(raa).ultimate_.to_frame()
16-
cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=False)
17-
cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=True)
10+
def test_empty_triangle():
11+
assert cl.Triangle()
12+
1813

19-
except:
20-
assert False
14+
def test_to_frame(raa):
15+
assert cl.Chainladder().fit(raa).cdf_.to_frame()
16+
assert cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=False)
17+
assert cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=True)
18+
assert cl.Chainladder().fit(raa).ultimate_.to_frame()
19+
assert cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=False)
20+
assert cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=True)

chainladder/core/triangle.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ def __init__(
130130
data, index, columns, origin, development
131131
)
132132

133+
self.columns_label = columns
134+
self.origin_label = origin
135+
133136
# Handle any ultimate vectors in triangles separately
134137
data, ult = self._split_ult(data, index, columns, origin, development)
135138
# Conform origins and developments to datetimes and determine lowest grains
@@ -172,6 +175,7 @@ def __init__(
172175
# Deal with labels
173176
if not index:
174177
index = ["Total"]
178+
self.index_label = index
175179
data_agg[index[0]] = "Total"
176180

177181
self.kdims, key_idx = self._set_kdims(data_agg, index)
@@ -672,8 +676,8 @@ def grain(self, grain="", trailing=False, inplace=False):
672676
obj = self.dev_to_val()
673677
if ograin_new != ograin_old:
674678
freq = {"Y": "A", "S": "2Q"}.get(ograin_new, ograin_new)
675-
if trailing or (obj.origin.freqstr[-3:] != "DEC" and ograin_old != 'M'):
676-
origin_period_end = self.origin[-1].strftime("%b").upper()
679+
if trailing or (obj.origin.freqstr[-3:] != "DEC" and ograin_old != "M"):
680+
origin_period_end = self.origin[-1].strftime("%b").upper()
677681
else:
678682
origin_period_end = "DEC"
679683
indices = (
@@ -687,12 +691,16 @@ def grain(self, grain="", trailing=False, inplace=False):
687691
obj = obj.groupby(groups, axis=2).sum()
688692
obj.origin_close = origin_period_end
689693
d_start = pd.Period(
690-
obj.valuation[0],
691-
freq=dgrain_old if dgrain_old == 'M' else dgrain_old + obj.origin.freqstr[-4:]
692-
).to_timestamp(how='s')
693-
if (len(obj.ddims) > 1 and obj.origin.to_timestamp(how='s')[0] != d_start):
694+
obj.valuation[0],
695+
freq=dgrain_old
696+
if dgrain_old == "M"
697+
else dgrain_old + obj.origin.freqstr[-4:],
698+
).to_timestamp(how="s")
699+
if len(obj.ddims) > 1 and obj.origin.to_timestamp(how="s")[0] != d_start:
694700
addl_ts = (
695-
pd.period_range(obj.odims[0], obj.valuation[0], freq=dgrain_old)[:-1]
701+
pd.period_range(obj.odims[0], obj.valuation[0], freq=dgrain_old)[
702+
:-1
703+
]
696704
.to_timestamp()
697705
.values
698706
)

0 commit comments

Comments
 (0)