From cb2c244422c878474e7720a80ead4485d000366a Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Fri, 5 Sep 2025 11:16:27 -0400 Subject: [PATCH 1/3] fix: don't destroy dtypes in to_dataframe --- xarray/core/coordinates.py | 6 ++++-- xarray/tests/test_dataarray.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index df6abe0a5cb..403bf4bc6f6 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -180,11 +180,13 @@ def to_index(self, ordered_dims: Sequence[Hashable] | None = None) -> pd.Index: np.tile(np.repeat(code, repeat_counts[i]), tile_counts[i]) for code in codes ] - level_list += [list(level) for level in levels] + level_list += levels names += index.names return pd.MultiIndex( - levels=level_list, codes=[list(c) for c in code_list], names=names + levels=level_list, + codes=[list(c) for c in code_list], + names=names, # type: ignore[arg-type] ) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 3bf16b3c70d..41604ef00ee 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -3528,6 +3528,34 @@ def test_to_dataframe_0length(self) -> None: assert len(actual) == 0 assert_array_equal(actual.index.names, list("ABC")) + @pytest.mark.parametrize( + "x_dtype,y_dtype,v_dtype", + [ + (np.uint32, np.float32, np.uint32), + (np.int16, np.float64, np.int64), + (np.uint8, np.float32, np.uint16), + (np.int32, np.float32, np.int8), + ], + ) + def test_to_dataframe_coord_dtypes_2d(self, x_dtype, y_dtype, v_dtype) -> None: + x = np.array([1], dtype=x_dtype) + y = np.array([1.0], dtype=y_dtype) + v = np.array([[42]], dtype=v_dtype) + + da = DataArray(v, dims=["x", "y"], coords={"x": x, "y": y}) + df = da.to_dataframe(name="v").reset_index() + + # Check that coordinate dtypes are preserved + assert df["x"].dtype == np.dtype(x_dtype), ( + f"x coord: expected {x_dtype}, got {df['x'].dtype}" + ) + assert df["y"].dtype == np.dtype(y_dtype), ( + f"y coord: expected {y_dtype}, got {df['y'].dtype}" + ) + assert df["v"].dtype == np.dtype(v_dtype), ( + f"v data: expected {v_dtype}, got {df['v'].dtype}" + ) + @requires_dask_expr @requires_dask @pytest.mark.xfail(not has_dask_ge_2025_1_0, reason="dask-expr is broken") From 9cb6d8090c09bd26aa2abe793ea27eec285dcbc6 Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Fri, 5 Sep 2025 11:25:40 -0400 Subject: [PATCH 2/3] add a used-ignore --- xarray/core/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index 403bf4bc6f6..c579904fab6 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -186,7 +186,7 @@ def to_index(self, ordered_dims: Sequence[Hashable] | None = None) -> pd.Index: return pd.MultiIndex( levels=level_list, codes=[list(c) for c in code_list], - names=names, # type: ignore[arg-type] + names=names, # type: ignore[arg-type,unused-ignore] ) From f03912061a871f636e3f520e06a7318b5f21fc9a Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Fri, 5 Sep 2025 11:31:38 -0400 Subject: [PATCH 3/3] put type ignore in correct spot --- xarray/core/coordinates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py index c579904fab6..ab5bf7408f3 100644 --- a/xarray/core/coordinates.py +++ b/xarray/core/coordinates.py @@ -184,9 +184,9 @@ def to_index(self, ordered_dims: Sequence[Hashable] | None = None) -> pd.Index: names += index.names return pd.MultiIndex( - levels=level_list, + levels=level_list, # type: ignore[arg-type,unused-ignore] codes=[list(c) for c in code_list], - names=names, # type: ignore[arg-type,unused-ignore] + names=names, )