Skip to content

Commit cb5b2e6

Browse files
authored
BUG: RecursionError in loc.setitem (#55201)
1 parent ef5c016 commit cb5b2e6

File tree

4 files changed

+33
-0
lines changed

4 files changed

+33
-0
lines changed

doc/source/whatsnew/v2.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ Interval
266266
Indexing
267267
^^^^^^^^
268268
- Bug in :meth:`Index.difference` not returning a unique set of values when ``other`` is empty or ``other`` is considered non-comparable (:issue:`55113`)
269+
- Bug in setting :class:`Categorical` values into a :class:`DataFrame` with numpy dtypes raising ``RecursionError`` (:issue:`52927`)
269270
-
270271

271272
Missing

pandas/core/dtypes/cast.py

+18
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
PeriodDtype,
6969
)
7070
from pandas.core.dtypes.generic import (
71+
ABCExtensionArray,
7172
ABCIndex,
7273
ABCSeries,
7374
)
@@ -1772,6 +1773,23 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
17721773
return casted
17731774
raise LossySetitemError
17741775

1776+
elif isinstance(element, ABCExtensionArray) and isinstance(
1777+
element.dtype, CategoricalDtype
1778+
):
1779+
# GH#52927 setting Categorical value into non-EA frame
1780+
# TODO: general-case for EAs?
1781+
try:
1782+
casted = element.astype(dtype)
1783+
except (ValueError, TypeError):
1784+
raise LossySetitemError
1785+
# Check for cases of either
1786+
# a) lossy overflow/rounding or
1787+
# b) semantic changes like dt64->int64
1788+
comp = casted == element
1789+
if not comp.all():
1790+
raise LossySetitemError
1791+
return casted
1792+
17751793
# Anything other than integer we cannot hold
17761794
raise LossySetitemError
17771795
if (

pandas/core/internals/blocks.py

+6
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,12 @@ def coerce_to_target_dtype(self, other, warn_on_upcast: bool = False) -> Block:
460460
and will receive the same block
461461
"""
462462
new_dtype = find_result_type(self.values.dtype, other)
463+
if new_dtype == self.dtype:
464+
# GH#52927 avoid RecursionError
465+
raise AssertionError(
466+
"Something has gone wrong, please report a bug at "
467+
"https://github.com/pandas-dev/pandas/issues"
468+
)
463469

464470
# In a future version of pandas, the default will be that
465471
# setting `nan` into an integer series won't raise.

pandas/tests/indexing/test_loc.py

+8
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,14 @@ def test_loc_setitem_range_key(self, frame_or_series):
16571657
expected = frame_or_series([0, 1, 10, 9, 11], index=obj.index)
16581658
tm.assert_equal(obj, expected)
16591659

1660+
def test_loc_setitem_numpy_frame_categorical_value(self):
1661+
# GH#52927
1662+
df = DataFrame({"a": [1, 1, 1, 1, 1], "b": ["a", "a", "a", "a", "a"]})
1663+
df.loc[1:2, "a"] = Categorical([2, 2], categories=[1, 2])
1664+
1665+
expected = DataFrame({"a": [1, 2, 2, 1, 1], "b": ["a", "a", "a", "a", "a"]})
1666+
tm.assert_frame_equal(df, expected)
1667+
16601668

16611669
class TestLocWithEllipsis:
16621670
@pytest.fixture(params=[tm.loc, tm.iloc])

0 commit comments

Comments
 (0)