Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 137 additions & 3 deletions chainladder/core/dunders.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def _prep_index(self, x, y):
x = x.sort_index()
try:
y = y.loc[x.index]
except:
except Exception:
x = x.groupby(list(common))
y = y.groupby(list(common))
return x, y
Expand Down Expand Up @@ -206,7 +206,7 @@ def _prep_origin_development(self, obj, other):
other_arr.shape = (other.shape[0], other.shape[1], len(odims), len(ddims))
obj_arr.shape = (self.shape[0], self.shape[1], len(odims), len(ddims))
obj.odims = np.array(odims.index)
if type(obj.ddims) == pd.DatetimeIndex:
if isinstance(obj.ddims, pd.DatetimeIndex):
obj.ddims = pd.DatetimeIndex(ddims.index)
else:
obj.ddims = np.array(ddims.index)
Expand Down Expand Up @@ -247,6 +247,38 @@ def _arithmetic_mapper(self, obj, other, f):
return concat(c, 0).sort_index()

def __add__(self, other):
"""Element-wise addition.

Examples
--------
Adding a scalar shifts every observed cell.

.. testsetup::

import chainladder as cl

.. testcode::

tri = cl.Triangle(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should tri be in testcode instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved. testsetup is only import chainladder as cl; the Triangle is built in testcode.

data={
'origin': [1985, 1985, 1986],
'development': [1985, 1986, 1986],
'paid': [100, 150, 80],
},
origin='origin',
development='development',
columns=['paid'],
cumulative=True,
)
print(tri + 10)

.. testoutput::
:options: +NORMALIZE_WHITESPACE

12 24
1985 110.0 160.0
1986 90.0 NaN
"""
obj, other = self._validate_arithmetic(other)
if isinstance(obj, TriangleGroupBy):
def f(k, self, obj, other):
Expand All @@ -262,6 +294,36 @@ def __radd__(self, other):
return self if other == 0 else self.__add__(other)

def __sub__(self, other):
"""Element-wise subtraction.

Examples
--------
.. testsetup::

import chainladder as cl

.. testcode::

tri = cl.Triangle(
data={
'origin': [1985, 1985, 1986],
'development': [1985, 1986, 1986],
'paid': [100, 150, 80],
},
origin='origin',
development='development',
columns=['paid'],
cumulative=True,
)
print(tri - 10)

.. testoutput::
:options: +NORMALIZE_WHITESPACE

12 24
1985 90.0 140.0
1986 70.0 NaN
"""
obj, other = self._validate_arithmetic(other)
if isinstance(obj, TriangleGroupBy):
def f(k, self, obj, other):
Expand Down Expand Up @@ -306,14 +368,43 @@ def __abs__(self):
return obj

def __mul__(self, other):
"""Element-wise multiplication.

Examples
--------
.. testsetup::

import chainladder as cl

.. testcode::

tri = cl.Triangle(
data={
'origin': [1985, 1985, 1986],
'development': [1985, 1986, 1986],
'paid': [100, 150, 80],
},
origin='origin',
development='development',
columns=['paid'],
cumulative=True,
)
print(tri * 2)

.. testoutput::
:options: +NORMALIZE_WHITESPACE

12 24
1985 200.0 300.0
1986 160.0 NaN
"""
obj, other = self._validate_arithmetic(other)
if isinstance(obj, TriangleGroupBy):
def f(k, self, obj, other):
return (self._slice_or_nan(obj, other, k) *
self._slice_or_nan(other, obj, k))
obj = self._arithmetic_mapper(obj, other, f)
else:
xp = obj.get_array_module()
obj.values = obj.values * other
return obj

Expand Down Expand Up @@ -347,6 +438,49 @@ def __truediv__(self, other: Any):

other: Any
The thing that divides the triangle.

Examples
--------
Dividing by a scalar scales the triangle. Dividing two columns is the
usual way to form a ratio triangle, such as paid-to-incurred.

.. testsetup::

import chainladder as cl

.. testcode::

tri = cl.Triangle(
data={
'origin': [1985, 1985, 1986],
'development': [1985, 1986, 1986],
'paid': [100, 150, 80],
'incurred': [120, 160, 100],
},
origin='origin',
development='development',
columns=['paid', 'incurred'],
cumulative=True,
)
print(tri['paid'] / 2)

.. testoutput::
:options: +NORMALIZE_WHITESPACE

12 24
1985 50.0 75.0
1986 40.0 NaN

.. testcode::

print(tri['paid'] / tri['incurred'])

.. testoutput::
:options: +NORMALIZE_WHITESPACE

12 24
1985 0.833333 0.9375
1986 0.800000 NaN
"""
obj, other = self._validate_arithmetic(other)
if isinstance(obj, TriangleGroupBy):
Expand Down
6 changes: 5 additions & 1 deletion docs/_templates/autosummary/class_inherited.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

.. currentmodule:: {{ module }}

{% set documented_attrs = ['loc', 'iloc', 'at', 'iat', 'shape', 'empty', 'dimensionality', 'nan_triangle'] %}
Comment thread
henrydingliu marked this conversation as resolved.
{% set hidden_attrs = attributes | reject('in', documented_attrs) | list %}

.. autoclass:: {{ objname }}
:members:
:inherited-members:
:undoc-members:
:exclude-members: set_fit_request, set_predict_request, set_score_request, set_transform_request, {{ attributes | join(', ') }}
:special-members: __add__, __sub__, __mul__, __truediv__
:exclude-members: set_fit_request, set_predict_request, set_score_request, set_transform_request{% if hidden_attrs %}, {{ hidden_attrs | join(', ') }}{% endif %}
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ select = ["E2", "E4", "E7", "E9", "F"]
"chainladder/core/common.py" = ["F401"]
"chainladder/core/correlation.py" = ["E741"]
"chainladder/core/display.py" = ["E203", "E252"]
"chainladder/core/dunders.py" = ["E721", "E722", "F841"]
"chainladder/core/pandas.py" = ["E231", "E251", "E261", "E275", "E721", "F841"]
"chainladder/core/slice.py" = ["E225", "E712", "E721", "E741"]
"chainladder/core/tests/rtest_correlation.py" = ["E266", "E722", "F821"]
Expand Down
Loading