Skip to content

Commit 40daf00

Browse files
authored
BUG: Categorical setitem, comparison with tuple category (#36623)
1 parent 810d8cd commit 40daf00

File tree

4 files changed

+31
-6
lines changed

4 files changed

+31
-6
lines changed

doc/source/whatsnew/v1.2.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ Bug fixes
293293
Categorical
294294
^^^^^^^^^^^
295295
- :meth:`Categorical.fillna` will always return a copy, will validate a passed fill value regardless of whether there are any NAs to fill, and will disallow a ``NaT`` as a fill value for numeric categories (:issue:`36530`)
296-
-
296+
- Bug in :meth:`Categorical.__setitem__` that incorrectly raised when trying to set a tuple value (:issue:`20439`)
297297
-
298298

299299
Datetimelike

pandas/core/arrays/categorical.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
is_dict_like,
2929
is_dtype_equal,
3030
is_extension_array_dtype,
31+
is_hashable,
3132
is_integer_dtype,
3233
is_list_like,
3334
is_object_dtype,
@@ -62,8 +63,9 @@ def _cat_compare_op(op):
6263

6364
@unpack_zerodim_and_defer(opname)
6465
def func(self, other):
65-
if is_list_like(other) and len(other) != len(self):
66-
# TODO: Could this fail if the categories are listlike objects?
66+
hashable = is_hashable(other)
67+
if is_list_like(other) and len(other) != len(self) and not hashable:
68+
# in hashable case we may have a tuple that is itself a category
6769
raise ValueError("Lengths must match.")
6870

6971
if not self.ordered:
@@ -91,7 +93,7 @@ def func(self, other):
9193
ret[mask] = fill_value
9294
return ret
9395

94-
if is_scalar(other):
96+
if hashable:
9597
if other in self.categories:
9698
i = self._unbox_scalar(other)
9799
ret = op(self._codes, i)
@@ -1885,7 +1887,8 @@ def _validate_setitem_value(self, value):
18851887
new_codes = self._validate_listlike(value)
18861888
value = Categorical.from_codes(new_codes, dtype=self.dtype)
18871889

1888-
rvalue = value if is_list_like(value) else [value]
1890+
# wrap scalars and hashable-listlikes in list
1891+
rvalue = value if not is_hashable(value) else [value]
18891892

18901893
from pandas import Index
18911894

pandas/tests/arrays/categorical/test_indexing.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,22 @@ def test_setitem_different_unordered_raises(self, other):
7575
pd.Categorical(["b", "a"], categories=["a", "b", "c"], ordered=True),
7676
],
7777
)
78-
def test_setitem_same_ordered_rasies(self, other):
78+
def test_setitem_same_ordered_raises(self, other):
7979
# Gh-24142
8080
target = pd.Categorical(["a", "b"], categories=["a", "b"], ordered=True)
8181
mask = np.array([True, False])
8282
msg = "Cannot set a Categorical with another, without identical categories"
8383
with pytest.raises(ValueError, match=msg):
8484
target[mask] = other[mask]
8585

86+
def test_setitem_tuple(self):
87+
# GH#20439
88+
cat = pd.Categorical([(0, 1), (0, 2), (0, 1)])
89+
90+
# This should not raise
91+
cat[1] = cat[0]
92+
assert cat[1] == (0, 1)
93+
8694

8795
class TestCategoricalIndexing:
8896
def test_getitem_slice(self):

pandas/tests/arrays/categorical/test_operators.py

+14
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,20 @@ def test_comparison_with_unknown_scalars(self):
179179
tm.assert_numpy_array_equal(cat == 4, np.array([False, False, False]))
180180
tm.assert_numpy_array_equal(cat != 4, np.array([True, True, True]))
181181

182+
def test_comparison_with_tuple(self):
183+
cat = pd.Categorical(np.array(["foo", (0, 1), 3, (0, 1)], dtype=object))
184+
185+
result = cat == "foo"
186+
expected = np.array([True, False, False, False], dtype=bool)
187+
tm.assert_numpy_array_equal(result, expected)
188+
189+
result = cat == (0, 1)
190+
expected = np.array([False, True, False, True], dtype=bool)
191+
tm.assert_numpy_array_equal(result, expected)
192+
193+
result = cat != (0, 1)
194+
tm.assert_numpy_array_equal(result, ~expected)
195+
182196
def test_comparison_of_ordered_categorical_with_nan_to_scalar(
183197
self, compare_operators_no_eq_ne
184198
):

0 commit comments

Comments
 (0)