diff --git a/pandas/core/reshape/reshape.py b/pandas/core/reshape/reshape.py index eaeb4a50d0bf3..7e27c924bfbed 100644 --- a/pandas/core/reshape/reshape.py +++ b/pandas/core/reshape/reshape.py @@ -747,7 +747,18 @@ def _convert_level_number(level_num: int, columns: Index): if slice_len != levsize: chunk = this.loc[:, this.columns[loc]] chunk.columns = level_vals_nan.take(chunk.columns.codes[-1]) - value_slice = chunk.reindex(columns=level_vals_used).values + # Override fill value to prevent upcasting to float64 + # if we have lower precision floats + common_type = find_common_type(chunk.dtypes.tolist()) + if np.issubdtype(common_type, np.floating): + fill_value = common_type.type(np.nan) + elif is_extension_array_dtype(common_type): + fill_value = common_type.na_value + else: + fill_value = np.nan + value_slice = chunk.reindex( + columns=level_vals_used, fill_value=fill_value + ).values else: subset = this.iloc[:, loc] dtype = find_common_type(subset.dtypes.tolist()) diff --git a/pandas/tests/frame/test_stack_unstack.py b/pandas/tests/frame/test_stack_unstack.py index 889c44522f7bb..98cc8871d4d28 100644 --- a/pandas/tests/frame/test_stack_unstack.py +++ b/pandas/tests/frame/test_stack_unstack.py @@ -1481,6 +1481,19 @@ def test_stack(self, multiindex_year_month_day_dataframe_random_data): expected = ymd.unstack(0).stack(0) tm.assert_equal(result, expected) + def test_stack_nans_doesnt_upcast_float(self): + # GH 51059 + df = DataFrame({("n1", "q1"): [1], ("n2", "q2"): [2]}, dtype="float32") + res = df.stack(level=0) + exp = DataFrame( + { + "q1": np.array([1.0, np.nan], dtype="float32"), + "q2": np.array([np.nan, 2.0], dtype="float32"), + }, + index=MultiIndex.from_product([[0], ["n1", "n2"]]), + ) + tm.assert_frame_equal(res, exp) + @pytest.mark.parametrize( "idx, columns, exp_idx", [