From 6ca875787b60b5d45ad0136ff501f5d523719a3a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 21 Jun 2021 11:28:17 -0700 Subject: [PATCH] REGR: preserve Int32 dtype on setitem (#42166) --- pandas/core/indexing.py | 21 ++++++++++++++++++++- pandas/tests/indexing/test_loc.py | 17 +++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 0fd2eedae2da9..f8578d87e4cad 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -42,8 +42,12 @@ isna, ) +from pandas.core import algorithms as algos import pandas.core.common as com -from pandas.core.construction import array as pd_array +from pandas.core.construction import ( + array as pd_array, + extract_array, +) from pandas.core.indexers import ( check_array_indexer, is_empty_indexer, @@ -1660,6 +1664,21 @@ def _setitem_with_indexer(self, indexer, value, name="iloc"): if com.is_null_slice(indexer[0]): # We are setting an entire column self.obj[key] = value + return + elif is_array_like(value): + # GH#42099 + arr = extract_array(value, extract_numpy=True) + taker = -1 * np.ones(len(self.obj), dtype=np.intp) + empty_value = algos.take_nd(arr, taker) + if not isinstance(value, ABCSeries): + # if not Series (in which case we need to align), + # we can short-circuit + empty_value[indexer[0]] = arr + self.obj[key] = empty_value + return + + self.obj[key] = empty_value + else: self.obj[key] = infer_fill_value(value) diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index a8a2055ffb093..e96b25418d408 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -1830,6 +1830,23 @@ def test_loc_setitem_with_expansion_nonunique_index(self, index, request): ) tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize( + "dtype", ["Int32", "Int64", "UInt32", "UInt64", "Float32", "Float64"] + ) + def test_loc_setitem_with_expansion_preserves_nullable_int(self, dtype): + # GH#42099 + ser = Series([0, 1, 2, 3], dtype=dtype) + df = DataFrame({"data": ser}) + + result = DataFrame(index=df.index) + result.loc[df.index, "data"] = ser + + tm.assert_frame_equal(result, df) + + result = DataFrame(index=df.index) + result.loc[df.index, "data"] = ser._values + tm.assert_frame_equal(result, df) + class TestLocCallable: def test_frame_loc_getitem_callable(self):