diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index c3d896166fabe..f522734d6551d 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -223,6 +223,7 @@ Indexing ^^^^^^^^ - Bug in :meth:`CategoricalIndex.get_indexer` failing to raise ``InvalidIndexError`` when non-unique (:issue:`38372`) - Bug in inserting many new columns into a :class:`DataFrame` causing incorrect subsequent indexing behavior (:issue:`38380`) +- Bug in :meth:`DataFrame.loc` converted ``float32`` to ``float64`` when setting values for more than one column (:issue:`18415`) - Bug in :meth:`DataFrame.iloc.__setitem__` and :meth:`DataFrame.loc.__setitem__` with mixed dtypes when setting with a dictionary value (:issue:`38335`) - - diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 693b09336fefc..153d7fea5bf84 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1728,7 +1728,8 @@ def _setitem_with_indexer_2d_value(self, indexer, value): ilocs = self._ensure_iterable_column_indexer(indexer[1]) # GH#7551 Note that this coerces the dtype if we are mixed - value = np.array(value, dtype=object) + if not isinstance(value, np.ndarray): + value = np.array(value, dtype=object) if len(ilocs) != value.shape[1]: raise ValueError( "Must have equal len keys and value when setting with an ndarray" @@ -1736,7 +1737,10 @@ def _setitem_with_indexer_2d_value(self, indexer, value): for i, loc in enumerate(ilocs): # setting with a list, re-coerces - self._setitem_single_column(loc, value[:, i].tolist(), pi) + values = value[:, i] + if value.dtype == "object": + values = values.tolist() + self._setitem_single_column(loc, values, pi) def _setitem_with_indexer_frame_value(self, indexer, value: "DataFrame", name: str): ilocs = self._ensure_iterable_column_indexer(indexer[1]) diff --git a/pandas/tests/frame/indexing/test_setitem.py b/pandas/tests/frame/indexing/test_setitem.py index 19d2f8301037a..84a4aa0fb057f 100644 --- a/pandas/tests/frame/indexing/test_setitem.py +++ b/pandas/tests/frame/indexing/test_setitem.py @@ -9,6 +9,7 @@ DataFrame, Index, Interval, + MultiIndex, NaT, Period, PeriodIndex, @@ -324,6 +325,14 @@ def test_setitem_complete_column_with_array(self): ) tm.assert_frame_equal(df, expected) + def test_loc_setitem_complete_column_float32(self): + # GH#18415 + mi = MultiIndex.from_tuples([(1, 2), (1, 3), (2, 2)]) + df = DataFrame([[0, 0, 0], [0, 0, 0]], dtype=np.float32, columns=mi) + df.loc[:, (1, slice(2, 3))] = np.ones((2, 2), dtype=np.float32) + expected = DataFrame([[1, 1, 0], [1, 1, 0]], dtype=np.float32, columns=mi) + tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize("dtype", ["f8", "i8", "u8"]) def test_setitem_bool_with_numeric_index(self, dtype): # GH#36319 diff --git a/pandas/tests/reshape/test_pivot.py b/pandas/tests/reshape/test_pivot.py index f9b2a02920841..2b770a6c535f6 100644 --- a/pandas/tests/reshape/test_pivot.py +++ b/pandas/tests/reshape/test_pivot.py @@ -962,12 +962,14 @@ def test_margins_dtype(self): # GH 17013 df = self.data.copy() - df[["D", "E", "F"]] = np.arange(len(df) * 3).reshape(len(df), 3) + df[["D", "E", "F"]] = np.arange(len(df) * 3, dtype="int64").reshape(len(df), 3) mi_val = list(product(["bar", "foo"], ["one", "two"])) + [("All", "")] mi = MultiIndex.from_tuples(mi_val, names=("A", "B")) expected = DataFrame( - {"dull": [12, 21, 3, 9, 45], "shiny": [33, 0, 36, 51, 120]}, index=mi + {"dull": [12, 21, 3, 9, 45], "shiny": [33, 0, 36, 51, 120]}, + index=mi, + dtype="int64", ).rename_axis("C", axis=1) expected["All"] = expected["dull"] + expected["shiny"]