diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 6d67d234020..0a8129a4952 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -117,6 +117,12 @@ Bug Fixes type error when applying reduction methods, due to the reduction methods being dynamically generated (:issue:`8136`). By `Andrew Scherer `_. +- :py:func:`concat` no longer raises ``TypeError: Cannot interpret + '' as a data type`` when the objects being joined carry + index coordinates with a mix of numpy and pandas extension dtypes, which + happens routinely under pandas 3 when one dimension coordinate came from a + :py:class:`pandas.Index` and another from a plain list (:issue:`11317`). + By `Dipak Chaudhari `_. .. _`pandas-dev/pandas#64793`: https://github.com/pandas-dev/pandas/pull/64793 diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py index ab9b7962020..116f444d297 100644 --- a/xarray/core/indexes.py +++ b/xarray/core/indexes.py @@ -789,6 +789,16 @@ def concat( indexes_coord_dtypes = {idx.coord_dtype for idx in indexes} if len(indexes_coord_dtypes) == 1: coord_dtype = next(iter(indexes_coord_dtypes)) + elif any( + isinstance(dtype, pd.api.extensions.ExtensionDtype) + for dtype in indexes_coord_dtypes + ): + # np.result_type cannot interpret a pandas extension dtype, and + # mixing one with a numpy dtype is ordinary now that pandas + # gives string indexes a StringDtype. _concat_indexes above has + # already had pandas promote these, so use the dtype it settled + # on rather than reimplementing pandas' rules here. + coord_dtype = new_pd_index.dtype else: coord_dtype = np.result_type(*indexes_coord_dtypes) diff --git a/xarray/tests/test_concat.py b/xarray/tests/test_concat.py index bc98d72d50c..08f5b668412 100644 --- a/xarray/tests/test_concat.py +++ b/xarray/tests/test_concat.py @@ -1491,6 +1491,29 @@ def test_concat_index_not_same_dim() -> None: concat([ds1, ds2], dim="x") +def test_concat_mixed_numpy_and_extension_coord_dtype() -> None: + # GH11317. Passing a pd.Index as the concat dim gives that index a pandas + # extension dtype (StringDtype under pandas 3), while a coord built from a + # plain list keeps a numpy None: + # The extension-dtype branch must not swallow the ordinary case: two numpy + # string coords of different widths still promote to the wider numpy dtype. + da = DataArray([0], dims=["d"], coords={"d": ["aaa"]}) + db = DataArray([1], dims=["d"], coords={"d": ["b"]}) + + assert concat([da, db], dim="d").coords["d"].dtype == np.dtype(" None: ds1 = Dataset(data_vars={"a": ("y", [0.1]), "b": 0.1}, coords={"x": 0.1})