diff --git a/doc/whats-new.rst b/doc/whats-new.rst index fe63a923fe6..8b74d3cdb1e 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -42,6 +42,9 @@ Bug fixes - Use mean of min/max years as offset in calculation of datetime64 mean (:issue:`10019`, :pull:`10035`). By `Kai Mühlbauer `_. +- Fix DataArray().drop_attrs(deep=False) and add support for attrs to + DataArray()._replace(). (:issue:`10027`, :pull:`10030`). By `Jan + Haacker `_. Documentation diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 6c4c17e76cd..ff2d5198548 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -1,5 +1,6 @@ from __future__ import annotations +import copy import datetime import warnings from collections.abc import ( @@ -522,6 +523,7 @@ def _replace( variable: Variable | None = None, coords=None, name: Hashable | None | Default = _default, + attrs=_default, indexes=None, ) -> Self: if variable is None: @@ -532,6 +534,11 @@ def _replace( indexes = self._indexes if name is _default: name = self.name + if attrs is _default: + attrs = copy.copy(self.attrs) + else: + variable = variable.copy() + variable.attrs = attrs return type(self)(variable, coords, name=name, indexes=indexes, fastpath=True) def _replace_maybe_drop_dims( @@ -7575,6 +7582,11 @@ def drop_attrs(self, *, deep: bool = True) -> Self: ------- DataArray """ - return ( - self._to_temp_dataset().drop_attrs(deep=deep).pipe(self._from_temp_dataset) - ) + if not deep: + return self._replace(attrs={}) + else: + return ( + self._to_temp_dataset() + .drop_attrs(deep=deep) + .pipe(self._from_temp_dataset) + ) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 7d1017f1e6b..75d6d919e19 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -1908,6 +1908,21 @@ def test_rename_dimension_coord_warnings(self) -> None: warnings.simplefilter("error") da.rename(x="x") + def test_replace(self) -> None: + # Tests the `attrs` replacement and whether it interferes with a + # `variable` replacement + da = self.mda + attrs1 = {"a1": "val1", "a2": 161} + x = np.ones((10, 20)) + v = Variable(["x", "y"], x) + assert da._replace(variable=v, attrs=attrs1).attrs == attrs1 + attrs2 = {"b1": "val2", "b2": 1312} + va = Variable(["x", "y"], x, attrs2) + # assuming passed `attrs` should prevail + assert da._replace(variable=va, attrs=attrs1).attrs == attrs1 + # assuming `va.attrs` should be adopted + assert da._replace(variable=va).attrs == attrs2 + def test_init_value(self) -> None: expected = DataArray( np.full((3, 4), 3), dims=["x", "y"], coords=[range(3), range(4)] @@ -2991,8 +3006,11 @@ def test_assign_attrs(self) -> None: def test_drop_attrs(self) -> None: # Mostly tested in test_dataset.py, but adding a very small test here - da = DataArray([], attrs=dict(a=1, b=2)) + coord_ = DataArray([], attrs=dict(d=3, e=4)) + da = DataArray([], attrs=dict(a=1, b=2)).assign_coords(dict(coord_=coord_)) assert da.drop_attrs().attrs == {} + assert da.drop_attrs().coord_.attrs == {} + assert da.drop_attrs(deep=False).coord_.attrs == dict(d=3, e=4) @pytest.mark.parametrize( "func", [lambda x: x.clip(0, 1), lambda x: np.float64(1.0) * x, np.abs, abs]