diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 24725484bd3c6..a530fe4028ce4 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -53,6 +53,7 @@ Other enhancements - :meth:`DataFrame.apply` can now accept non-callable DataFrame properties as strings, e.g. ``df.apply("size")``, which was already the case for :meth:`Series.apply` (:issue:`39116`) - :meth:`Series.apply` can now accept list-like or dictionary-like arguments that aren't lists or dictionaries, e.g. ``ser.apply(np.array(["sum", "mean"]))``, which was already the case for :meth:`DataFrame.apply` (:issue:`39140`) - :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes. +- :meth:`Series.loc.__getitem__` and :meth:`Series.loc.__setitem__` with :class:`MultiIndex` now raising helpful error message when indexer has too many dimensions (:issue:`35349`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 998a34ce834ea..9185098ad48bf 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -846,6 +846,11 @@ def _getitem_nested_tuple(self, tup: Tuple): if self.name != "loc": # This should never be reached, but lets be explicit about it raise ValueError("Too many indices") + if isinstance(self.obj, ABCSeries) and any( + isinstance(k, tuple) for k in tup + ): + # 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): # GH#10521 Series should reduce MultiIndex dimensions instead of # DataFrame, IndexingError is not raised when slice(None,None,None) @@ -1203,6 +1208,11 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False): return {"key": key} if is_nested_tuple(key, labels): + if isinstance(self.obj, ABCSeries) and any( + isinstance(k, tuple) for k in key + ): + # GH#35349 Raise if tuple in tuple for series + raise ValueError("Too many indices") return labels.get_locs(key) elif is_list_like_indexer(key): diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index aec5e3adfe111..b6e4c38ef7f0e 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -17,6 +17,7 @@ DataFrame, DatetimeIndex, Index, + IndexSlice, MultiIndex, Series, SparseDtype, @@ -2130,3 +2131,17 @@ def test_loc_iloc_setitem_with_listlike(self, size, array_fn): ser = Series(0, index=list("abcde"), dtype=object) ser.iloc[0] = arr tm.assert_series_equal(ser, expected) + + @pytest.mark.parametrize("indexer", [IndexSlice["A", :], ("A", slice(None))]) + def test_loc_series_getitem_too_many_dimensions(self, indexer): + # GH#35349 + ser = Series( + index=MultiIndex.from_tuples([("A", "0"), ("A", "1"), ("B", "0")]), + data=[21, 22, 23], + ) + msg = "Too many indices" + with pytest.raises(ValueError, match=msg): + ser.loc[indexer, :] + + with pytest.raises(ValueError, match=msg): + ser.loc[indexer, :] = 1