diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 040c424fb4127..d02c0e38427ac 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -717,6 +717,7 @@ Indexing - Bug in :meth:`IntervalIndex.get_indexer_non_unique` returning boolean mask instead of array of integers for a non unique and non monotonic index (:issue:`44084`) - Bug in :meth:`IntervalIndex.get_indexer_non_unique` not handling targets of ``dtype`` 'object' with NaNs correctly (:issue:`44482`) - Fixed regression where a single column ``np.matrix`` was no longer coerced to a 1d ``np.ndarray`` when added to a :class:`DataFrame` (:issue:`42376`) +- Bug in :meth:`Series.__getitem__` with a :class:`CategoricalIndex` of integers treating lists of integers as positional indexers, inconsistent with the behavior with a single scalar integer (:issue:`15470`,:issue:`14865`) - Missing diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 9ef43c740d602..1cc15e9a5569f 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -16,7 +16,10 @@ DtypeObj, npt, ) -from pandas.util._decorators import doc +from pandas.util._decorators import ( + cache_readonly, + doc, +) from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( @@ -180,6 +183,10 @@ class CategoricalIndex(NDArrayBackedExtensionIndex): def _can_hold_strings(self): return self.categories._can_hold_strings + @cache_readonly + def _should_fallback_to_positional(self) -> bool: + return self.categories._should_fallback_to_positional + codes: np.ndarray categories: Index ordered: bool | None diff --git a/pandas/tests/series/indexing/test_getitem.py b/pandas/tests/series/indexing/test_getitem.py index 343cc21beb4b6..dabe3d480ff19 100644 --- a/pandas/tests/series/indexing/test_getitem.py +++ b/pandas/tests/series/indexing/test_getitem.py @@ -163,6 +163,27 @@ def test_getitem_scalar_categorical_index(self): result = ser[cats[0]] assert result == expected + def test_getitem_numeric_categorical_listlike_matches_scalar(self): + # GH#15470 + ser = Series(["a", "b", "c"], index=pd.CategoricalIndex([2, 1, 0])) + + # 0 is treated as a label + assert ser[0] == "c" + + # the listlike analogue should also be treated as labels + res = ser[[0]] + expected = ser.iloc[-1:] + tm.assert_series_equal(res, expected) + + res2 = ser[[0, 1, 2]] + tm.assert_series_equal(res2, ser.iloc[::-1]) + + def test_getitem_integer_categorical_not_positional(self): + # GH#14865 + ser = Series(["a", "b", "c"], index=Index([1, 2, 3], dtype="category")) + assert ser.get(3) == "c" + assert ser[3] == "c" + def test_getitem_str_with_timedeltaindex(self): rng = timedelta_range("1 day 10:11:12", freq="h", periods=500) ser = Series(np.arange(len(rng)), index=rng)