Skip to content

Commit

Permalink
Began work on trendconstant
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethshsu committed Apr 11, 2024
1 parent 71a6ced commit e7744c5
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 14 deletions.
103 changes: 99 additions & 4 deletions chainladder/adjustments/trend.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,112 @@ def fit(self, X, y=None, sample_weight=None):
dates = [dates] if type(dates) is not list else dates
if type(dates[0]) is not tuple:
raise AttributeError(
'Dates must be specified as a tuple of start and end dates')
"Dates must be specified as a tuple of start and end dates"
)
self.trend_ = X.copy()
for i, trend in enumerate(trends):
self.trend_ = self.trend_.trend(
trend, self.axis,
start=dates[i][0], end=dates[i][1])
trend, self.axis, start=dates[i][0], end=dates[i][1]
)
self.trend_ = self.trend_ / X
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
----------
X: Triangle
The triangle to be transformed
Returns
-------
X_new: New triangle with transformed attributes.
"""
X_new = X.copy()
triangles = ["trend_"]
for item in triangles:
setattr(X_new, item, getattr(self, item))
X_new._set_slicers()
return X_new


class TrendConstant(BaseEstimator, TransformerMixin, EstimatorIO):
"""
Estimator to create and apply trend factors to a Triangle object. Allows
for compound trends as well as storage of the trend matrix to be used in
other estimators.
Parameters
----------
trends: list-like
The list containing the annual trends expressed as a decimal. For example,
5% decrease should be stated as -0.05
effective_date: list of date-likes
A list-like of (start, end) dates to correspond to the `trend` list.
axis: str (options: [‘origin’, ‘valuation’])
The axis on which to apply the trend
Attributes
----------
trend_:
A triangle representation of the trend factors
"""

def __init__(self, trends=0.0, effective_date=None, axis="origin"):
self.trends = trends
self.effective_date = effective_date
self.axis = axis

def fit(self, X, y=None, sample_weight=None):
"""Fit the model with X.
Parameters
----------
X: Triangle-like
Data to which the model will be applied.
y: Ignored
sample_weight: Ignored
Returns
-------
self: object
Returns the instance itself.
"""
if type(self.trends):
trend_list = self.trends
else:
trend_list = [self.trends]

if self.dates is None:
dates = [(None, None)]
else:
dates = self.dates

if type(dates) is not list:
dates = [dates]
else:
dates = dates

if type(dates[0]) is not tuple:
raise AttributeError(
"Dates must be specified as a tuple of start and end dates"
)

self.trend_ = X.copy()
for i, trend in enumerate(trend_list):
self.trend_ = self.trend_.trend(
trend, self.axis, start=dates[i][0], end=dates[i][1]
)
self.trend_ = self.trend_ / X
return self

def transform(self, X, y=None, sample_weight=None):
"""If X and self are of different shapes, align self to X, else
return self.
Parameters
Expand Down
31 changes: 21 additions & 10 deletions chainladder/core/triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def __init__(
return
elif not isinstance(data, pd.DataFrame) and hasattr(data, "__dataframe__"):
data = self._interchange_dataframe(data)

index, columns, origin, development = self._input_validation(
data, index, columns, origin, development
)
Expand Down Expand Up @@ -276,7 +276,7 @@ def __init__(
self.ddims = obj.ddims
self.values = obj.values
self.valuation_date = pd.Timestamp(options.ULT_VAL)

@staticmethod
def _split_ult(data, index, columns, origin, development):
"""Deal with triangles with ultimate values"""
Expand Down Expand Up @@ -330,17 +330,25 @@ def origin(self):
if self.is_pattern and len(self.odims) == 1:
return pd.Series(["(All)"])
else:
freq = {"Y": "Y" if float('.'.join(pd.__version__.split('.')[:-1])) < 2.2 else "A",
"S": "2Q", "H": "2Q"}.get(
self.origin_grain, self.origin_grain
)
freq = {
"Y": (
"Y"
if float(".".join(pd.__version__.split(".")[:-1])) < 2.2
else "A"
),
"S": "2Q",
"H": "2Q",
}.get(self.origin_grain, self.origin_grain)
freq = freq if freq == "M" else freq + "-" + self.origin_close
return pd.DatetimeIndex(self.odims, name="origin").to_period(freq=freq)

@origin.setter
def origin(self, value):
self._len_check(self.origin, value)
freq = {"Y": "Y" if float('.'.join(pd.__version__.split('.')[:-1])) < 2.2 else "A", "S": "2Q"}.get(self.origin_grain, self.origin_grain)
freq = {
"Y": "Y" if float(".".join(pd.__version__.split(".")[:-1])) < 2.2 else "A",
"S": "2Q",
}.get(self.origin_grain, self.origin_grain)
freq = freq if freq == "M" else freq + "-" + self.origin_close
value = pd.PeriodIndex(list(value), freq=freq)
self.odims = value.to_timestamp().values
Expand Down Expand Up @@ -693,9 +701,11 @@ def grain(self, grain="", trailing=False, inplace=False):
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:],
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 = (
Expand Down Expand Up @@ -763,6 +773,7 @@ def trend(
Triangle
updated with multiplicative trend applied.
"""
print("in trend")
if axis not in ["origin", "valuation", 2, -2]:
raise ValueError(
"Only origin and valuation axes are supported for trending"
Expand Down

0 comments on commit e7744c5

Please sign in to comment.