Skip to content

Commit

Permalink
497 (#501)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
kennethshsu and jbogaardt authored Apr 3, 2024
1 parent bf9a630 commit 88a2f1c
Show file tree
Hide file tree
Showing 9 changed files with 512 additions and 220 deletions.
11 changes: 9 additions & 2 deletions chainladder/adjustments/parallelogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ class ParallelogramOLF(BaseEstimator, TransformerMixin, EstimatorIO):
"""

def __init__(
self, rate_history=None, change_col="", date_col="", vertical_line=False
self,
rate_history=None,
change_col="",
date_col="",
approximation_grain="M",
vertical_line=False,
):
self.rate_history = rate_history
self.change_col = change_col
self.date_col = date_col
self.approximation_grain = approximation_grain
self.vertical_line = vertical_line

def fit(self, X, y=None, sample_weight=None):
Expand Down Expand Up @@ -77,6 +83,7 @@ def fit(self, X, y=None, sample_weight=None):
end_date=X.origin[-1].to_timestamp(how="e"),
grain=X.origin_grain,
vertical_line=self.vertical_line,
approximation_grain=self.approximation_grain,
)

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

def transform(self, X, y=None, sample_weight=None):
""" If X and self are of different shapes, align self to X, else
"""If X and self are of different shapes, align self to X, else
return self.
Parameters
Expand Down
2 changes: 1 addition & 1 deletion chainladder/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def _to_datetime(data, fields, period_end=False, format=None):
def _development_lag(origin, valuation):
"""For tabular format, this will convert the origin/valuation
difference to a development lag"""
return ((valuation - origin) / (365.25/12)).round('1d').dt.days
return ((valuation - origin) / (365.25/12)).dt.round('1d').dt.days


@staticmethod
Expand Down
19 changes: 15 additions & 4 deletions chainladder/core/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

class TriangleDisplay:
def __repr__(self):
try:
self.values
except:
print("Triangle is empty")
return

if (self.values.shape[0], self.values.shape[1]) == (1, 1):
data = self._repr_format()
return data.to_string()
Expand All @@ -33,7 +39,7 @@ def _summary_frame(self):
).to_frame()

def _repr_html_(self):
""" Jupyter/Ipython HTML representation """
"""Jupyter/Ipython HTML representation"""
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
data = self._repr_format()
fmt_str = self._get_format_str(data)
Expand Down Expand Up @@ -66,7 +72,7 @@ def _get_format_str(self, data):
def _repr_format(self, origin_as_datetime=False):
out = self.compute().set_backend("numpy").values[0, 0]
if origin_as_datetime and not self.is_pattern:
origin = self.origin.to_timestamp(how='s')
origin = self.origin.to_timestamp(how="s")
else:
origin = self.origin.copy()
origin.name = None
Expand All @@ -85,7 +91,7 @@ def _repr_format(self, origin_as_datetime=False):
return pd.DataFrame(out, index=origin, columns=development)

def heatmap(self, cmap="coolwarm", low=0, high=0, axis=0, subset=None):
""" Color the background in a gradient according to the data in each
"""Color the background in a gradient according to the data in each
column (optionally row). Requires matplotlib
Parameters
Expand Down Expand Up @@ -134,7 +140,12 @@ def heatmap(self, cmap="coolwarm", low=0, high=0, axis=0, subset=None):
else:
default_output = (
data.style.format(fmt_str)
.background_gradient(cmap=cmap, low=low, high=high, axis=axis,)
.background_gradient(
cmap=cmap,
low=low,
high=high,
axis=axis,
)
.render()
)
output_xnan = re.sub("<td.*nan.*td>", "<td></td>", default_output)
Expand Down
24 changes: 12 additions & 12 deletions chainladder/core/tests/test_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@


def test_heatmap_render(raa):
""" The heatmap method should render correctly given the sample."""
return raa.heatmap()
"""The heatmap method should render correctly given the sample."""
assert raa.heatmap()


def test_to_frame(raa):
try:
cl.Chainladder().fit(raa).cdf_.to_frame()
cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=False)
cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=True)
cl.Chainladder().fit(raa).ultimate_.to_frame()
cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=False)
cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=True)
def test_empty_triangle():
assert cl.Triangle()


except:
assert False
def test_to_frame(raa):
assert cl.Chainladder().fit(raa).cdf_.to_frame()
assert cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=False)
assert cl.Chainladder().fit(raa).cdf_.to_frame(origin_as_datetime=True)
assert cl.Chainladder().fit(raa).ultimate_.to_frame()
assert cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=False)
assert cl.Chainladder().fit(raa).ultimate_.to_frame(origin_as_datetime=True)
22 changes: 15 additions & 7 deletions chainladder/core/triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ def __init__(
data, index, columns, origin, development
)

self.columns_label = columns
self.origin_label = origin

# Handle any ultimate vectors in triangles separately
data, ult = self._split_ult(data, index, columns, origin, development)
# Conform origins and developments to datetimes and determine lowest grains
Expand Down Expand Up @@ -172,6 +175,7 @@ def __init__(
# Deal with labels
if not index:
index = ["Total"]
self.index_label = index
data_agg[index[0]] = "Total"

self.kdims, key_idx = self._set_kdims(data_agg, index)
Expand Down Expand Up @@ -672,8 +676,8 @@ def grain(self, grain="", trailing=False, inplace=False):
obj = self.dev_to_val()
if ograin_new != ograin_old:
freq = {"Y": "A", "S": "2Q"}.get(ograin_new, ograin_new)
if trailing or (obj.origin.freqstr[-3:] != "DEC" and ograin_old != 'M'):
origin_period_end = self.origin[-1].strftime("%b").upper()
if trailing or (obj.origin.freqstr[-3:] != "DEC" and ograin_old != "M"):
origin_period_end = self.origin[-1].strftime("%b").upper()
else:
origin_period_end = "DEC"
indices = (
Expand All @@ -687,12 +691,16 @@ def grain(self, grain="", trailing=False, inplace=False):
obj = obj.groupby(groups, axis=2).sum()
obj.origin_close = origin_period_end
d_start = pd.Period(
obj.valuation[0],
freq=dgrain_old if dgrain_old == 'M' else dgrain_old + obj.origin.freqstr[-4:]
).to_timestamp(how='s')
if (len(obj.ddims) > 1 and obj.origin.to_timestamp(how='s')[0] != d_start):
obj.valuation[0],
freq=dgrain_old
if dgrain_old == "M"
else dgrain_old + obj.origin.freqstr[-4:],
).to_timestamp(how="s")
if len(obj.ddims) > 1 and obj.origin.to_timestamp(how="s")[0] != d_start:
addl_ts = (
pd.period_range(obj.odims[0], obj.valuation[0], freq=dgrain_old)[:-1]
pd.period_range(obj.odims[0], obj.valuation[0], freq=dgrain_old)[
:-1
]
.to_timestamp()
.values
)
Expand Down
Loading

0 comments on commit 88a2f1c

Please sign in to comment.