Skip to content

Commit f8ce218

Browse files
topper-123proost
authored andcommitted
BUG: slice indexing with CategoricalIndex (pandas-dev#30225)
1 parent 166b3e0 commit f8ce218

File tree

4 files changed

+27
-15
lines changed

4 files changed

+27
-15
lines changed

doc/source/whatsnew/v1.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ Indexing
745745
- Fix assignment of column via `.loc` with numpy non-ns datetime type (:issue:`27395`)
746746
- Bug in :meth:`Float64Index.astype` where ``np.inf`` was not handled properly when casting to an integer dtype (:issue:`28475`)
747747
- :meth:`Index.union` could fail when the left contained duplicates (:issue:`28257`)
748-
- Bug when indexing with ``.loc`` where the index was a :class:`CategoricalIndex` with integer and float categories, a ValueError was raised (:issue:`17569`)
748+
- Bug when indexing with ``.loc`` where the index was a :class:`CategoricalIndex` with non-string categories didn't work (:issue:`17569`, :issue:`30225`)
749749
- :meth:`Index.get_indexer_non_unique` could fail with `TypeError` in some cases, such as when searching for ints in a string index (:issue:`28257`)
750750
- Bug in :meth:`Float64Index.get_loc` incorrectly raising ``TypeError`` instead of ``KeyError`` (:issue:`29189`)
751751

pandas/core/indexes/base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -2982,7 +2982,9 @@ def is_int(v):
29822982

29832983
is_null_slicer = start is None and stop is None
29842984
is_index_slice = is_int(start) and is_int(stop)
2985-
is_positional = is_index_slice and not self.is_integer()
2985+
is_positional = is_index_slice and not (
2986+
self.is_integer() or self.is_categorical()
2987+
)
29862988

29872989
if kind == "getitem":
29882990
"""

pandas/core/indexes/category.py

+7
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,13 @@ def is_dtype_equal(self, other):
753753

754754
take_nd = take
755755

756+
@Appender(_index_shared_docs["_maybe_cast_slice_bound"])
757+
def _maybe_cast_slice_bound(self, label, side, kind):
758+
if kind == "loc":
759+
return label
760+
761+
return super()._maybe_cast_slice_bound(label, side, kind)
762+
756763
def map(self, mapper):
757764
"""
758765
Map values using input correspondence (a dict, Series, or function).

pandas/tests/indexing/test_categorical.py

+16-13
Original file line numberDiff line numberDiff line change
@@ -654,22 +654,13 @@ def test_reindexing(self):
654654
df.reindex(["a"], limit=2)
655655

656656
def test_loc_slice(self):
657-
# slicing
658-
# not implemented ATM
659657
# GH9748
660-
661-
msg = (
662-
"cannot do slice indexing on {klass} with these "
663-
r"indexers \[1\] of {kind}".format(
664-
klass=str(CategoricalIndex), kind=str(int)
665-
)
666-
)
667-
with pytest.raises(TypeError, match=msg):
658+
with pytest.raises(KeyError, match="1"):
668659
self.df.loc[1:5]
669660

670-
# result = df.loc[1:5]
671-
# expected = df.iloc[[1,2,3,4]]
672-
# tm.assert_frame_equal(result, expected)
661+
result = self.df.loc["b":"c"]
662+
expected = self.df.iloc[[2, 3, 4]]
663+
tm.assert_frame_equal(result, expected)
673664

674665
def test_loc_and_at_with_categorical_index(self):
675666
# GH 20629
@@ -794,6 +785,7 @@ def test_loc_with_non_string_categories(self, idx_values, ordered_fixture):
794785
# GH-17569
795786
cat_idx = CategoricalIndex(idx_values, ordered=ordered_fixture)
796787
df = DataFrame({"A": ["foo", "bar", "baz"]}, index=cat_idx)
788+
sl = slice(idx_values[0], idx_values[1])
797789

798790
# scalar selection
799791
result = df.loc[idx_values[0]]
@@ -805,6 +797,11 @@ def test_loc_with_non_string_categories(self, idx_values, ordered_fixture):
805797
expected = DataFrame(["foo", "bar"], index=cat_idx[:2], columns=["A"])
806798
tm.assert_frame_equal(result, expected)
807799

800+
# slice selection
801+
result = df.loc[sl]
802+
expected = DataFrame(["foo", "bar"], index=cat_idx[:2], columns=["A"])
803+
tm.assert_frame_equal(result, expected)
804+
808805
# scalar assignment
809806
result = df.copy()
810807
result.loc[idx_values[0]] = "qux"
@@ -816,3 +813,9 @@ def test_loc_with_non_string_categories(self, idx_values, ordered_fixture):
816813
result.loc[idx_values[:2], "A"] = ["qux", "qux2"]
817814
expected = DataFrame({"A": ["qux", "qux2", "baz"]}, index=cat_idx)
818815
tm.assert_frame_equal(result, expected)
816+
817+
# slice assignment
818+
result = df.copy()
819+
result.loc[sl, "A"] = ["qux", "qux2"]
820+
expected = DataFrame({"A": ["qux", "qux2", "baz"]}, index=cat_idx)
821+
tm.assert_frame_equal(result, expected)

0 commit comments

Comments
 (0)