Skip to content

Commit 275b187

Browse files
BUG: fix .loc.__setitem__ not raising when using too many indexers (#44656)
1 parent ae67cb7 commit 275b187

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

doc/source/whatsnew/v1.5.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ Indexing
265265
- Bug in :meth:`DataFrame.iloc` where indexing a single row on a :class:`DataFrame` with a single ExtensionDtype column gave a copy instead of a view on the underlying data (:issue:`45241`)
266266
- Bug in setting a NA value (``None`` or ``np.nan``) into a :class:`Series` with int-based :class:`IntervalDtype` incorrectly casting to object dtype instead of a float-based :class:`IntervalDtype` (:issue:`45568`)
267267
- Bug in :meth:`Series.__setitem__` with a non-integer :class:`Index` when using an integer key to set a value that cannot be set inplace where a ``ValueError`` was raised insead of casting to a common dtype (:issue:`45070`)
268+
- Bug in :meth:`Series.loc.__setitem__` and :meth:`Series.loc.__getitem__` not raising when using multiple keys without using a :class:`MultiIndex` (:issue:`13831`)
268269
- Bug when setting a value too large for a :class:`Series` dtype failing to coerce to a common type (:issue:`26049`, :issue:`32878`)
269270
- Bug in :meth:`loc.__setitem__` treating ``range`` keys as positional instead of label-based (:issue:`45479`)
270271
- Bug in :meth:`Series.__setitem__` when setting ``boolean`` dtype values containing ``NA`` incorrectly raising instead of casting to ``boolean`` dtype (:issue:`45462`)

pandas/conftest.py

+8
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,14 @@ def indexer_sli(request):
17341734
return request.param
17351735

17361736

1737+
@pytest.fixture(params=[tm.loc, tm.iloc])
1738+
def indexer_li(request):
1739+
"""
1740+
Parametrize over loc.__getitem__, iloc.__getitem__
1741+
"""
1742+
return request.param
1743+
1744+
17371745
@pytest.fixture(params=[tm.setitem, tm.iloc])
17381746
def indexer_si(request):
17391747
"""

pandas/core/indexing.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ def _getitem_nested_tuple(self, tup: tuple):
934934
# we are only getting non-hashable tuples, in particular ones
935935
# that themselves contain a slice entry
936936
# See test_loc_series_getitem_too_many_dimensions
937-
raise ValueError("Too many indices")
937+
raise IndexingError("Too many indexers")
938938

939939
# this is a series with a multi-index specified a tuple of
940940
# selectors
@@ -1260,6 +1260,14 @@ def _convert_to_indexer(self, key, axis: int):
12601260
is_int_index = labels.is_integer()
12611261
is_int_positional = is_integer(key) and not is_int_index
12621262

1263+
if (
1264+
isinstance(key, tuple)
1265+
and not isinstance(labels, MultiIndex)
1266+
and self.ndim < 2
1267+
and len(key) > 1
1268+
):
1269+
raise IndexingError("Too many indexers")
1270+
12631271
if is_scalar(key) or (isinstance(labels, MultiIndex) and is_hashable(key)):
12641272
# Otherwise get_loc will raise InvalidIndexError
12651273

@@ -1291,7 +1299,7 @@ def _convert_to_indexer(self, key, axis: int):
12911299
if is_nested_tuple(key, labels):
12921300
if self.ndim == 1 and any(isinstance(k, tuple) for k in key):
12931301
# GH#35349 Raise if tuple in tuple for series
1294-
raise ValueError("Too many indices")
1302+
raise IndexingError("Too many indexers")
12951303
return labels.get_locs(key)
12961304

12971305
elif is_list_like_indexer(key):

pandas/tests/indexing/test_indexing.py

+29
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
)
2525
import pandas._testing as tm
2626
from pandas.core.api import Float64Index
27+
from pandas.core.indexing import IndexingError
2728
from pandas.tests.indexing.common import _mklbl
2829
from pandas.tests.indexing.test_floats import gen_obj
2930

@@ -981,3 +982,31 @@ def test_extension_array_cross_section_converts():
981982

982983
result = df.iloc[0]
983984
tm.assert_series_equal(result, expected)
985+
986+
987+
@pytest.mark.parametrize(
988+
"ser, keys",
989+
[(Series([10]), (0, 0)), (Series([1, 2, 3], index=list("abc")), (0, 1))],
990+
)
991+
def test_ser_tup_indexer_exceeds_dimensions(ser, keys, indexer_li):
992+
# GH#13831
993+
exp_err, exp_msg = IndexingError, "Too many indexers"
994+
with pytest.raises(exp_err, match=exp_msg):
995+
indexer_li(ser)[keys]
996+
997+
if indexer_li == tm.iloc:
998+
# For iloc.__setitem__ we let numpy handle the error reporting.
999+
exp_err, exp_msg = IndexError, "too many indices for array"
1000+
1001+
with pytest.raises(exp_err, match=exp_msg):
1002+
indexer_li(ser)[keys] = 0
1003+
1004+
1005+
def test_ser_list_indexer_exceeds_dimensions(indexer_li):
1006+
# GH#13831
1007+
# Make sure an exception is raised when a tuple exceeds the dimension of the series,
1008+
# but not list when a list is used.
1009+
ser = Series([10])
1010+
res = indexer_li(ser)[[0, 0]]
1011+
exp = Series([10, 10], index=Index([0, 0]))
1012+
tm.assert_series_equal(res, exp)

pandas/tests/indexing/test_loc.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -2704,6 +2704,18 @@ def test_loc_with_period_index_indexer():
27042704
tm.assert_frame_equal(df, df.loc[list(idx)])
27052705

27062706

2707+
def test_loc_setitem_multiindex_timestamp():
2708+
# GH#13831
2709+
vals = np.random.randn(8, 6)
2710+
idx = date_range("1/1/2000", periods=8)
2711+
cols = ["A", "B", "C", "D", "E", "F"]
2712+
exp = DataFrame(vals, index=idx, columns=cols)
2713+
exp.loc[exp.index[1], ("A", "B")] = np.nan
2714+
vals[1][0:2] = np.nan
2715+
res = DataFrame(vals, index=idx, columns=cols)
2716+
tm.assert_frame_equal(res, exp)
2717+
2718+
27072719
def test_loc_getitem_multiindex_tuple_level():
27082720
# GH#27591
27092721
lev1 = ["a", "b", "c"]
@@ -2959,11 +2971,11 @@ def test_loc_series_getitem_too_many_dimensions(self, indexer):
29592971
index=MultiIndex.from_tuples([("A", "0"), ("A", "1"), ("B", "0")]),
29602972
data=[21, 22, 23],
29612973
)
2962-
msg = "Too many indices"
2963-
with pytest.raises(ValueError, match=msg):
2974+
msg = "Too many indexers"
2975+
with pytest.raises(IndexingError, match=msg):
29642976
ser.loc[indexer, :]
29652977

2966-
with pytest.raises(ValueError, match=msg):
2978+
with pytest.raises(IndexingError, match=msg):
29672979
ser.loc[indexer, :] = 1
29682980

29692981
def test_loc_setitem(self, string_series):

0 commit comments

Comments
 (0)