Skip to content

Commit 99826bb

Browse files
phoflJulianWgs
authored andcommitted
BUG: Series.loc raising KeyError for Iterator indexer in case of setitem (pandas-dev#39623)
1 parent e897350 commit 99826bb

File tree

5 files changed

+48
-0
lines changed

5 files changed

+48
-0
lines changed

doc/source/whatsnew/v1.3.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ Indexing
691691
- Bug in :meth:`DataFrame.__setitem__` raising ``ValueError`` with empty :class:`DataFrame` and specified columns for string indexer and non empty :class:`DataFrame` to set (:issue:`38831`)
692692
- Bug in :meth:`DataFrame.loc.__setitem__` raising ValueError when expanding unique column for :class:`DataFrame` with duplicate columns (:issue:`38521`)
693693
- Bug in :meth:`DataFrame.iloc.__setitem__` and :meth:`DataFrame.loc.__setitem__` with mixed dtypes when setting with a dictionary value (:issue:`38335`)
694+
- Bug in :meth:`Series.loc.__setitem__` and :meth:`DataFrame.loc.__setitem__` raising ``KeyError`` for boolean Iterator indexer (:issue:`39614`)
695+
- Bug in :meth:`Series.iloc` and :meth:`DataFrame.iloc` raising ``KeyError`` for Iterator indexer (:issue:`39614`)
694696
- Bug in :meth:`DataFrame.__setitem__` not raising ``ValueError`` when right hand side is a :class:`DataFrame` with wrong number of columns (:issue:`38604`)
695697
- Bug in :meth:`Series.__setitem__` raising ``ValueError`` when setting a :class:`Series` with a scalar indexer (:issue:`38303`)
696698
- Bug in :meth:`DataFrame.loc` dropping levels of :class:`MultiIndex` when :class:`DataFrame` used as input has only one row (:issue:`10521`)

pandas/core/indexing.py

+10
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None):
703703

704704
def __setitem__(self, key, value):
705705
if isinstance(key, tuple):
706+
key = tuple(list(x) if is_iterator(x) else x for x in key)
706707
key = tuple(com.apply_if_callable(x, self.obj) for x in key)
707708
else:
708709
key = com.apply_if_callable(key, self.obj)
@@ -911,6 +912,7 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False):
911912

912913
def __getitem__(self, key):
913914
if type(key) is tuple:
915+
key = tuple(list(x) if is_iterator(x) else x for x in key)
914916
key = tuple(com.apply_if_callable(x, self.obj) for x in key)
915917
if self._is_scalar_access(key):
916918
with suppress(KeyError, IndexError, AttributeError):
@@ -1242,6 +1244,9 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False):
12421244

12431245
elif is_list_like_indexer(key):
12441246

1247+
if is_iterator(key):
1248+
key = list(key)
1249+
12451250
if com.is_bool_indexer(key):
12461251
key = check_bool_indexer(labels, key)
12471252
(inds,) = key.nonzero()
@@ -1530,6 +1535,9 @@ def _getitem_axis(self, key, axis: int):
15301535
if isinstance(key, slice):
15311536
return self._get_slice_axis(key, axis=axis)
15321537

1538+
if is_iterator(key):
1539+
key = list(key)
1540+
15331541
if isinstance(key, list):
15341542
key = np.asarray(key)
15351543

@@ -1571,6 +1579,8 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False):
15711579

15721580
def _get_setitem_indexer(self, key):
15731581
# GH#32257 Fall through to let numpy do validation
1582+
if is_iterator(key):
1583+
return list(key)
15741584
return key
15751585

15761586
# -------------------------------------------------------------------

pandas/tests/frame/indexing/test_getitem.py

+15
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,21 @@ def test_getitem_listlike(self, idx_type, levels, float_frame):
144144
with pytest.raises(KeyError, match="not in index"):
145145
frame[idx]
146146

147+
def test_getitem_iloc_generator(self):
148+
# GH#39614
149+
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
150+
indexer = (x for x in [1, 2])
151+
result = df.iloc[indexer]
152+
expected = DataFrame({"a": [2, 3], "b": [5, 6]}, index=[1, 2])
153+
tm.assert_frame_equal(result, expected)
154+
155+
def test_getitem_iloc_two_dimensional_generator(self):
156+
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
157+
indexer = (x for x in [1, 2])
158+
result = df.iloc[indexer, 1]
159+
expected = Series([5, 6], name="b", index=[1, 2])
160+
tm.assert_series_equal(result, expected)
161+
147162

148163
class TestGetitemCallable:
149164
def test_getitem_callable(self, float_frame):

pandas/tests/frame/indexing/test_setitem.py

+15
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,21 @@ def test_setitem_list_of_tuples(self, float_frame):
610610
expected = Series(tuples, index=float_frame.index, name="tuples")
611611
tm.assert_series_equal(result, expected)
612612

613+
def test_setitem_iloc_generator(self):
614+
# GH#39614
615+
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
616+
indexer = (x for x in [1, 2])
617+
df.iloc[indexer] = 1
618+
expected = DataFrame({"a": [1, 1, 1], "b": [4, 1, 1]})
619+
tm.assert_frame_equal(df, expected)
620+
621+
def test_setitem_iloc_two_dimensional_generator(self):
622+
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
623+
indexer = (x for x in [1, 2])
624+
df.iloc[indexer, 1] = 1
625+
expected = DataFrame({"a": [1, 2, 3], "b": [4, 1, 1]})
626+
tm.assert_frame_equal(df, expected)
627+
613628

614629
class TestSetitemTZAwareValues:
615630
@pytest.fixture

pandas/tests/series/indexing/test_setitem.py

+6
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,9 @@ def test_int_key(self, obj, key, expected, val, indexer_sli, is_inplace):
556556
indkey = np.array(ilkey)
557557
self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
558558

559+
genkey = (x for x in [key])
560+
self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
561+
559562
def test_slice_key(self, obj, key, expected, val, indexer_sli, is_inplace):
560563
if not isinstance(key, slice):
561564
return
@@ -570,6 +573,9 @@ def test_slice_key(self, obj, key, expected, val, indexer_sli, is_inplace):
570573
indkey = np.array(ilkey)
571574
self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
572575

576+
genkey = (x for x in indkey)
577+
self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
578+
573579
def test_mask_key(self, obj, key, expected, val, indexer_sli):
574580
# setitem with boolean mask
575581
mask = np.zeros(obj.shape, dtype=bool)

0 commit comments

Comments
 (0)