diff --git a/doc/indexing.rst b/doc/indexing.rst index 9af9db227bc..4c5b93db0b4 100644 --- a/doc/indexing.rst +++ b/doc/indexing.rst @@ -236,6 +236,7 @@ The :py:meth:`~xarray.Dataset.drop` method returns a new object with the listed index labels along a dimension dropped: .. ipython:: python + :okwarning: ds.drop(['IN', 'IL'], dim='space') diff --git a/doc/whats-new.rst b/doc/whats-new.rst index ae5dc1989cc..a8421d89e74 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -57,12 +57,17 @@ Enhancements - Added ``join='override'``. This only checks that index sizes are equal among objects and skips checking indexes for equality. By `Deepak Cherian `_. + - :py:func:`~xarray.concat` and :py:func:`~xarray.open_mfdataset` now support the ``join`` kwarg. It is passed down to :py:func:`~xarray.align`. By `Deepak Cherian `_. + - In :py:meth:`~xarray.Dataset.to_zarr`, passing ``mode`` is not mandatory if ``append_dim`` is set, as it will automatically be set to ``'a'`` internally. By `David Brochart `_. +- :py:meth:`~xarray.Dataset.drop` now supports keyword arguments; dropping index labels by specifying both ``dim`` and ``labels`` is deprecated (:issue:`2910`). + By `Gregory Gundersen `_. + Bug fixes ~~~~~~~~~ - Fix regression introduced in v0.12.2 where ``copy(deep=True)`` would convert diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 4250bf9564e..d25f24c1e28 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -54,6 +54,7 @@ ) from .coordinates import ( DatasetCoordinates, + DataArrayCoordinates, LevelCoordinatesSource, assert_coordinate_consistent, remap_label_indexers, @@ -3450,7 +3451,7 @@ def _assert_all_in_dataset( ) # Drop variables - @overload + @overload # noqa: F811 def drop( self, labels: Union[Hashable, Iterable[Hashable]], *, errors: str = "raise" ) -> "Dataset": @@ -3463,7 +3464,9 @@ def drop( ) -> "Dataset": ... - def drop(self, labels, dim=None, *, errors="raise"): # noqa: F811 + def drop( # noqa: F811 + self, labels=None, dim=None, *, errors="raise", **labels_kwargs + ): """Drop variables or index labels from this dataset. Parameters @@ -3479,34 +3482,75 @@ def drop(self, labels, dim=None, *, errors="raise"): # noqa: F811 any of the variable or index labels passed are not in the dataset. If 'ignore', any given labels that are in the dataset are dropped and no error is raised. + **labels_kwargs : {dim: label, ...}, optional + The keyword arguments form of ``dim`` and ``labels``. Returns ------- dropped : Dataset + + Examples + -------- + >>> data = np.random.randn(2, 3) + >>> labels = ['a', 'b', 'c'] + >>> ds = xr.Dataset({'A': (['x', 'y'], data), 'y': labels}) + >>> ds.drop(y=['a', 'c']) + + Dimensions: (x: 2, y: 1) + Coordinates: + * y (y) >> ds.drop(y='b') + + Dimensions: (x: 2, y: 2) + Coordinates: + * y (y) "Dataset": if errors == "raise": diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 66a7adfc95e..ba478686d61 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -250,6 +250,10 @@ def is_full_slice(value: Any) -> bool: return isinstance(value, slice) and value == slice(None) +def is_list_like(value: Any) -> bool: + return isinstance(value, list) or isinstance(value, tuple) + + def either_dict_or_kwargs( pos_kwargs: Optional[Mapping[Hashable, T]], kw_kwargs: Mapping[str, T], diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index f63aaf34f82..1b78d139485 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -2206,6 +2206,37 @@ def test_drop_index_labels(self): with raises_regex(ValueError, "does not have coordinate labels"): data.drop(1, "y") + def test_drop_labels_by_keyword(self): + # Tests for #2910: Support for a additional `drop()` API. + data = Dataset( + {"A": (["x", "y"], np.random.randn(2, 6)), "x": ["a", "b"], "y": range(6)} + ) + # Basic functionality. + assert len(data.coords["x"]) == 2 + + # This API is allowed but deprecated. + with pytest.warns(DeprecationWarning): + ds1 = data.drop(["a"], dim="x") + ds2 = data.drop(x="a") + ds3 = data.drop(x=["a"]) + ds4 = data.drop(x=["a", "b"]) + ds5 = data.drop(x=["a", "b"], y=range(0, 6, 2)) + + assert_array_equal(ds1.coords["x"], ["b"]) + assert_array_equal(ds2.coords["x"], ["b"]) + assert_array_equal(ds3.coords["x"], ["b"]) + assert ds4.coords["x"].size == 0 + assert ds5.coords["x"].size == 0 + assert_array_equal(ds5.coords["y"], [1, 3, 5]) + + # Error handling if user tries both approaches. + with pytest.raises(ValueError): + data.drop(labels=["a"], x="a") + with pytest.raises(ValueError): + data.drop(dim="x", x="a") + with pytest.raises(ValueError): + data.drop(labels=["a"], dim="x", x="a") + def test_drop_dims(self): data = xr.Dataset( {