From b65ce8666020ba3a0300154655d2e5c05884d73b Mon Sep 17 00:00:00 2001 From: David Huard Date: Mon, 16 Sep 2019 00:16:15 +0200 Subject: [PATCH] Honor `keep_attrs` in DataArray.quantile (#3305) * Added `keep_attrs` argument to Variable.quantile. TestDataArray.test_quantile now checks for attributes in output. * black * updated whats new. * removed vestigial comment. Switched default Variable.quantile keep_attrs to False. --- doc/whats-new.rst | 20 +++++++++++--------- xarray/core/dataset.py | 5 ++++- xarray/core/variable.py | 17 +++++++++++++---- xarray/tests/test_dataarray.py | 4 ++-- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index d81986cb948..ab4b17ff16d 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -26,8 +26,8 @@ Breaking changes - The ``isel_points`` and ``sel_points`` methods are removed, having been deprecated since v0.10.0. These are redundant with the ``isel`` / ``sel`` methods. - See :ref:`vectorized_indexing` for the details - By `Maximilian Roos `_ + See :ref:`vectorized_indexing` for the details + By `Maximilian Roos `_ - The ``inplace`` kwarg for public methods now raises an error, having been deprecated since v0.11.0. By `Maximilian Roos `_ @@ -52,12 +52,12 @@ Breaking changes error in a later release. (:issue:`3250`) by `Guido Imperiale `_. -- :py:meth:`~Dataset.to_dataset` requires ``name`` to be passed as a kwarg (previously ambiguous +- :py:meth:`~Dataset.to_dataset` requires ``name`` to be passed as a kwarg (previously ambiguous positional arguments were deprecated) - Reindexing with variables of a different dimension now raise an error (previously deprecated) -- :py:func:`~xarray.broadcast_array` is removed (previously deprecated in favor of +- :py:func:`~xarray.broadcast_array` is removed (previously deprecated in favor of :py:func:`~xarray.broadcast`) -- :py:meth:`~Variable.expand_dims` is removed (previously deprecated in favor of +- :py:meth:`~Variable.expand_dims` is removed (previously deprecated in favor of :py:meth:`~Variable.set_dims`) New functions/methods @@ -90,7 +90,7 @@ New functions/methods and `Maximilian Roos `_. - Added :py:meth:`DataArray.broadcast_like` and :py:meth:`Dataset.broadcast_like`. - By `Deepak Cherian `_ and `David Mertz + By `Deepak Cherian `_ and `David Mertz `_. - Dataset plotting API for visualizing dependencies between two `DataArray`s! @@ -131,14 +131,14 @@ Enhancements :py:meth:`DataArray.set_index`, as well are more specific error messages when the user passes invalid arguments (:issue:`3176`). By `Gregory Gundersen `_. - + - :py:func:`filter_by_attrs` now filters the coordinates as well as the variables. By `Spencer Jones `_. Bug fixes ~~~~~~~~~ -- Improve "missing dimensions" error message for :py:func:`~xarray.apply_ufunc` - (:issue:`2078`). +- Improve "missing dimensions" error message for :py:func:`~xarray.apply_ufunc` + (:issue:`2078`). By `Rick Russotto `_. - :py:meth:`~xarray.DataArray.assign_coords` now supports dictionary arguments (:issue:`3231`). @@ -170,6 +170,8 @@ Bug fixes dask compute (:issue:`3237`). By `Ulrich Herter `_. - Plots in 2 dimensions (pcolormesh, contour) now allow to specify levels as numpy array (:issue:`3284`). By `Mathias Hauser `_. +- Fixed bug in :meth:`DataArray.quantile` failing to keep attributes when + `keep_attrs` was True (:issue:`3304`). By David Huard `_. .. _whats-new.0.12.3: diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 1eeb5350dfe..8a53e7ba757 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -4831,7 +4831,10 @@ def quantile( # the former is often more efficient reduce_dims = None variables[name] = var.quantile( - q, dim=reduce_dims, interpolation=interpolation + q, + dim=reduce_dims, + interpolation=interpolation, + keep_attrs=keep_attrs, ) else: diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 2e9906ce5ae..b4b01f7ee49 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -1592,7 +1592,7 @@ def no_conflicts(self, other): """ return self.broadcast_equals(other, equiv=duck_array_ops.array_notnull_equiv) - def quantile(self, q, dim=None, interpolation="linear"): + def quantile(self, q, dim=None, interpolation="linear", keep_attrs=None): """Compute the qth quantile of the data along the specified dimension. Returns the qth quantiles(s) of the array elements. @@ -1615,6 +1615,10 @@ def quantile(self, q, dim=None, interpolation="linear"): * higher: ``j``. * nearest: ``i`` or ``j``, whichever is nearest. * midpoint: ``(i + j) / 2``. + keep_attrs : bool, optional + If True, the variable's attributes (`attrs`) will be copied from + the original object to the new one. If False (default), the new + object will be returned without attributes. Returns ------- @@ -1623,7 +1627,7 @@ def quantile(self, q, dim=None, interpolation="linear"): is a scalar. If multiple percentiles are given, first axis of the result corresponds to the quantile and a quantile dimension is added to the return array. The other dimensions are the - dimensions that remain after the reduction of the array. + dimensions that remain after the reduction of the array. See Also -------- @@ -1651,14 +1655,19 @@ def quantile(self, q, dim=None, interpolation="linear"): axis = None new_dims = [] - # only add the quantile dimension if q is array like + # Only add the quantile dimension if q is array-like if q.ndim != 0: new_dims = ["quantile"] + new_dims qs = np.nanpercentile( self.data, q * 100.0, axis=axis, interpolation=interpolation ) - return Variable(new_dims, qs) + + if keep_attrs is None: + keep_attrs = _get_keep_attrs(default=False) + attrs = self._attrs if keep_attrs else None + + return Variable(new_dims, qs, attrs) def rank(self, dim, pct=False): """Ranks the data. diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 78d9ace6be1..49980c75b15 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -2333,17 +2333,17 @@ def test_reduce_out(self): with pytest.raises(TypeError): orig.mean(out=np.ones(orig.shape)) - # skip due to bug in older versions of numpy.nanpercentile def test_quantile(self): for q in [0.25, [0.50], [0.25, 0.75]]: for axis, dim in zip( [None, 0, [0], [0, 1]], [None, "x", ["x"], ["x", "y"]] ): - actual = self.dv.quantile(q, dim=dim) + actual = DataArray(self.va).quantile(q, dim=dim, keep_attrs=True) expected = np.nanpercentile( self.dv.values, np.array(q) * 100, axis=axis ) np.testing.assert_allclose(actual.values, expected) + assert actual.attrs == self.attrs def test_reduce_keep_attrs(self): # Test dropped attrs