Skip to content

Commit eec200d

Browse files
authored
BUG: loc.setitem modifying values with empty indexer (#51193)
1 parent b2a26ec commit eec200d

File tree

4 files changed

+30
-1
lines changed

4 files changed

+30
-1
lines changed

doc/source/whatsnew/v2.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,7 @@ Indexing
12001200
- Bug in :meth:`Series.loc` raising error for out of bounds end of slice indexer (:issue:`50161`)
12011201
- Bug in :meth:`DataFrame.loc` raising ``ValueError`` with ``bool`` indexer and :class:`MultiIndex` (:issue:`47687`)
12021202
- Bug in :meth:`DataFrame.loc` raising ``IndexError`` when setting values for a pyarrow-backed column with a non-scalar indexer (:issue:`50085`)
1203+
- Bug in :meth:`DataFrame.loc` modifying object when setting incompatible value with an empty indexer (:issue:`45981`)
12031204
- Bug in :meth:`DataFrame.__setitem__` raising ``ValueError`` when right hand side is :class:`DataFrame` with :class:`MultiIndex` columns (:issue:`49121`)
12041205
- Bug in :meth:`DataFrame.reindex` casting dtype to ``object`` when :class:`DataFrame` has single extension array column when re-indexing ``columns`` and ``index`` (:issue:`48190`)
12051206
- Bug in :meth:`DataFrame.iloc` raising ``IndexError`` when indexer is a :class:`Series` with numeric extension array dtype (:issue:`49521`)

pandas/core/common.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,18 @@ def is_null_slice(obj) -> bool:
316316
)
317317

318318

319+
def is_empty_slice(obj) -> bool:
320+
"""
321+
We have an empty slice, e.g. no values are selected.
322+
"""
323+
return (
324+
isinstance(obj, slice)
325+
and obj.start is not None
326+
and obj.stop is not None
327+
and obj.start == obj.stop
328+
)
329+
330+
319331
def is_true_slices(line) -> list[bool]:
320332
"""
321333
Find non-trivial slices in "line": return a list of booleans with same length.

pandas/core/indexing.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,13 @@ def _setitem_single_column(self, loc: int, value, plane_indexer) -> None:
20172017

20182018
is_full_setter = com.is_null_slice(pi) or com.is_full_slice(pi, len(self.obj))
20192019

2020-
if is_full_setter:
2020+
is_null_setter = com.is_empty_slice(pi) or is_array_like(pi) and len(pi) == 0
2021+
2022+
if is_null_setter:
2023+
# no-op, don't cast dtype later
2024+
return
2025+
2026+
elif is_full_setter:
20212027

20222028
try:
20232029
self.obj._mgr.column_setitem(

pandas/tests/series/indexing/test_indexing.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,16 @@ def test_getitem_bool_int_key():
382382
ser.loc[0]
383383

384384

385+
@pytest.mark.parametrize("val", [{}, {"b": "x"}])
386+
@pytest.mark.parametrize("indexer", [[], [False, False], slice(0, -1), np.array([])])
387+
def test_setitem_empty_indexer(indexer, val):
388+
# GH#45981
389+
df = DataFrame({"a": [1, 2], **val})
390+
expected = df.copy()
391+
df.loc[indexer] = 1.5
392+
tm.assert_frame_equal(df, expected)
393+
394+
385395
class TestDeprecatedIndexers:
386396
@pytest.mark.parametrize("key", [{1}, {1: 1}])
387397
def test_getitem_dict_and_set_deprecated(self, key):

0 commit comments

Comments
 (0)