diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index f8578d87e4cad..7681ac588d3ca 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -825,7 +825,15 @@ def _getitem_lowerdim(self, tup: tuple): ax0 = self.obj._get_axis(0) # ...but iloc should handle the tuple as simple integer-location # instead of checking it as multiindex representation (GH 13797) - if isinstance(ax0, MultiIndex) and self.name != "iloc": + if ( + isinstance(ax0, MultiIndex) + and self.name != "iloc" + and not any(isinstance(x, slice) for x in tup) + ): + # Note: in all extant test cases, replacing the slice condition with + # `all(is_hashable(x) or com.is_null_slice(x) for x in tup)` + # is equivalent. + # (see the other place where we call _handle_lowerdim_multi_index_axis0) with suppress(IndexingError): return self._handle_lowerdim_multi_index_axis0(tup) @@ -879,7 +887,7 @@ def _getitem_nested_tuple(self, tup: tuple): ): # GH#35349 Raise if tuple in tuple for series raise ValueError("Too many indices") - if self.ndim == 1 or not any(isinstance(x, slice) for x in tup): + if all(is_hashable(x) or com.is_null_slice(x) for x in tup): # GH#10521 Series should reduce MultiIndex dimensions instead of # DataFrame, IndexingError is not raised when slice(None,None,None) # with one row. @@ -1117,16 +1125,16 @@ def _handle_lowerdim_multi_index_axis0(self, tup: tuple): try: # fast path for series or for tup devoid of slices return self._get_label(tup, axis=axis) - except (TypeError, InvalidIndexError): + except TypeError as err: # slices are unhashable - pass + raise IndexingError("No label returned") from err + except KeyError as ek: # raise KeyError if number of indexers match # else IndexingError will be raised if self.ndim < len(tup) <= self.obj.index.nlevels: raise ek - - raise IndexingError("No label returned") + raise IndexingError("No label returned") from ek def _getitem_axis(self, key, axis: int): key = item_from_zerodim(key) diff --git a/pandas/tests/indexing/multiindex/test_getitem.py b/pandas/tests/indexing/multiindex/test_getitem.py index f1fbe0c5a6b9c..3790a6e9a5319 100644 --- a/pandas/tests/indexing/multiindex/test_getitem.py +++ b/pandas/tests/indexing/multiindex/test_getitem.py @@ -28,9 +28,11 @@ def test_series_getitem_multiindex(access_method, level1_value, expected): # GH 6018 # series regression getitem with a multi-index - s = Series([1, 2, 3]) - s.index = MultiIndex.from_tuples([(0, 0), (1, 1), (2, 1)]) - result = access_method(s, level1_value) + mi = MultiIndex.from_tuples([(0, 0), (1, 1), (2, 1)], names=["A", "B"]) + ser = Series([1, 2, 3], index=mi) + expected.index.name = "A" + + result = access_method(ser, level1_value) tm.assert_series_equal(result, expected) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index afcff6db5e3dd..bc59c51e359ae 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -787,10 +787,10 @@ def test_loc_getitem_index_differently_ordered_slice_none_duplicates(indexer): def test_loc_getitem_drops_levels_for_one_row_dataframe(): - # GH#10521 + # GH#10521 "x" and "z" are both scalar indexing, so those levels are dropped mi = MultiIndex.from_arrays([["x"], ["y"], ["z"]], names=["a", "b", "c"]) df = DataFrame({"d": [0]}, index=mi) - expected = df.copy() + expected = df.droplevel([0, 2]) result = df.loc["x", :, "z"] tm.assert_frame_equal(result, expected)