Skip to content

Commit 53c1425

Browse files
authored
BUG: loc.setitem coercing rhs df dtypes (#50475)
1 parent 2ef976c commit 53c1425

File tree

3 files changed

+20
-6
lines changed

3 files changed

+20
-6
lines changed

doc/source/whatsnew/v2.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ Indexing
883883
^^^^^^^^
884884
- Bug in :meth:`DataFrame.__setitem__` raising when indexer is a :class:`DataFrame` with ``boolean`` dtype (:issue:`47125`)
885885
- Bug in :meth:`DataFrame.reindex` filling with wrong values when indexing columns and index for ``uint`` dtypes (:issue:`48184`)
886+
- Bug in :meth:`DataFrame.loc` when setting :class:`DataFrame` with different dtypes coercing values to single dtype (:issue:`50467`)
886887
- Bug in :meth:`DataFrame.loc` coercing dtypes when setting values with a list indexer (:issue:`49159`)
887888
- Bug in :meth:`Series.loc` raising error for out of bounds end of slice indexer (:issue:`50161`)
888889
- Bug in :meth:`DataFrame.loc` raising ``ValueError`` with ``bool`` indexer and :class:`MultiIndex` (:issue:`47687`)

pandas/core/indexing.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,10 @@ def _setitem_with_indexer(self, indexer, value, name: str = "iloc"):
17031703
# maybe partial set
17041704
take_split_path = not self.obj._mgr.is_single_block
17051705

1706+
if not take_split_path and isinstance(value, ABCDataFrame):
1707+
# Avoid cast of values
1708+
take_split_path = not value._mgr.is_single_block
1709+
17061710
# if there is only one block/type, still have to take split path
17071711
# unless the block is one-dimensional or it can hold the value
17081712
if not take_split_path and len(self.obj._mgr.arrays) and self.ndim > 1:
@@ -2055,7 +2059,7 @@ def _setitem_single_block(self, indexer, value, name: str) -> None:
20552059
value = self._align_series(indexer, Series(value))
20562060

20572061
elif isinstance(value, ABCDataFrame) and name != "iloc":
2058-
value = self._align_frame(indexer, value)
2062+
value = self._align_frame(indexer, value)._values
20592063

20602064
# check for chained assignment
20612065
self.obj._check_is_chained_assignment_possible()
@@ -2291,7 +2295,7 @@ def ravel(i):
22912295

22922296
raise ValueError("Incompatible indexer with Series")
22932297

2294-
def _align_frame(self, indexer, df: DataFrame):
2298+
def _align_frame(self, indexer, df: DataFrame) -> DataFrame:
22952299
is_frame = self.ndim == 2
22962300

22972301
if isinstance(indexer, tuple):
@@ -2315,15 +2319,15 @@ def _align_frame(self, indexer, df: DataFrame):
23152319
if idx is not None and cols is not None:
23162320

23172321
if df.index.equals(idx) and df.columns.equals(cols):
2318-
val = df.copy()._values
2322+
val = df.copy()
23192323
else:
2320-
val = df.reindex(idx, columns=cols)._values
2324+
val = df.reindex(idx, columns=cols)
23212325
return val
23222326

23232327
elif (isinstance(indexer, slice) or is_list_like_indexer(indexer)) and is_frame:
23242328
ax = self.obj.index[indexer]
23252329
if df.index.equals(ax):
2326-
val = df.copy()._values
2330+
val = df.copy()
23272331
else:
23282332

23292333
# we have a multi-index and are trying to align
@@ -2338,7 +2342,7 @@ def _align_frame(self, indexer, df: DataFrame):
23382342
"specifying the join levels"
23392343
)
23402344

2341-
val = df.reindex(index=ax)._values
2345+
val = df.reindex(index=ax)
23422346
return val
23432347

23442348
raise ValueError("Incompatible indexer with DataFrame")

pandas/tests/frame/indexing/test_indexing.py

+9
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,15 @@ def test_loc_datetime_assignment_dtype_does_not_change(self, utc, indexer):
14931493

14941494
tm.assert_frame_equal(df, expected)
14951495

1496+
@pytest.mark.parametrize("indexer, idx", [(tm.loc, 1), (tm.iloc, 2)])
1497+
def test_setitem_value_coercing_dtypes(self, indexer, idx):
1498+
# GH#50467
1499+
df = DataFrame([["1", np.nan], ["2", np.nan], ["3", np.nan]], dtype=object)
1500+
rhs = DataFrame([[1, np.nan], [2, np.nan]])
1501+
indexer(df)[:idx, :] = rhs
1502+
expected = DataFrame([[1, np.nan], [2, np.nan], ["3", np.nan]], dtype=object)
1503+
tm.assert_frame_equal(df, expected)
1504+
14961505

14971506
class TestDataFrameIndexingUInt64:
14981507
def test_setitem(self, uint64_frame):

0 commit comments

Comments
 (0)