Skip to content
Open
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
32 changes: 28 additions & 4 deletions chainladder/core/slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,20 @@ def __setitem__(
raise ValueError('Setting values with sparse backend requires .at or .iat')
if isinstance(values, TriangleSlicer):
values = values.values
# attempt to make keys contig
contig_key = tuple([_LocBase._contig_slice(x) for x in key])
# Create a slice for any key elements that are integers, otherwise preserve the slice or array.
key = tuple(
[slice(item, item + 1) if isinstance(item, int) else item for item in key]
tuple_key = tuple(
[slice(item, item + 1) if isinstance(item, int) else item for item in contig_key]
)
cast(np.ndarray, cast(object, self.obj.values)).__setitem__(self._normalize_index(key), values)
norm_key = self._normalize_index(tuple_key)
if type(norm_key[2]) != slice or type(norm_key[3]) != slice:
raise ValueError("Setting while fancy indexing on origin/development is not supported.")
Comment thread
cursor[bot] marked this conversation as resolved.
if type(norm_key[0]) is slice or type(norm_key[1]) is slice:
cast(np.ndarray, cast(object, self.obj.values)).__setitem__(norm_key, values)
else:
#the getter uses arr[idx,:][:,idx] to get the Cartesian product, using np.ix_ on the setter to match
cast(np.ndarray, cast(object, self.obj.values)).__setitem__(np.ix_(norm_key[0], norm_key[1])+(norm_key[2], norm_key[3]), values)
Comment thread
cursor[bot] marked this conversation as resolved.

def _normalize_index(self, key: IndexExpression) -> tuple[_AxisKey, _AxisKey, _AxisKey, _AxisKey]:
"""
Expand Down Expand Up @@ -182,7 +191,7 @@ def _normalize_index(self, key: IndexExpression) -> tuple[_AxisKey, _AxisKey, _A
start: int | None= i.start if i.start > 0 else None
stop: int | None = i.stop if i.stop > -1 else None
stop: int | None = None if stop == self.obj.shape[n] else stop
step: int | None = None if start is None and stop is None else i.step
step: int | None = None if start is None and stop is None and (i.step == 1) else i.step
l.append(slice(start, stop, step))
else:
l.append(i)
Expand Down Expand Up @@ -431,6 +440,21 @@ def key_to_slice(self, key: _LabelKey) -> tuple[_AxisKey, _AxisKey, _AxisKey, _A
return out

def __setitem__(self, key: _LabelKey, values: int | float | TriangleSlicer) -> None:
"""
Supports the .loc[] for setting Triangle values. Only supported for numpy backend.

Parameters
----------
key: _LabelKey
Indicates the location of the Triangle you want to set values for.
values: int | float | TriangleSlicer
The value(s) you want to assign to the slice of the Triangle.

Returns
-------
None

"""
super().__setitem__(cast(tuple[_AxisKey, _AxisKey, _AxisKey, _AxisKey], self.key_to_slice(key)), values)

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.

Looks like I missed filling out a docstring when I annotated the file. Could you fill it out?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done


class Ilocation(_LocBase):
Expand Down
142 changes: 142 additions & 0 deletions chainladder/core/tests/test_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,32 @@ def test_loc_setitem_triangle_value(clrd: Triangle) -> None:
tri.loc["Aegis Grp", "comauto"] = sub * 2
assert tri.loc["Aegis Grp", "comauto"] == sub * 2

def test_loc_setitem_partial_triangles(raa: Triangle) -> None:
"""
Use Triangle.loc to set a few origin or develop period via a TriangleSlicer.

Parameters
----------
raa: Triangle
The raa sample data set fixture.

Returns
-------
None

"""
raa2 = raa * 2
raa_new = raa.copy()
if raa_new.array_backend == "sparse":
pytest.skip("Test is specific to the numpy backend.")
else:
raa_new.loc[:,:,:,:60] = raa2.loc[:,:,:,:60]
raa_new.loc[:,:,:,72:] = raa2.loc[:,:,:,72:]
assert raa_new == raa2
raa_new = raa.copy()
raa_new.loc[:,:,:'1984',:] = raa2.loc[:,:,:'1984',:]
raa_new.loc[:,:,'1985':,:] = raa2.loc[:,:,'1985':,:]
assert raa_new == raa2

def test_invalid_iloc_sparse_assignment(prism) -> None:
"""
Expand Down Expand Up @@ -328,6 +353,29 @@ def test_get_idx_fancy_origin_raises(raa: Triangle) -> None:
with pytest.raises(ValueError, match="Fancy indexing on origin/development is not supported"):
_= raa.iloc[0, 0, [0, 1, 5], :]

def test_set_fancy_origin_raises(raa: Triangle) -> None:
"""
Attempt setting with fancy indexing on origin axis, raise an error.

Parameters
----------
raa: Triangle
The raa sample data set fixture.

Returns
-------
None

"""
raa_copy = raa.copy()
if raa.array_backend == "sparse":
pytest.skip("Test is specific to the numpy backend.")
else:
with pytest.raises(ValueError, match="Setting while fancy indexing on origin/development is not supported."):
raa_copy.iloc[0, 0, [0, 1, 5], :] = raa.iloc[0, 0, :3, :]
with pytest.raises(ValueError, match="Setting while fancy indexing on origin/development is not supported."):
raa_copy.loc['Total', 'values', ['1983', '1984', '1986'], :] = raa.iloc[0, 0, :3, :]


def test_get_idx_fancy_development_raises(raa: Triangle) -> None:
"""
Expand All @@ -346,6 +394,29 @@ def test_get_idx_fancy_development_raises(raa: Triangle) -> None:
with pytest.raises(ValueError, match="Fancy indexing on origin/development is not supported"):
_= raa.iloc[0, 0, :, [0, 1, 5]]

def test_set_fancy_development_raises(raa: Triangle) -> None:
"""
Attempt setting with fancy indexing on development axis, raise an error.

Parameters
----------
raa: Triangle
The raa sample data set fixture.

Returns
-------
None

"""
raa_copy = raa.copy()
if raa.array_backend == "sparse":
pytest.skip("Test is specific to the numpy backend.")
else:
with pytest.raises(ValueError, match="Setting while fancy indexing on origin/development is not supported."):
raa_copy.iloc[0, 0, :, [0, 1, 5]] = raa.iloc[0, 0, :, :3]
with pytest.raises(ValueError, match="Setting while fancy indexing on origin/development is not supported."):
raa_copy.loc['Total', 'values', :, [12, 24, 48]] = raa.iloc[0, 0, :, :3]


def test_get_idx_non_contiguous_index_and_columns(clrd: Triangle) -> None:
"""
Expand All @@ -371,6 +442,77 @@ def test_get_idx_non_contiguous_index_and_columns(clrd: Triangle) -> None:
assert result.index.values.tolist() == expected_index
assert result.columns.tolist() == ['IncurLoss', 'CumPaidLoss', 'EarnedPremNet']

def test_loc_setting_non_contiguous_index_and_columns(clrd: Triangle) -> None:
"""
Set lists of non-contiguous indexes and column through Triangle.loc. Check the values
of the assigned triangles.

Parameters
----------
clrd: Triangle
The clrd sample data set fixture.

Returns
-------
None

"""
if clrd.array_backend == "sparse":
pytest.skip("Test is specific to the numpy backend.")
else:
dest_index = [
['Adriatic Ins Co', 'othliab'],
['Adriatic Ins Co', 'ppauto'],
['Agency Ins Co Of MD Inc', 'ppauto'],
]
val_index = [
['Adriatic Ins Co', 'ppauto'],
['Aegis Grp', 'comauto'],
['Agency Ins Co Of MD Inc', 'ppauto'],
]
dest_col = ['CumPaidLoss', 'BulkLoss', 'EarnedPremNet']
val_col = ['IncurLoss', 'CumPaidLoss', 'EarnedPremNet']
clrd_copy = clrd.copy()
clrd_copy.loc[dest_index] = clrd.loc[val_index]
assert clrd_copy.loc[dest_index] == clrd.loc[val_index]
clrd_copy = clrd.copy()
clrd_copy.loc[:,dest_col] = clrd.loc[:,val_col]
assert clrd_copy.loc[:,dest_col] == clrd.loc[:,val_col]
clrd_copy = clrd.copy()
clrd_copy.loc[dest_index,dest_col] = clrd.loc[val_index,val_col]
assert clrd_copy.loc[dest_index,dest_col] == clrd.loc[val_index,val_col]

def test_iloc_setting_non_contiguous_index_and_columns(clrd: Triangle) -> None:
"""
Set lists of non-contiguous indexes and column through Triangle.iloc. Check the values
of the assigned triangles.

Parameters
----------
clrd: Triangle
The clrd sample data set fixture.

Returns
-------
None

"""
if clrd.array_backend == "sparse":
pytest.skip("Test is specific to the numpy backend.")
else:
dest_index = [0,1,5]
val_index = [1,4,6]
dest_col = [2,3,5]
val_col = [1,2,4]
clrd_copy = clrd.copy()
clrd_copy.iloc[dest_index] = clrd.iloc[val_index]
assert clrd_copy.iloc[dest_index] == clrd.iloc[val_index]
clrd_copy = clrd.copy()
clrd_copy.iloc[:,dest_col] = clrd.iloc[:,val_col]
assert clrd_copy.iloc[:,dest_col] == clrd.iloc[:,val_col]
clrd_copy = clrd.copy()
clrd_copy.iloc[dest_index,dest_col] = clrd.iloc[val_index,val_col]
assert clrd_copy.iloc[dest_index,dest_col] == clrd.iloc[val_index,val_col]

def test_sparse_at_iat1(prism):
t = prism.copy()
Expand Down
Loading