From a268eaa66f832fb3b5983b8473f41f1707954a02 Mon Sep 17 00:00:00 2001 From: nameloCmaS Date: Sun, 21 Jan 2024 19:34:16 +0000 Subject: [PATCH 01/30] Fix doc building following breaking change in Pandas 2.2.0 (#8633) https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#deprecate-aliases-m-q-y-etc-in-favour-of-me-qe-ye-etc-for-offsets --- doc/whats-new.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index b62712ce96a..897d5bec053 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -7262,6 +7262,7 @@ Breaking changes such `'DJF'`: .. ipython:: python + :okwarning: ds = xray.Dataset({"t": pd.date_range("2000-01-01", periods=12, freq="M")}) ds["t.season"] From a913cfe598c22d8706e5631b6a28136efca144c3 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 12:18:22 -0800 Subject: [PATCH 02/30] Workaround broken test from pyarrow (#8634) While fixing the previous issue, I introduced another (but didn't see it because of the errors from the test suite, probably should have looked closer...) This doesn't fix the behavior, but I think it's minor so fine to push off. I do prioritize getting the tests where pass vs failure is meaningful again --- xarray/tests/test_dask.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py index 386f1479c26..4412f95796d 100644 --- a/xarray/tests/test_dask.py +++ b/xarray/tests/test_dask.py @@ -802,7 +802,7 @@ def test_to_dask_dataframe(self): assert isinstance(actual, dd.DataFrame) # use the .equals from pandas to check dataframes are equivalent - assert_frame_equal(expected.compute(), actual.compute()) + assert_frame_equal(actual.compute(), expected.compute()) # test if no index is given expected = dd.from_pandas(expected_pd.reset_index(drop=False), chunksize=4) @@ -810,7 +810,7 @@ def test_to_dask_dataframe(self): actual = ds.to_dask_dataframe(set_index=False) assert isinstance(actual, dd.DataFrame) - assert_frame_equal(expected.compute(), actual.compute()) + assert_frame_equal(actual.compute(), expected.compute()) def test_to_dask_dataframe_2D(self): # Test if 2-D dataset is supplied @@ -830,7 +830,11 @@ def test_to_dask_dataframe_2D(self): actual = ds.to_dask_dataframe(set_index=False) assert isinstance(actual, dd.DataFrame) - assert_frame_equal(expected, actual.compute()) + # TOOD: not sure if this is the correct behavior, but currently pandas with + # pyarrow installed will return a `string[pyarrow]` type, so matching that until + # we can fix the underlying issue + expected["y"] = expected["y"].astype("string[pyarrow]") + assert_frame_equal(actual.compute(), expected) @pytest.mark.xfail(raises=NotImplementedError) def test_to_dask_dataframe_2D_set_index(self): From f4ed09d30dc4317c9177f723568b2a32c2a1702f Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:03:33 -0800 Subject: [PATCH 03/30] xfail pyarrow test (#8635) * xfail pyarrow test Sorry for the repeated PR -- some tests passed but some failed without pyarrow installed. So this xfails the test for the moment --- xarray/tests/test_dask.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py index 4412f95796d..7d80ad01b17 100644 --- a/xarray/tests/test_dask.py +++ b/xarray/tests/test_dask.py @@ -812,6 +812,10 @@ def test_to_dask_dataframe(self): assert isinstance(actual, dd.DataFrame) assert_frame_equal(actual.compute(), expected.compute()) + @pytest.mark.xfail( + reason="Currently pandas with pyarrow installed will return a `string[pyarrow]` type, " + "which causes the `y` column to have a different type depending on whether pyarrow is installed" + ) def test_to_dask_dataframe_2D(self): # Test if 2-D dataset is supplied w = np.random.randn(2, 3) @@ -830,10 +834,6 @@ def test_to_dask_dataframe_2D(self): actual = ds.to_dask_dataframe(set_index=False) assert isinstance(actual, dd.DataFrame) - # TOOD: not sure if this is the correct behavior, but currently pandas with - # pyarrow installed will return a `string[pyarrow]` type, so matching that until - # we can fix the underlying issue - expected["y"] = expected["y"].astype("string[pyarrow]") assert_frame_equal(actual.compute(), expected) @pytest.mark.xfail(raises=NotImplementedError) From e4bdc3930345eeaa8276bfcbc346ef23607453fd Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:08:38 -0800 Subject: [PATCH 04/30] Don't show stdlib paths for `user_level_warnings` (#8625) Was previously seeing: ``` :801: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`. ``` Now: ``` /Users/maximilian/workspace/xarray/xarray/tests/test_dataset.py:701: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`. assert ds.dims == ds.sizes ``` It's a heuristic, so not perfect, but I think very likely to be accurate. Any contrary cases very welcome... --- xarray/core/utils.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/xarray/core/utils.py b/xarray/core/utils.py index bf2b2efa13e..3bbf06cf967 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -60,6 +60,7 @@ ValuesView, ) from enum import Enum +from pathlib import Path from typing import ( TYPE_CHECKING, Any, @@ -1224,12 +1225,12 @@ def module_available(module: str, minversion: str | None = None) -> bool: def find_stack_level(test_mode=False) -> int: - """Find the first place in the stack that is not inside xarray. + """Find the first place in the stack that is not inside xarray or the Python standard library. This is unless the code emanates from a test, in which case we would prefer to see the xarray source. - This function is taken from pandas. + This function is taken from pandas and modified to exclude standard library paths. Parameters ---------- @@ -1240,19 +1241,27 @@ def find_stack_level(test_mode=False) -> int: Returns ------- stacklevel : int - First level in the stack that is not part of xarray. + First level in the stack that is not part of xarray or the Python standard library. """ import xarray as xr - pkg_dir = os.path.dirname(xr.__file__) - test_dir = os.path.join(pkg_dir, "tests") + pkg_dir = Path(xr.__file__).parent + test_dir = pkg_dir / "tests" + + std_lib_dir = Path(sys.modules["os"].__file__).parent # Standard library path - # https://stackoverflow.com/questions/17407119/python-inspect-stack-is-slow frame = inspect.currentframe() n = 0 while frame: fname = inspect.getfile(frame) - if fname.startswith(pkg_dir) and (not fname.startswith(test_dir) or test_mode): + if ( + fname.startswith(str(pkg_dir)) + and (not fname.startswith(str(test_dir)) or test_mode) + ) or ( + fname.startswith(str(std_lib_dir)) + and "site-packages" not in fname + and "dist-packages" not in fname + ): frame = frame.f_back n += 1 else: From 131c023385dc59b14a98c3f28507082e8fdcb3ce Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:42:21 -0800 Subject: [PATCH 05/30] xfail another dask/pyarrow test (#8636) * xfail another dask/pyarrow test Unsure why this wasn't showing prior -- having tests fail in the good state does make it much more difficult to ensure everything is fixed before merging. --- xarray/core/utils.py | 7 ++++++- xarray/tests/test_dask.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 3bbf06cf967..9da937b996e 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -1248,7 +1248,12 @@ def find_stack_level(test_mode=False) -> int: pkg_dir = Path(xr.__file__).parent test_dir = pkg_dir / "tests" - std_lib_dir = Path(sys.modules["os"].__file__).parent # Standard library path + std_lib_init = sys.modules["os"].__file__ + # Mostly to appease mypy; I don't think this can happen... + if std_lib_init is None: + return 0 + + std_lib_dir = Path(std_lib_init).parent frame = inspect.currentframe() n = 0 diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py index 7d80ad01b17..d384d6a07fa 100644 --- a/xarray/tests/test_dask.py +++ b/xarray/tests/test_dask.py @@ -867,6 +867,10 @@ def test_to_dask_dataframe_coordinates(self): assert isinstance(actual, dd.DataFrame) assert_frame_equal(expected.compute(), actual.compute()) + @pytest.mark.xfail( + reason="Currently pandas with pyarrow installed will return a `string[pyarrow]` type, " + "which causes the index to have a different type depending on whether pyarrow is installed" + ) def test_to_dask_dataframe_not_daskarray(self): # Test if DataArray is not a dask array x = np.random.randn(10) From 32c164518ab1042d485b389fc7e7849246c1ad51 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:00:58 -0800 Subject: [PATCH 06/30] xfail a cftime test (#8637) https://github.com/pydata/xarray/pull/8636#issuecomment-1902775153 --- xarray/tests/test_cftime_offsets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xarray/tests/test_cftime_offsets.py b/xarray/tests/test_cftime_offsets.py index 0ffcb5e8ab9..68231ecd143 100644 --- a/xarray/tests/test_cftime_offsets.py +++ b/xarray/tests/test_cftime_offsets.py @@ -1377,6 +1377,9 @@ def test_date_range_errors() -> None: @requires_cftime +@pytest.mark.xfail( + reason="https://github.com/pydata/xarray/pull/8636#issuecomment-1902775153" +) @pytest.mark.parametrize( "start,freq,cal_src,cal_tgt,use_cftime,exp0,exp_pd", [ From 5bd3d8bb4c8a8565bb4ac8f929edd2a267e73018 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:04:53 -0800 Subject: [PATCH 07/30] Silence deprecation warning from `.dims` in tests (#8639) --- xarray/tests/test_dataset.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 664d108b89c..fa9448f2f41 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -698,7 +698,11 @@ def test_properties(self) -> None: # TODO change after deprecation cycle in GH #8500 is complete assert isinstance(ds.dims.mapping, dict) assert type(ds.dims.mapping) is dict # noqa: E721 - assert ds.dims == ds.sizes + with pytest.warns( + FutureWarning, + match=" To access a mapping from dimension names to lengths, please use `Dataset.sizes`", + ): + assert ds.dims == ds.sizes assert ds.sizes == {"dim1": 8, "dim2": 9, "dim3": 10, "time": 20} # dtypes From e571d1cd469f4ea6941aeb20c26827ed8fbb24a0 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:28:06 -0800 Subject: [PATCH 08/30] Use `T_DataArray` in `Weighted` (#8630) * Use `T_DataArray` in `Weighted` Allows subtypes. (I had this in my git stash, so commiting it...) * Apply suggestions from code review --- xarray/core/weighted.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/xarray/core/weighted.py b/xarray/core/weighted.py index 53ff6db5f28..a86121eb9c8 100644 --- a/xarray/core/weighted.py +++ b/xarray/core/weighted.py @@ -10,7 +10,7 @@ from xarray.core.alignment import align, broadcast from xarray.core.computation import apply_ufunc, dot from xarray.core.pycompat import is_duck_dask_array -from xarray.core.types import Dims, T_Xarray +from xarray.core.types import Dims, T_DataArray, T_Xarray from xarray.util.deprecation_helpers import _deprecate_positional_args # Weighted quantile methods are a subset of the numpy supported quantile methods. @@ -145,7 +145,7 @@ class Weighted(Generic[T_Xarray]): __slots__ = ("obj", "weights") - def __init__(self, obj: T_Xarray, weights: DataArray) -> None: + def __init__(self, obj: T_Xarray, weights: T_DataArray) -> None: """ Create a Weighted object @@ -189,7 +189,7 @@ def _weight_check(w): _weight_check(weights.data) self.obj: T_Xarray = obj - self.weights: DataArray = weights + self.weights: T_DataArray = weights def _check_dim(self, dim: Dims): """raise an error if any dimension is missing""" @@ -208,11 +208,11 @@ def _check_dim(self, dim: Dims): @staticmethod def _reduce( - da: DataArray, - weights: DataArray, + da: T_DataArray, + weights: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """reduce using dot; equivalent to (da * weights).sum(dim, skipna) for internal use only @@ -230,7 +230,7 @@ def _reduce( # DataArray (if `weights` has additional dimensions) return dot(da, weights, dim=dim) - def _sum_of_weights(self, da: DataArray, dim: Dims = None) -> DataArray: + def _sum_of_weights(self, da: T_DataArray, dim: Dims = None) -> T_DataArray: """Calculate the sum of weights, accounting for missing values""" # we need to mask data values that are nan; else the weights are wrong @@ -255,10 +255,10 @@ def _sum_of_weights(self, da: DataArray, dim: Dims = None) -> DataArray: def _sum_of_squares( self, - da: DataArray, + da: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Reduce a DataArray by a weighted ``sum_of_squares`` along some dimension(s).""" demeaned = da - da.weighted(self.weights).mean(dim=dim) @@ -267,20 +267,20 @@ def _sum_of_squares( def _weighted_sum( self, - da: DataArray, + da: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Reduce a DataArray by a weighted ``sum`` along some dimension(s).""" return self._reduce(da, self.weights, dim=dim, skipna=skipna) def _weighted_mean( self, - da: DataArray, + da: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Reduce a DataArray by a weighted ``mean`` along some dimension(s).""" weighted_sum = self._weighted_sum(da, dim=dim, skipna=skipna) @@ -291,10 +291,10 @@ def _weighted_mean( def _weighted_var( self, - da: DataArray, + da: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Reduce a DataArray by a weighted ``var`` along some dimension(s).""" sum_of_squares = self._sum_of_squares(da, dim=dim, skipna=skipna) @@ -305,21 +305,21 @@ def _weighted_var( def _weighted_std( self, - da: DataArray, + da: T_DataArray, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Reduce a DataArray by a weighted ``std`` along some dimension(s).""" - return cast("DataArray", np.sqrt(self._weighted_var(da, dim, skipna))) + return cast("T_DataArray", np.sqrt(self._weighted_var(da, dim, skipna))) def _weighted_quantile( self, - da: DataArray, + da: T_DataArray, q: ArrayLike, dim: Dims = None, skipna: bool | None = None, - ) -> DataArray: + ) -> T_DataArray: """Apply a weighted ``quantile`` to a DataArray along some dimension(s).""" def _get_h(n: float, q: np.ndarray, method: QUANTILE_METHODS) -> np.ndarray: From 5a92d48ab29200730cd09a2f1e1e7aaa39228aaa Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 22 Jan 2024 09:01:24 +0100 Subject: [PATCH 09/30] rename "Y" freq string to "YE" (pandas parity) (#8629) * rename "Y" freq string to "YE" (pandas parity) * Update xarray/coding/cftime_offsets.py Co-authored-by: Spencer Clark * fix table * changelog * remove unneeded elif branch --------- Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Co-authored-by: Spencer Clark --- doc/whats-new.rst | 9 ++++++ xarray/coding/cftime_offsets.py | 43 +++++++++++++++-------------- xarray/tests/test_cftime_offsets.py | 12 ++++---- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 897d5bec053..b810be83457 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -26,10 +26,19 @@ New Features Breaking changes ~~~~~~~~~~~~~~~~ +- Following pandas, :py:meth:`infer_freq` will return ``"YE"``, instead of ``"Y"`` (formerly ``"A"``). + This is to be consistent with the deprecation of the latter frequency string in pandas 2.2. + This is a follow up to :pull:`8415` (:issue:`8612`, :pull:`8629`). + By `Mathias Hauser `_. Deprecations ~~~~~~~~~~~~ +- Following pandas, the frequency string ``"Y"`` (formerly ``"A"``) is deprecated in + favor of ``"YE"``. These strings are used, for example, in :py:func:`date_range`, + :py:func:`cftime_range`, :py:meth:`DataArray.resample`, and :py:meth:`Dataset.resample` + among others (:issue:`8612`, :pull:`8629`). + By `Mathias Hauser `_. Bug fixes ~~~~~~~~~ diff --git a/xarray/coding/cftime_offsets.py b/xarray/coding/cftime_offsets.py index 100f3b249d2..0f4c46bb00e 100644 --- a/xarray/coding/cftime_offsets.py +++ b/xarray/coding/cftime_offsets.py @@ -573,7 +573,7 @@ def rollback(self, date): class YearEnd(YearOffset): - _freq = "Y" + _freq = "YE" _day_option = "end" _default_month = 12 @@ -669,6 +669,7 @@ def _generate_anchored_offsets(base_freq, offset): "A": YearEnd, "AS": YearBegin, "Y": YearEnd, + "YE": YearEnd, "YS": YearBegin, "Q": partial(QuarterEnd, month=12), "QE": partial(QuarterEnd, month=12), @@ -691,6 +692,7 @@ def _generate_anchored_offsets(base_freq, offset): **_generate_anchored_offsets("A", YearEnd), **_generate_anchored_offsets("YS", YearBegin), **_generate_anchored_offsets("Y", YearEnd), + **_generate_anchored_offsets("YE", YearEnd), **_generate_anchored_offsets("QS", QuarterBegin), **_generate_anchored_offsets("Q", QuarterEnd), **_generate_anchored_offsets("QE", QuarterEnd), @@ -716,7 +718,8 @@ def _generate_anchored_deprecated_frequencies(deprecated, recommended): _DEPRECATED_FREQUENICES = { - "A": "Y", + "A": "YE", + "Y": "YE", "AS": "YS", "Q": "QE", "M": "ME", @@ -725,7 +728,8 @@ def _generate_anchored_deprecated_frequencies(deprecated, recommended): "S": "s", "L": "ms", "U": "us", - **_generate_anchored_deprecated_frequencies("A", "Y"), + **_generate_anchored_deprecated_frequencies("A", "YE"), + **_generate_anchored_deprecated_frequencies("Y", "YE"), **_generate_anchored_deprecated_frequencies("AS", "YS"), **_generate_anchored_deprecated_frequencies("Q", "QE"), } @@ -979,7 +983,7 @@ def cftime_range( +--------+--------------------------+ | Alias | Description | +========+==========================+ - | Y | Year-end frequency | + | YE | Year-end frequency | +--------+--------------------------+ | YS | Year-start frequency | +--------+--------------------------+ @@ -1009,29 +1013,29 @@ def cftime_range( +------------+--------------------------------------------------------------------+ | Alias | Description | +============+====================================================================+ - | Y(S)-JAN | Annual frequency, anchored at the end (or beginning) of January | + | Y(E,S)-JAN | Annual frequency, anchored at the (end, beginning) of January | +------------+--------------------------------------------------------------------+ - | Y(S)-FEB | Annual frequency, anchored at the end (or beginning) of February | + | Y(E,S)-FEB | Annual frequency, anchored at the (end, beginning) of February | +------------+--------------------------------------------------------------------+ - | Y(S)-MAR | Annual frequency, anchored at the end (or beginning) of March | + | Y(E,S)-MAR | Annual frequency, anchored at the (end, beginning) of March | +------------+--------------------------------------------------------------------+ - | Y(S)-APR | Annual frequency, anchored at the end (or beginning) of April | + | Y(E,S)-APR | Annual frequency, anchored at the (end, beginning) of April | +------------+--------------------------------------------------------------------+ - | Y(S)-MAY | Annual frequency, anchored at the end (or beginning) of May | + | Y(E,S)-MAY | Annual frequency, anchored at the (end, beginning) of May | +------------+--------------------------------------------------------------------+ - | Y(S)-JUN | Annual frequency, anchored at the end (or beginning) of June | + | Y(E,S)-JUN | Annual frequency, anchored at the (end, beginning) of June | +------------+--------------------------------------------------------------------+ - | Y(S)-JUL | Annual frequency, anchored at the end (or beginning) of July | + | Y(E,S)-JUL | Annual frequency, anchored at the (end, beginning) of July | +------------+--------------------------------------------------------------------+ - | Y(S)-AUG | Annual frequency, anchored at the end (or beginning) of August | + | Y(E,S)-AUG | Annual frequency, anchored at the (end, beginning) of August | +------------+--------------------------------------------------------------------+ - | Y(S)-SEP | Annual frequency, anchored at the end (or beginning) of September | + | Y(E,S)-SEP | Annual frequency, anchored at the (end, beginning) of September | +------------+--------------------------------------------------------------------+ - | Y(S)-OCT | Annual frequency, anchored at the end (or beginning) of October | + | Y(E,S)-OCT | Annual frequency, anchored at the (end, beginning) of October | +------------+--------------------------------------------------------------------+ - | Y(S)-NOV | Annual frequency, anchored at the end (or beginning) of November | + | Y(E,S)-NOV | Annual frequency, anchored at the (end, beginning) of November | +------------+--------------------------------------------------------------------+ - | Y(S)-DEC | Annual frequency, anchored at the end (or beginning) of December | + | Y(E,S)-DEC | Annual frequency, anchored at the (end, beginning) of December | +------------+--------------------------------------------------------------------+ | Q(E,S)-JAN | Quarter frequency, anchored at the (end, beginning) of January | +------------+--------------------------------------------------------------------+ @@ -1311,11 +1315,8 @@ def date_range_like(source, calendar, use_cftime=None): freq = freq.replace("QE", "Q") elif isinstance(freq_as_offset, YearBegin) and "YS" in freq: freq = freq.replace("YS", "AS") - elif isinstance(freq_as_offset, YearEnd) and "Y-" in freq: - # Check for and replace "Y-" instead of just "Y" to prevent - # corrupting anchored offsets that contain "Y" in the month - # abbreviation, e.g. "Y-MAY" -> "A-MAY". - freq = freq.replace("Y-", "A-") + elif isinstance(freq_as_offset, YearEnd) and "YE" in freq: + freq = freq.replace("YE", "A") use_cftime = _should_cftime_be_used(source, calendar, use_cftime) diff --git a/xarray/tests/test_cftime_offsets.py b/xarray/tests/test_cftime_offsets.py index 68231ecd143..1dd8ddb4151 100644 --- a/xarray/tests/test_cftime_offsets.py +++ b/xarray/tests/test_cftime_offsets.py @@ -157,7 +157,7 @@ def test_year_offset_constructor_invalid_month(offset, invalid_month, exception) (MonthBegin(), "MS"), (MonthEnd(), "ME"), (YearBegin(), "YS-JAN"), - (YearEnd(), "Y-DEC"), + (YearEnd(), "YE-DEC"), (QuarterBegin(), "QS-MAR"), (QuarterEnd(), "QE-MAR"), (Day(), "D"), @@ -1387,7 +1387,7 @@ def test_date_range_errors() -> None: ("2020-02-01", "ME", "noleap", "gregorian", True, "2020-02-29", True), ("2020-02-01", "QE-DEC", "noleap", "gregorian", True, "2020-03-31", True), ("2020-02-01", "YS-FEB", "noleap", "gregorian", True, "2020-02-01", True), - ("2020-02-01", "Y-FEB", "noleap", "gregorian", True, "2020-02-29", True), + ("2020-02-01", "YE-FEB", "noleap", "gregorian", True, "2020-02-29", True), ("2020-02-28", "3h", "all_leap", "gregorian", False, "2020-02-28", True), ("2020-03-30", "ME", "360_day", "gregorian", False, "2020-03-31", True), ("2020-03-31", "ME", "gregorian", "360_day", None, "2020-03-30", False), @@ -1409,8 +1409,8 @@ def test_date_range_like(start, freq, cal_src, cal_tgt, use_cftime, exp0, exp_pd elif "YS" in freq: freq = freq.replace("YS", "AS") expected_pandas_freq = freq - elif "Y-" in freq: - freq = freq.replace("Y-", "A-") + elif "YE-" in freq: + freq = freq.replace("YE-", "A-") expected_pandas_freq = freq elif "h" in freq: expected_pandas_freq = freq.replace("h", "H") @@ -1534,7 +1534,9 @@ def test_cftime_or_date_range_inclusive_None(function) -> None: np.testing.assert_equal(result_None.values, result_both.values) -@pytest.mark.parametrize("freq", ["A", "AS", "Q", "M", "H", "T", "S", "L", "U"]) +@pytest.mark.parametrize( + "freq", ["A", "AS", "Q", "M", "H", "T", "S", "L", "U", "Y", "A-MAY"] +) def test_to_offset_deprecation_warning(freq): # Test for deprecations outlined in GitHub issue #8394 with pytest.warns(FutureWarning, match="is deprecated"): From d6deb46a098eaebff9a44c572d86c07dfaa5997a Mon Sep 17 00:00:00 2001 From: nameloCmaS Date: Mon, 22 Jan 2024 23:47:03 +0000 Subject: [PATCH 10/30] Pandas 2.2.0 test fixes (#8638) * Fix doc building following breaking change in Pandas 2.2.0 https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#deprecate-aliases-m-q-y-etc-in-favour-of-me-qe-ye-etc-for-offsets * Further fixes for changed Pandas 2.2 date aliases Fix docstrings following Pandas 2.2 date aliases changed * Update test_cftime_offsets.py * Update whats-new.rst Changed to code-block rather than suppressing the warning as PR comment. * Update dataarray.py Reverted due to different precision on aarch64 leading to different output. * Update test_accessor_dt.py Reverted * Update test_cftime_offsets.py Reverted changes. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update _aggregations.py Reversed changes * Update whats-new.rst Fixed hangover from clashing commits. * Update generate_aggregations.py * Update _aggregations.py Updated with xarray/util/generate_aggregations.py * Update whats-new.rst Corrected the correction... --------- Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- doc/whats-new.rst | 13 +- xarray/backends/api.py | 2 +- xarray/core/_aggregations.py | 264 +++++++++++++-------------- xarray/core/accessor_dt.py | 2 +- xarray/core/dataarray.py | 2 +- xarray/util/generate_aggregations.py | 8 +- 6 files changed, 148 insertions(+), 143 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index b810be83457..3364087143b 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -7270,11 +7270,16 @@ Breaking changes - The ``season`` datetime shortcut now returns an array of string labels such `'DJF'`: - .. ipython:: python - :okwarning: + .. code-block:: ipython - ds = xray.Dataset({"t": pd.date_range("2000-01-01", periods=12, freq="M")}) - ds["t.season"] + In[92]: ds = xray.Dataset({"t": pd.date_range("2000-01-01", periods=12, freq="M")}) + + In[93]: ds["t.season"] + Out[93]: + + array(['DJF', 'DJF', 'MAM', ..., 'SON', 'SON', 'DJF'], dtype='>> ds = xr.Dataset( ... {"a": ("time", np.linspace(0, 1, 48))}, - ... coords={"time": pd.date_range("2010-01-01", freq="M", periods=48)}, + ... coords={"time": pd.date_range("2010-01-01", freq="ME", periods=48)}, ... ) >>> ds diff --git a/xarray/core/_aggregations.py b/xarray/core/_aggregations.py index 0d4b4413b7c..e214c2c7c5a 100644 --- a/xarray/core/_aggregations.py +++ b/xarray/core/_aggregations.py @@ -77,7 +77,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -149,7 +149,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -221,7 +221,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -299,7 +299,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -386,7 +386,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -477,7 +477,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -575,7 +575,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -682,7 +682,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -786,7 +786,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -890,7 +890,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -990,7 +990,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1081,7 +1081,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1174,7 +1174,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1273,7 +1273,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1339,7 +1339,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1405,7 +1405,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1477,7 +1477,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1556,7 +1556,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1639,7 +1639,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1729,7 +1729,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1826,7 +1826,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -1920,7 +1920,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2014,7 +2014,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2104,7 +2104,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2187,7 +2187,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2276,7 +2276,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2401,7 +2401,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2499,7 +2499,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2597,7 +2597,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2701,7 +2701,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2817,7 +2817,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -2935,7 +2935,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3060,7 +3060,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3197,7 +3197,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3331,7 +3331,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3465,7 +3465,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3595,7 +3595,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3698,7 +3698,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3799,7 +3799,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3928,7 +3928,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -3942,7 +3942,7 @@ def count( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").count() + >>> ds.resample(time="3ME").count() Dimensions: (time: 3) Coordinates: @@ -4026,7 +4026,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4040,7 +4040,7 @@ def all( Data variables: da (time) bool True True True True True False - >>> ds.resample(time="3M").all() + >>> ds.resample(time="3ME").all() Dimensions: (time: 3) Coordinates: @@ -4124,7 +4124,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4138,7 +4138,7 @@ def any( Data variables: da (time) bool True True True True True False - >>> ds.resample(time="3M").any() + >>> ds.resample(time="3ME").any() Dimensions: (time: 3) Coordinates: @@ -4228,7 +4228,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4242,7 +4242,7 @@ def max( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").max() + >>> ds.resample(time="3ME").max() Dimensions: (time: 3) Coordinates: @@ -4252,7 +4252,7 @@ def max( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").max(skipna=False) + >>> ds.resample(time="3ME").max(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4344,7 +4344,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4358,7 +4358,7 @@ def min( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").min() + >>> ds.resample(time="3ME").min() Dimensions: (time: 3) Coordinates: @@ -4368,7 +4368,7 @@ def min( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").min(skipna=False) + >>> ds.resample(time="3ME").min(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4462,7 +4462,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4476,7 +4476,7 @@ def mean( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").mean() + >>> ds.resample(time="3ME").mean() Dimensions: (time: 3) Coordinates: @@ -4486,7 +4486,7 @@ def mean( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").mean(skipna=False) + >>> ds.resample(time="3ME").mean(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4587,7 +4587,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4601,7 +4601,7 @@ def prod( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").prod() + >>> ds.resample(time="3ME").prod() Dimensions: (time: 3) Coordinates: @@ -4611,7 +4611,7 @@ def prod( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").prod(skipna=False) + >>> ds.resample(time="3ME").prod(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4621,7 +4621,7 @@ def prod( Specify ``min_count`` for finer control over when NaNs are ignored. - >>> ds.resample(time="3M").prod(skipna=True, min_count=2) + >>> ds.resample(time="3ME").prod(skipna=True, min_count=2) Dimensions: (time: 3) Coordinates: @@ -4724,7 +4724,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4738,7 +4738,7 @@ def sum( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").sum() + >>> ds.resample(time="3ME").sum() Dimensions: (time: 3) Coordinates: @@ -4748,7 +4748,7 @@ def sum( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").sum(skipna=False) + >>> ds.resample(time="3ME").sum(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4758,7 +4758,7 @@ def sum( Specify ``min_count`` for finer control over when NaNs are ignored. - >>> ds.resample(time="3M").sum(skipna=True, min_count=2) + >>> ds.resample(time="3ME").sum(skipna=True, min_count=2) Dimensions: (time: 3) Coordinates: @@ -4858,7 +4858,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -4872,7 +4872,7 @@ def std( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").std() + >>> ds.resample(time="3ME").std() Dimensions: (time: 3) Coordinates: @@ -4882,7 +4882,7 @@ def std( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").std(skipna=False) + >>> ds.resample(time="3ME").std(skipna=False) Dimensions: (time: 3) Coordinates: @@ -4892,7 +4892,7 @@ def std( Specify ``ddof=1`` for an unbiased estimate. - >>> ds.resample(time="3M").std(skipna=True, ddof=1) + >>> ds.resample(time="3ME").std(skipna=True, ddof=1) Dimensions: (time: 3) Coordinates: @@ -4992,7 +4992,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5006,7 +5006,7 @@ def var( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").var() + >>> ds.resample(time="3ME").var() Dimensions: (time: 3) Coordinates: @@ -5016,7 +5016,7 @@ def var( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").var(skipna=False) + >>> ds.resample(time="3ME").var(skipna=False) Dimensions: (time: 3) Coordinates: @@ -5026,7 +5026,7 @@ def var( Specify ``ddof=1`` for an unbiased estimate. - >>> ds.resample(time="3M").var(skipna=True, ddof=1) + >>> ds.resample(time="3ME").var(skipna=True, ddof=1) Dimensions: (time: 3) Coordinates: @@ -5122,7 +5122,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5136,7 +5136,7 @@ def median( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").median() + >>> ds.resample(time="3ME").median() Dimensions: (time: 3) Coordinates: @@ -5146,7 +5146,7 @@ def median( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").median(skipna=False) + >>> ds.resample(time="3ME").median(skipna=False) Dimensions: (time: 3) Coordinates: @@ -5225,7 +5225,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5239,7 +5239,7 @@ def cumsum( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").cumsum() + >>> ds.resample(time="3ME").cumsum() Dimensions: (time: 6) Dimensions without coordinates: time @@ -5248,7 +5248,7 @@ def cumsum( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").cumsum(skipna=False) + >>> ds.resample(time="3ME").cumsum(skipna=False) Dimensions: (time: 6) Dimensions without coordinates: time @@ -5326,7 +5326,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5340,7 +5340,7 @@ def cumprod( Data variables: da (time) float64 1.0 2.0 3.0 0.0 2.0 nan - >>> ds.resample(time="3M").cumprod() + >>> ds.resample(time="3ME").cumprod() Dimensions: (time: 6) Dimensions without coordinates: time @@ -5349,7 +5349,7 @@ def cumprod( Use ``skipna`` to control whether NaNs are ignored. - >>> ds.resample(time="3M").cumprod(skipna=False) + >>> ds.resample(time="3ME").cumprod(skipna=False) Dimensions: (time: 6) Dimensions without coordinates: time @@ -5455,7 +5455,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5546,7 +5546,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5637,7 +5637,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5734,7 +5734,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5841,7 +5841,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -5950,7 +5950,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6066,7 +6066,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6192,7 +6192,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6315,7 +6315,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6438,7 +6438,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6557,7 +6557,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6652,7 +6652,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6749,7 +6749,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6874,7 +6874,7 @@ def count( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6885,7 +6885,7 @@ def count( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").count() + >>> da.resample(time="3ME").count() array([1, 3, 1]) Coordinates: @@ -6965,7 +6965,7 @@ def all( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -6976,7 +6976,7 @@ def all( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").all() + >>> da.resample(time="3ME").all() array([ True, True, False]) Coordinates: @@ -7056,7 +7056,7 @@ def any( ... np.array([True, True, True, True, True, False], dtype=bool), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7067,7 +7067,7 @@ def any( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").any() + >>> da.resample(time="3ME").any() array([ True, True, True]) Coordinates: @@ -7153,7 +7153,7 @@ def max( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7164,7 +7164,7 @@ def max( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").max() + >>> da.resample(time="3ME").max() array([1., 3., 2.]) Coordinates: @@ -7172,7 +7172,7 @@ def max( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").max(skipna=False) + >>> da.resample(time="3ME").max(skipna=False) array([ 1., 3., nan]) Coordinates: @@ -7260,7 +7260,7 @@ def min( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7271,7 +7271,7 @@ def min( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").min() + >>> da.resample(time="3ME").min() array([1., 0., 2.]) Coordinates: @@ -7279,7 +7279,7 @@ def min( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").min(skipna=False) + >>> da.resample(time="3ME").min(skipna=False) array([ 1., 0., nan]) Coordinates: @@ -7369,7 +7369,7 @@ def mean( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7380,7 +7380,7 @@ def mean( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").mean() + >>> da.resample(time="3ME").mean() array([1. , 1.66666667, 2. ]) Coordinates: @@ -7388,7 +7388,7 @@ def mean( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").mean(skipna=False) + >>> da.resample(time="3ME").mean(skipna=False) array([1. , 1.66666667, nan]) Coordinates: @@ -7485,7 +7485,7 @@ def prod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7496,7 +7496,7 @@ def prod( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").prod() + >>> da.resample(time="3ME").prod() array([1., 0., 2.]) Coordinates: @@ -7504,7 +7504,7 @@ def prod( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").prod(skipna=False) + >>> da.resample(time="3ME").prod(skipna=False) array([ 1., 0., nan]) Coordinates: @@ -7512,7 +7512,7 @@ def prod( Specify ``min_count`` for finer control over when NaNs are ignored. - >>> da.resample(time="3M").prod(skipna=True, min_count=2) + >>> da.resample(time="3ME").prod(skipna=True, min_count=2) array([nan, 0., nan]) Coordinates: @@ -7611,7 +7611,7 @@ def sum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7622,7 +7622,7 @@ def sum( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").sum() + >>> da.resample(time="3ME").sum() array([1., 5., 2.]) Coordinates: @@ -7630,7 +7630,7 @@ def sum( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").sum(skipna=False) + >>> da.resample(time="3ME").sum(skipna=False) array([ 1., 5., nan]) Coordinates: @@ -7638,7 +7638,7 @@ def sum( Specify ``min_count`` for finer control over when NaNs are ignored. - >>> da.resample(time="3M").sum(skipna=True, min_count=2) + >>> da.resample(time="3ME").sum(skipna=True, min_count=2) array([nan, 5., nan]) Coordinates: @@ -7734,7 +7734,7 @@ def std( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7745,7 +7745,7 @@ def std( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").std() + >>> da.resample(time="3ME").std() array([0. , 1.24721913, 0. ]) Coordinates: @@ -7753,7 +7753,7 @@ def std( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").std(skipna=False) + >>> da.resample(time="3ME").std(skipna=False) array([0. , 1.24721913, nan]) Coordinates: @@ -7761,7 +7761,7 @@ def std( Specify ``ddof=1`` for an unbiased estimate. - >>> da.resample(time="3M").std(skipna=True, ddof=1) + >>> da.resample(time="3ME").std(skipna=True, ddof=1) array([ nan, 1.52752523, nan]) Coordinates: @@ -7857,7 +7857,7 @@ def var( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7868,7 +7868,7 @@ def var( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").var() + >>> da.resample(time="3ME").var() array([0. , 1.55555556, 0. ]) Coordinates: @@ -7876,7 +7876,7 @@ def var( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").var(skipna=False) + >>> da.resample(time="3ME").var(skipna=False) array([0. , 1.55555556, nan]) Coordinates: @@ -7884,7 +7884,7 @@ def var( Specify ``ddof=1`` for an unbiased estimate. - >>> da.resample(time="3M").var(skipna=True, ddof=1) + >>> da.resample(time="3ME").var(skipna=True, ddof=1) array([ nan, 2.33333333, nan]) Coordinates: @@ -7976,7 +7976,7 @@ def median( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -7987,7 +7987,7 @@ def median( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").median() + >>> da.resample(time="3ME").median() array([1., 2., 2.]) Coordinates: @@ -7995,7 +7995,7 @@ def median( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").median(skipna=False) + >>> da.resample(time="3ME").median(skipna=False) array([ 1., 2., nan]) Coordinates: @@ -8071,7 +8071,7 @@ def cumsum( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -8082,7 +8082,7 @@ def cumsum( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").cumsum() + >>> da.resample(time="3ME").cumsum() array([1., 2., 5., 5., 2., 2.]) Coordinates: @@ -8091,7 +8091,7 @@ def cumsum( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").cumsum(skipna=False) + >>> da.resample(time="3ME").cumsum(skipna=False) array([ 1., 2., 5., 5., 2., nan]) Coordinates: @@ -8168,7 +8168,7 @@ def cumprod( ... np.array([1, 2, 3, 0, 2, np.nan]), ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -8179,7 +8179,7 @@ def cumprod( * time (time) datetime64[ns] 2001-01-31 2001-02-28 ... 2001-06-30 labels (time) >> da.resample(time="3M").cumprod() + >>> da.resample(time="3ME").cumprod() array([1., 2., 6., 0., 2., 2.]) Coordinates: @@ -8188,7 +8188,7 @@ def cumprod( Use ``skipna`` to control whether NaNs are ignored. - >>> da.resample(time="3M").cumprod(skipna=False) + >>> da.resample(time="3ME").cumprod(skipna=False) array([ 1., 2., 6., 0., 2., nan]) Coordinates: diff --git a/xarray/core/accessor_dt.py b/xarray/core/accessor_dt.py index b57c2f3857c..2b964edbea7 100644 --- a/xarray/core/accessor_dt.py +++ b/xarray/core/accessor_dt.py @@ -545,7 +545,7 @@ class TimedeltaAccessor(TimeAccessor[T_DataArray]): Examples -------- - >>> dates = pd.timedelta_range(start="1 day", freq="6H", periods=20) + >>> dates = pd.timedelta_range(start="1 day", freq="6h", periods=20) >>> ts = xr.DataArray(dates, dims=("time")) >>> ts diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index e6e65c73a53..ddf2b3e1891 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -6352,7 +6352,7 @@ def curvefit( ... param="time_constant" ... ) # doctest: +NUMBER - array([1.0569203, 1.7354963, 2.9421577]) + array([1.05692036, 1.73549638, 2.94215771]) Coordinates: * x (x) int64 0 1 2 param >> da = xr.DataArray({example_array}, ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... ) @@ -547,7 +547,7 @@ def generate_code(self, method, has_keep_attrs): >>> da = xr.DataArray({example_array}, ... dims="time", ... coords=dict( - ... time=("time", pd.date_range("2001-01-01", freq="M", periods=6)), + ... time=("time", pd.date_range("2001-01-01", freq="ME", periods=6)), ... labels=("time", np.array(["a", "b", "c", "c", "b", "a"])), ... ), ... )""", @@ -589,7 +589,7 @@ def generate_code(self, method, has_keep_attrs): methods=AGGREGATION_METHODS, docref="resampling", docref_description="resampling operations", - example_call_preamble='.resample(time="3M")', + example_call_preamble='.resample(time="3ME")', definition_preamble=RESAMPLE_PREAMBLE, notes=_FLOX_RESAMPLE_NOTES, ) @@ -609,7 +609,7 @@ def generate_code(self, method, has_keep_attrs): methods=AGGREGATION_METHODS, docref="resampling", docref_description="resampling operations", - example_call_preamble='.resample(time="3M")', + example_call_preamble='.resample(time="3ME")', definition_preamble=RESAMPLE_PREAMBLE, notes=_FLOX_RESAMPLE_NOTES, ) From f07e8956fdd2cb69febeb783e8ad24c7ba13cb48 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:40:28 -0800 Subject: [PATCH 11/30] xfail zarr test on Windows (#8643) I see this failing quite a lot of the time... Ofc open to a proper solution but in the meantime setting this to xfail --- xarray/tests/test_backends.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index d01cfd7ff55..b31b9011664 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -2945,6 +2945,10 @@ def create_zarr_target(self): @requires_zarr +@pytest.mark.skipif( + ON_WINDOWS, + reason="Very flaky on Windows CI. Can re-enable assuming it starts consistently passing.", +) class TestZarrDirectoryStore(ZarrBase): @contextlib.contextmanager def create_zarr_target(self): From 4b5c87bf8874802f33d93dc5a7d0e07a745e6c4f Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:25:38 -0800 Subject: [PATCH 12/30] Use ddof in `numbagg>=0.7.0` for aggregations (#8624) * Use numbagg ddof for aggregations (not yet for rolling) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- xarray/core/nputils.py | 15 +++++++++++---- xarray/tests/conftest.py | 14 ++++++++++++++ xarray/tests/test_rolling.py | 13 ------------- xarray/tests/test_variable.py | 3 ++- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/xarray/core/nputils.py b/xarray/core/nputils.py index ff54bccd3d2..7fcd4b34d95 100644 --- a/xarray/core/nputils.py +++ b/xarray/core/nputils.py @@ -185,8 +185,12 @@ def f(values, axis=None, **kwargs): and pycompat.mod_version("numbagg") >= Version("0.5.0") and OPTIONS["use_numbagg"] and isinstance(values, np.ndarray) - # numbagg uses ddof=1 only, but numpy uses ddof=0 by default - and (("var" in name or "std" in name) and kwargs.get("ddof", 0) == 1) + # numbagg<0.7.0 uses ddof=1 only, but numpy uses ddof=0 by default + and ( + pycompat.mod_version("numbagg") >= Version("0.7.0") + or ("var" not in name and "std" not in name) + or kwargs.get("ddof", 0) == 1 + ) # TODO: bool? and values.dtype.kind in "uifc" # and values.dtype.isnative @@ -196,9 +200,12 @@ def f(values, axis=None, **kwargs): nba_func = getattr(numbagg, name, None) if nba_func is not None: - # numbagg does not take care dtype, ddof + # numbagg does not use dtype kwargs.pop("dtype", None) - kwargs.pop("ddof", None) + # prior to 0.7.0, numbagg did not support ddof; we ensure it's limited + # to ddof=1 above. + if pycompat.mod_version("numbagg") < Version("0.7.0"): + kwargs.pop("ddof", None) return nba_func(values, axis=axis, **kwargs) if ( _BOTTLENECK_AVAILABLE diff --git a/xarray/tests/conftest.py b/xarray/tests/conftest.py index f153c2f4dc0..9c10bd6d18a 100644 --- a/xarray/tests/conftest.py +++ b/xarray/tests/conftest.py @@ -4,6 +4,7 @@ import pandas as pd import pytest +import xarray as xr from xarray import DataArray, Dataset from xarray.tests import create_test_data, requires_dask @@ -13,6 +14,19 @@ def backend(request): return request.param +@pytest.fixture(params=["numbagg", "bottleneck"]) +def compute_backend(request): + if request.param == "bottleneck": + options = dict(use_bottleneck=True, use_numbagg=False) + elif request.param == "numbagg": + options = dict(use_bottleneck=False, use_numbagg=True) + else: + raise ValueError + + with xr.set_options(**options): + yield request.param + + @pytest.fixture(params=[1]) def ds(request, backend): if request.param == 1: diff --git a/xarray/tests/test_rolling.py b/xarray/tests/test_rolling.py index 6db4d38b53e..79a5ba0a667 100644 --- a/xarray/tests/test_rolling.py +++ b/xarray/tests/test_rolling.py @@ -23,19 +23,6 @@ ] -@pytest.fixture(params=["numbagg", "bottleneck"]) -def compute_backend(request): - if request.param == "bottleneck": - options = dict(use_bottleneck=True, use_numbagg=False) - elif request.param == "numbagg": - options = dict(use_bottleneck=False, use_numbagg=True) - else: - raise ValueError - - with xr.set_options(**options): - yield request.param - - @pytest.mark.parametrize("func", ["mean", "sum"]) @pytest.mark.parametrize("min_periods", [1, 10]) def test_cumulative(d, func, min_periods) -> None: diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py index a2ae1e61cf2..fb083586415 100644 --- a/xarray/tests/test_variable.py +++ b/xarray/tests/test_variable.py @@ -1754,7 +1754,8 @@ def test_reduce(self): v.mean(dim="x", axis=0) @requires_bottleneck - def test_reduce_use_bottleneck(self, monkeypatch): + @pytest.mark.parametrize("compute_backend", ["bottleneck"], indirect=True) + def test_reduce_use_bottleneck(self, monkeypatch, compute_backend): def raise_if_called(*args, **kwargs): raise RuntimeError("should not have been called") From 4bb5175b9ac595138c5de78171d10f044d09341c Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Tue, 23 Jan 2024 13:44:14 +0100 Subject: [PATCH 13/30] infer_freq: return 'YE' (#8629 follow-up) (#8642) * infer_freq: return 'YE' (#8629 follow-up) * fix whats new --------- Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --- doc/whats-new.rst | 2 +- xarray/coding/frequencies.py | 2 +- xarray/tests/test_cftimeindex.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 3364087143b..14377792f0d 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -28,7 +28,7 @@ Breaking changes - Following pandas, :py:meth:`infer_freq` will return ``"YE"``, instead of ``"Y"`` (formerly ``"A"``). This is to be consistent with the deprecation of the latter frequency string in pandas 2.2. - This is a follow up to :pull:`8415` (:issue:`8612`, :pull:`8629`). + This is a follow up to :pull:`8415` (:issue:`8612`, :pull:`8642`). By `Mathias Hauser `_. Deprecations diff --git a/xarray/coding/frequencies.py b/xarray/coding/frequencies.py index c401fb95f1c..341627ab114 100644 --- a/xarray/coding/frequencies.py +++ b/xarray/coding/frequencies.py @@ -183,7 +183,7 @@ def _get_annual_rule(self): if len(np.unique(self.index.month)) > 1: return None - return {"cs": "YS", "ce": "Y"}.get(month_anchor_check(self.index)) + return {"cs": "YS", "ce": "YE"}.get(month_anchor_check(self.index)) def _get_quartely_rule(self): if len(self.month_deltas) > 1: diff --git a/xarray/tests/test_cftimeindex.py b/xarray/tests/test_cftimeindex.py index 062756e614b..6f0e00ef5bb 100644 --- a/xarray/tests/test_cftimeindex.py +++ b/xarray/tests/test_cftimeindex.py @@ -1353,7 +1353,7 @@ def test_infer_freq_invalid_inputs(): "freq", [ "300YS-JAN", - "Y-DEC", + "YE-DEC", "YS-JUL", "2YS-FEB", "QE-NOV", From 0a61f0ed62293fda0e26561d74705b3fe00912cf Mon Sep 17 00:00:00 2001 From: Tom Nicholas Date: Tue, 23 Jan 2024 13:28:35 -0500 Subject: [PATCH 14/30] Pin sphinx-book-theme to 1.0.1 to try to deal with #8619 (#8632) Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --- ci/requirements/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/requirements/doc.yml b/ci/requirements/doc.yml index d7737a8403e..e84710ffb0d 100644 --- a/ci/requirements/doc.yml +++ b/ci/requirements/doc.yml @@ -32,7 +32,7 @@ dependencies: - setuptools - sparse - sphinx-autosummary-accessors - - sphinx-book-theme >= 0.3.0 + - sphinx-book-theme<=1.0.1 - sphinx-copybutton - sphinx-design - sphinx-inline-tabs From 047543588981c43ff0b6be9e16727d693bfe7bea Mon Sep 17 00:00:00 2001 From: Casper Helms Date: Tue, 23 Jan 2024 19:28:48 +0100 Subject: [PATCH 15/30] Fixed typo in custom backend registration documentation (#8645) --- doc/internals/how-to-add-new-backend.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/internals/how-to-add-new-backend.rst b/doc/internals/how-to-add-new-backend.rst index ca42d60abaf..4352dd3df5b 100644 --- a/doc/internals/how-to-add-new-backend.rst +++ b/doc/internals/how-to-add-new-backend.rst @@ -281,7 +281,7 @@ You can declare the entrypoint in your project configuration like so: .. code:: toml - [project.entry-points."xarray-backends"] + [project.entry-points."xarray.backends"] my_engine = "my_package.my_module:MyBackendEntrypoint" .. tab:: pyproject.toml [Poetry] From d34ebff531b5c02ead95b9dcd9b1c8fdf101260a Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Tue, 23 Jan 2024 19:29:13 +0100 Subject: [PATCH 16/30] implement `isnull` using `full_like` instead of `zeros_like` (#7395) * use `full_like` to implement `isnull` on non-nullable dtypes * remove the unused import of `zeros_like` * add tests to make sure non-nullable dtypes still return all-`False` * remove the now unused `zeros_like` --- xarray/core/duck_array_ops.py | 4 +-- xarray/tests/test_duck_array_ops.py | 38 +++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/xarray/core/duck_array_ops.py b/xarray/core/duck_array_ops.py index af5c415cdce..b30ba4c3a78 100644 --- a/xarray/core/duck_array_ops.py +++ b/xarray/core/duck_array_ops.py @@ -18,6 +18,7 @@ from numpy import any as array_any # noqa from numpy import ( # noqa around, # noqa + full_like, gradient, isclose, isin, @@ -26,7 +27,6 @@ tensordot, transpose, unravel_index, - zeros_like, # noqa ) from numpy import concatenate as _concatenate from numpy.lib.stride_tricks import sliding_window_view # noqa @@ -151,7 +151,7 @@ def isnull(data): return xp.isnan(data) elif issubclass(scalar_type, (np.bool_, np.integer, np.character, np.void)): # these types cannot represent missing values - return zeros_like(data, dtype=bool) + return full_like(data, dtype=bool, fill_value=False) else: # at this point, array should have dtype=object if isinstance(data, np.ndarray): diff --git a/xarray/tests/test_duck_array_ops.py b/xarray/tests/test_duck_array_ops.py index 9167c2ddbea..7757ec58edc 100644 --- a/xarray/tests/test_duck_array_ops.py +++ b/xarray/tests/test_duck_array_ops.py @@ -577,17 +577,39 @@ def test_argmin_max_error(): @pytest.mark.parametrize( - "array", + ["array", "expected"], [ - np.array([np.datetime64("2000-01-01"), np.datetime64("NaT")]), - np.array([np.timedelta64(1, "h"), np.timedelta64("NaT")]), - np.array([0.0, np.nan]), - np.array([1j, np.nan]), - np.array(["foo", np.nan], dtype=object), + ( + np.array([np.datetime64("2000-01-01"), np.datetime64("NaT")]), + np.array([False, True]), + ), + ( + np.array([np.timedelta64(1, "h"), np.timedelta64("NaT")]), + np.array([False, True]), + ), + ( + np.array([0.0, np.nan]), + np.array([False, True]), + ), + ( + np.array([1j, np.nan]), + np.array([False, True]), + ), + ( + np.array(["foo", np.nan], dtype=object), + np.array([False, True]), + ), + ( + np.array([1, 2], dtype=int), + np.array([False, False]), + ), + ( + np.array([True, False], dtype=bool), + np.array([False, False]), + ), ], ) -def test_isnull(array): - expected = np.array([False, True]) +def test_isnull(array, expected): actual = duck_array_ops.isnull(array) np.testing.assert_equal(expected, actual) From 15fb1d8abf62f15c96b44ffb235fcd98fbe4fd21 Mon Sep 17 00:00:00 2001 From: TomNicholas Date: Tue, 23 Jan 2024 16:55:12 -0500 Subject: [PATCH 17/30] whatsnew for v2024.01.1 --- doc/whats-new.rst | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 14377792f0d..ce4829cd716 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -16,12 +16,10 @@ What's New .. _whats-new.2024.01.1: -v2024.01.1 (unreleased) ------------------------ - -New Features -~~~~~~~~~~~~ +v2024.01.1 (23 Jan, 2024) +------------------------- +This release is to fix a bug with the rendering of the documentation, but it also includes changes to the handling of pandas frequency strings. Breaking changes ~~~~~~~~~~~~~~~~ @@ -40,16 +38,11 @@ Deprecations among others (:issue:`8612`, :pull:`8629`). By `Mathias Hauser `_. -Bug fixes -~~~~~~~~~ - - Documentation ~~~~~~~~~~~~~ - -Internal Changes -~~~~~~~~~~~~~~~~ +- Pin ``sphinx-book-theme`` to ``1.0.1`` to fix a rendering issue with the sidebar in the docs. (:issue:`8619`, :pull:`8632`) + By `Tom Nicholas `_. .. _whats-new.2024.01.0: From 75075c85a01c73fa6d46bf72d1e55e8f0ddc6d4a Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:46:26 -0500 Subject: [PATCH 18/30] use first element of residual in _nonpolyfit_1d (#8647) --- xarray/core/nputils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/nputils.py b/xarray/core/nputils.py index 7fcd4b34d95..0151d715f6f 100644 --- a/xarray/core/nputils.py +++ b/xarray/core/nputils.py @@ -234,7 +234,7 @@ def _nanpolyfit_1d(arr, x, rcond=None): mask = np.isnan(arr) if not np.all(mask): out[:-1], resid, rank, _ = np.linalg.lstsq(x[~mask, :], arr[~mask], rcond=rcond) - out[-1] = resid if resid.size > 0 else np.nan + out[-1] = resid[0] if resid.size > 0 else np.nan warn_on_deficient_rank(rank, x.shape[1]) return out From b5a6e24b8ade0e5bdde31c838b89bacffa216211 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:23:25 -0800 Subject: [PATCH 19/30] xfail another test on windows (#8648) As ever, very open to approaches to fix these. But unless we can fix them, xfailing them seems like the most reasonable solution --- xarray/tests/test_backends.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index b31b9011664..85afbc1e147 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -2265,6 +2265,10 @@ def test_chunk_encoding(self) -> None: pass @requires_dask + @pytest.mark.skipif( + ON_WINDOWS, + reason="Very flaky on Windows CI. Can re-enable assuming it starts consistently passing.", + ) def test_chunk_encoding_with_dask(self) -> None: # These datasets DO have dask chunks. Need to check for various # interactions between dask and zarr chunks From b9df46705c764bf37cdf675fa6de76d94bcbfe86 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 24 Jan 2024 13:59:49 +0100 Subject: [PATCH 20/30] new whats-new section (#8652) --- doc/whats-new.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index ce4829cd716..317f3b1a824 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -14,6 +14,36 @@ What's New np.random.seed(123456) + +.. _whats-new.2024.02.0: + +v2024.02.0 (unreleased) +----------------------- + +New Features +~~~~~~~~~~~~ + + +Breaking changes +~~~~~~~~~~~~~~~~ + + +Deprecations +~~~~~~~~~~~~ + + +Bug fixes +~~~~~~~~~ + + +Documentation +~~~~~~~~~~~~~ + + +Internal Changes +~~~~~~~~~~~~~~~~ + + .. _whats-new.2024.01.1: v2024.01.1 (23 Jan, 2024) From e1a9dc648375c9947fe4e064af8f0e4a44a5f365 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 24 Jan 2024 16:59:11 +0100 Subject: [PATCH 21/30] ruff: use extend-exclude (#8649) --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 72bf2931e72..5ab4b4c5720 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -227,8 +227,7 @@ reportMissingTypeStubs = false [tool.ruff] builtins = ["ellipsis"] -exclude = [ - ".eggs", +extend-exclude = [ "doc", "_typed_ops.pyi", ] From d639d6e151bdeba070127aa7e286c5bfa6048194 Mon Sep 17 00:00:00 2001 From: Tom Nicholas Date: Wed, 24 Jan 2024 16:46:00 -0500 Subject: [PATCH 22/30] Update HOW_TO_RELEASE.md by clarifying where RTD build can be found (#8655) --- HOW_TO_RELEASE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HOW_TO_RELEASE.md b/HOW_TO_RELEASE.md index 34a63aad202..9d1164547b9 100644 --- a/HOW_TO_RELEASE.md +++ b/HOW_TO_RELEASE.md @@ -52,7 +52,7 @@ upstream https://github.com/pydata/xarray (push) ```sh pytest ``` - 8. Check that the ReadTheDocs build is passing on the `main` branch. + 8. Check that the [ReadTheDocs build](https://readthedocs.org/projects/xray/) is passing on the `latest` build version (which is built from the `main` branch). 9. Issue the release on GitHub. Click on "Draft a new release" at . Type in the version number (with a "v") and paste the release summary in the notes. From 037a39e249e5387bc15de447c57bfd559fd5a574 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Wed, 24 Jan 2024 16:56:34 -0600 Subject: [PATCH 23/30] CI: Pin scientific-python/upload-nightly-action to release sha (#8662) * For security best practices, use the action from known commit shas that correspond to tagged releases. These can be updated via dependabot. --- .github/workflows/nightly-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly-wheels.yml b/.github/workflows/nightly-wheels.yml index 0f74ba4b2ce..30c15e58e32 100644 --- a/.github/workflows/nightly-wheels.yml +++ b/.github/workflows/nightly-wheels.yml @@ -38,7 +38,7 @@ jobs: fi - name: Upload wheel - uses: scientific-python/upload-nightly-action@main + uses: scientific-python/upload-nightly-action@6e9304f7a3a5501c6f98351537493ec898728299 # 0.3.0 with: anaconda_nightly_upload_token: ${{ secrets.ANACONDA_NIGHTLY }} artifacts_path: dist From 6801e274c43f61f54025881e1885fa60aae93622 Mon Sep 17 00:00:00 2001 From: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:24:35 -0800 Subject: [PATCH 24/30] Add `dev` dependencies to `pyproject.toml` (#8661) * Add `dev` dependencies to `pyproject.toml` --- pyproject.toml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5ab4b4c5720..2a185933b47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,24 @@ dependencies = [ "pandas>=1.5", ] +[project.optional-dependencies] +accel = ["scipy", "bottleneck", "numbagg", "flox", "opt_einsum"] +complete = ["xarray[accel,io,parallel,viz,dev]"] +dev = [ + "hypothesis", + "pre-commit", + "pytest", + "pytest-cov", + "pytest-env", + "pytest-xdist", + "pytest-timeout", + "ruff", + "xarray[complete]", +] +io = ["netCDF4", "h5netcdf", "scipy", 'pydap; python_version<"3.10"', "zarr", "fsspec", "cftime", "pooch"] +parallel = ["dask[complete]"] +viz = ["matplotlib", "seaborn", "nc-time-axis"] + [project.urls] Documentation = "https://docs.xarray.dev" SciPy2015-talk = "https://www.youtube.com/watch?v=X0pAhJgySxk" @@ -38,13 +56,6 @@ source-code = "https://github.com/pydata/xarray" [project.entry-points."xarray.chunkmanagers"] dask = "xarray.core.daskmanager:DaskManager" -[project.optional-dependencies] -accel = ["scipy", "bottleneck", "numbagg", "flox", "opt_einsum"] -complete = ["xarray[accel,io,parallel,viz]"] -io = ["netCDF4", "h5netcdf", "scipy", 'pydap; python_version<"3.10"', "zarr", "fsspec", "cftime", "pooch"] -parallel = ["dask[complete]"] -viz = ["matplotlib", "seaborn", "nc-time-axis"] - [build-system] build-backend = "setuptools.build_meta" requires = [ @@ -273,5 +284,5 @@ test = "pytest" [tool.repo-review] ignore = [ - "PP308" # This option creates a large amount of log lines. + "PP308", # This option creates a large amount of log lines. ] From db9e448a8f836dc6b5ea970a9ba9029e920ad05c Mon Sep 17 00:00:00 2001 From: nameloCmaS Date: Thu, 25 Jan 2024 09:22:31 +0000 Subject: [PATCH 25/30] dt.weekday_name - removal of function (#8664) * Removed dt.weekday_name property * string formatting * remove from doc source * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * whats-new * Revert "whats-new" This reverts commit 41c79409cbcb4494b7ffd317cd56028eec8df117. * whats-new * suggested changes: accessor_dt.py Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> * suggested changes: accessor_dt.py Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> * suggested changes: whats-new.rst Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --- doc/api-hidden.rst | 1 - doc/api.rst | 1 - doc/whats-new.rst | 5 +++++ xarray/core/accessor_dt.py | 8 ++------ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/api-hidden.rst b/doc/api-hidden.rst index 374fe41fde5..56ed487d5c6 100644 --- a/doc/api-hidden.rst +++ b/doc/api-hidden.rst @@ -134,7 +134,6 @@ core.accessor_dt.DatetimeAccessor.time core.accessor_dt.DatetimeAccessor.week core.accessor_dt.DatetimeAccessor.weekday - core.accessor_dt.DatetimeAccessor.weekday_name core.accessor_dt.DatetimeAccessor.weekofyear core.accessor_dt.DatetimeAccessor.year diff --git a/doc/api.rst b/doc/api.rst index a7b526faa2a..a8f8ea7dd1c 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -523,7 +523,6 @@ Datetimelike properties DataArray.dt.nanosecond DataArray.dt.dayofweek DataArray.dt.weekday - DataArray.dt.weekday_name DataArray.dt.dayofyear DataArray.dt.quarter DataArray.dt.days_in_month diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 317f3b1a824..ac0015c14c5 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -30,6 +30,8 @@ Breaking changes Deprecations ~~~~~~~~~~~~ +- The `dt.weekday_name` parameter wasn't functional on modern pandas versions and has been removed. (:issue:`8610`, :pull:`8664`) + By `Sam Coleman `_. Bug fixes @@ -73,6 +75,9 @@ Documentation - Pin ``sphinx-book-theme`` to ``1.0.1`` to fix a rendering issue with the sidebar in the docs. (:issue:`8619`, :pull:`8632`) By `Tom Nicholas `_. +- Fixed documentation where the use of the depreciated pandas frequency string + prevented the documentation from being built. (:pull:`8638`) + By `Sam Coleman `_. .. _whats-new.2024.01.0: diff --git a/xarray/core/accessor_dt.py b/xarray/core/accessor_dt.py index 2b964edbea7..65705a9d32f 100644 --- a/xarray/core/accessor_dt.py +++ b/xarray/core/accessor_dt.py @@ -59,7 +59,8 @@ def _access_through_cftimeindex(values, name): field_values = _season_from_months(months) elif name == "date": raise AttributeError( - "'CFTimeIndex' object has no attribute `date`. Consider using the floor method instead, for instance: `.time.dt.floor('D')`." + "'CFTimeIndex' object has no attribute `date`. Consider using the floor method " + "instead, for instance: `.time.dt.floor('D')`." ) else: field_values = getattr(values_as_cftimeindex, name) @@ -456,11 +457,6 @@ def dayofweek(self) -> T_DataArray: weekday = dayofweek - @property - def weekday_name(self) -> T_DataArray: - """The name of day in a week""" - return self._date_field("weekday_name", object) - @property def dayofyear(self) -> T_DataArray: """The ordinal day of the year""" From 6a6404a77bbb0aa8d810300f94fa27193035654b Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Thu, 25 Jan 2024 17:11:29 -0800 Subject: [PATCH 26/30] Fix `variables` arg typo in `Dataset.sortby()` docstring (#8670) * Fix `variables` arg typo in `Dataset.sortby()` docstring * Add change to `whats-new.rst` * Remove whitespace in `whats-new.rst` * Update doc/whats-new.rst Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --------- Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com> --- doc/whats-new.rst | 4 +++- xarray/core/dataset.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index ac0015c14c5..d52300e83c5 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -40,7 +40,9 @@ Bug fixes Documentation ~~~~~~~~~~~~~ - +- Fix `variables` arg typo in `Dataset.sortby()` docstring + (:issue:`8663`, :pull:`8670`) + By `Tom Vo `_. Internal Changes ~~~~~~~~~~~~~~~~ diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index c83a56bb373..ad3260569b1 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -7947,7 +7947,7 @@ def sortby( Parameters ---------- - kariables : Hashable, DataArray, sequence of Hashable or DataArray, or Callable + variables : Hashable, DataArray, sequence of Hashable or DataArray, or Callable 1D DataArray objects or name(s) of 1D variable(s) in coords whose values are used to sort this array. If a callable, the callable is passed this object, and the result is used as the value for cond. From f5d22a699715e022101d8322162271c2c3ccefa9 Mon Sep 17 00:00:00 2001 From: Tom Nicholas Date: Fri, 26 Jan 2024 11:06:00 -0500 Subject: [PATCH 27/30] Fix unstack method when wrapping array api class (#8668) * add test for unstacking * fix bug with unstack * some other occurrences of reshape * whatsnew --- doc/whats-new.rst | 2 ++ xarray/core/missing.py | 11 ++++++++--- xarray/core/variable.py | 2 +- xarray/tests/test_array_api.py | 8 ++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index d52300e83c5..35cf0c548e2 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -37,6 +37,8 @@ Deprecations Bug fixes ~~~~~~~~~ +- Ensure :py:meth:`DataArray.unstack` works when wrapping array API-compliant classes. (:issue:`8666`, :pull:`8668`) + By `Tom Nicholas `_. Documentation ~~~~~~~~~~~~~ diff --git a/xarray/core/missing.py b/xarray/core/missing.py index b55fd6049a6..c6592f739da 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -13,7 +13,12 @@ from xarray.core import utils from xarray.core.common import _contains_datetime_like_objects, ones_like from xarray.core.computation import apply_ufunc -from xarray.core.duck_array_ops import datetime_to_numeric, push, timedelta_to_numeric +from xarray.core.duck_array_ops import ( + datetime_to_numeric, + push, + reshape, + timedelta_to_numeric, +) from xarray.core.options import _get_keep_attrs from xarray.core.parallelcompat import get_chunked_array_type, is_chunked_array from xarray.core.types import Interp1dOptions, InterpOptions @@ -748,7 +753,7 @@ def _interp1d(var, x, new_x, func, kwargs): x, new_x = x[0], new_x[0] rslt = func(x, var, assume_sorted=True, **kwargs)(np.ravel(new_x)) if new_x.ndim > 1: - return rslt.reshape(var.shape[:-1] + new_x.shape) + return reshape(rslt, (var.shape[:-1] + new_x.shape)) if new_x.ndim == 0: return rslt[..., -1] return rslt @@ -767,7 +772,7 @@ def _interpnd(var, x, new_x, func, kwargs): rslt = func(x, var, xi, **kwargs) # move back the interpolation axes to the last position rslt = rslt.transpose(range(-rslt.ndim + 1, 1)) - return rslt.reshape(rslt.shape[:-1] + new_x[0].shape) + return reshape(rslt, rslt.shape[:-1] + new_x[0].shape) def _chunked_aware_interpnd(var, *coords, interp_func, interp_kwargs, localize=True): diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 3add7a1441e..e4add9f838e 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -1571,7 +1571,7 @@ def _unstack_once_full(self, dim: Mapping[Any, int], old_dim: Hashable) -> Self: reordered = self.transpose(*dim_order) new_shape = reordered.shape[: len(other_dims)] + new_dim_sizes - new_data = reordered.data.reshape(new_shape) + new_data = duck_array_ops.reshape(reordered.data, new_shape) new_dims = reordered.dims[: len(other_dims)] + new_dim_names return type(self)( diff --git a/xarray/tests/test_array_api.py b/xarray/tests/test_array_api.py index fddaa120970..fea36d9aca4 100644 --- a/xarray/tests/test_array_api.py +++ b/xarray/tests/test_array_api.py @@ -115,6 +115,14 @@ def test_stack(arrays: tuple[xr.DataArray, xr.DataArray]) -> None: assert_equal(actual, expected) +def test_unstack(arrays: tuple[xr.DataArray, xr.DataArray]) -> None: + np_arr, xp_arr = arrays + expected = np_arr.stack(z=("x", "y")).unstack() + actual = xp_arr.stack(z=("x", "y")).unstack() + assert isinstance(actual.data, Array) + assert_equal(actual, expected) + + def test_where() -> None: np_arr = xr.DataArray(np.array([1, 0]), dims="x") xp_arr = xr.DataArray(xp.asarray([1, 0]), dims="x") From 870450172477e3fccc5495ee9356813fa6187594 Mon Sep 17 00:00:00 2001 From: Tom Nicholas Date: Fri, 26 Jan 2024 11:41:29 -0500 Subject: [PATCH 28/30] Fix automatic broadcasting when wrapping array api class (#8669) * test automatic broadcasting * fix bug * whatsnew * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- doc/whats-new.rst | 2 ++ xarray/core/variable.py | 3 ++- xarray/tests/test_array_api.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 35cf0c548e2..151ed9da105 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -37,6 +37,8 @@ Deprecations Bug fixes ~~~~~~~~~ +- Fix bug with broadcasting when wrapping array API-compliant classes. (:issue:`8665`, :pull:`8669`) + By `Tom Nicholas `_. - Ensure :py:meth:`DataArray.unstack` works when wrapping array API-compliant classes. (:issue:`8666`, :pull:`8668`) By `Tom Nicholas `_. diff --git a/xarray/core/variable.py b/xarray/core/variable.py index e4add9f838e..119495a486a 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -1476,7 +1476,8 @@ def set_dims(self, dims, shape=None): tmp_shape = tuple(dims_map[d] for d in expanded_dims) expanded_data = duck_array_ops.broadcast_to(self.data, tmp_shape) else: - expanded_data = self.data[(None,) * (len(expanded_dims) - self.ndim)] + indexer = (None,) * (len(expanded_dims) - self.ndim) + (...,) + expanded_data = self.data[indexer] expanded_var = Variable( expanded_dims, expanded_data, self._attrs, self._encoding, fastpath=True diff --git a/xarray/tests/test_array_api.py b/xarray/tests/test_array_api.py index fea36d9aca4..03dcfd9b20f 100644 --- a/xarray/tests/test_array_api.py +++ b/xarray/tests/test_array_api.py @@ -77,6 +77,22 @@ def test_broadcast(arrays: tuple[xr.DataArray, xr.DataArray]) -> None: assert_equal(a, e) +def test_broadcast_during_arithmetic(arrays: tuple[xr.DataArray, xr.DataArray]) -> None: + np_arr, xp_arr = arrays + np_arr2 = xr.DataArray(np.array([1.0, 2.0]), dims="x") + xp_arr2 = xr.DataArray(xp.asarray([1.0, 2.0]), dims="x") + + expected = np_arr * np_arr2 + actual = xp_arr * xp_arr2 + assert isinstance(actual.data, Array) + assert_equal(actual, expected) + + expected = np_arr2 * np_arr + actual = xp_arr2 * xp_arr + assert isinstance(actual.data, Array) + assert_equal(actual, expected) + + def test_concat(arrays: tuple[xr.DataArray, xr.DataArray]) -> None: np_arr, xp_arr = arrays expected = xr.concat((np_arr, np_arr), dim="x") From ca4f12133e9643c197facd17b54d5040a1bda002 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Fri, 26 Jan 2024 09:54:23 -0700 Subject: [PATCH 29/30] groupby: Don't set `method` by default on flox>=0.9 (#8657) * groupby: Set method="None" by default on flox>=0.9 * Fix? --- doc/whats-new.rst | 3 +++ xarray/core/groupby.py | 11 +++++++---- xarray/tests/test_groupby.py | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 151ed9da105..8865eb98481 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -23,6 +23,9 @@ v2024.02.0 (unreleased) New Features ~~~~~~~~~~~~ +- Xarray now defers to flox's `heuristics `_ + to set default `method` for groupby problems. This only applies to ``flox>=0.9``. + By `Deepak Cherian `_. Breaking changes ~~~~~~~~~~~~~~~~ diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index ebb488d42c9..3a9c3ee6470 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -16,6 +16,7 @@ import numpy as np import pandas as pd +from packaging.version import Version from xarray.core import dtypes, duck_array_ops, nputils, ops from xarray.core._aggregations import ( @@ -1003,6 +1004,7 @@ def _flox_reduce( **kwargs: Any, ): """Adaptor function that translates our groupby API to that of flox.""" + import flox from flox.xarray import xarray_reduce from xarray.core.dataset import Dataset @@ -1014,10 +1016,11 @@ def _flox_reduce( if keep_attrs is None: keep_attrs = _get_keep_attrs(default=True) - # preserve current strategy (approximately) for dask groupby. - # We want to control the default anyway to prevent surprises - # if flox decides to change its default - kwargs.setdefault("method", "cohorts") + if Version(flox.__version__) < Version("0.9"): + # preserve current strategy (approximately) for dask groupby + # on older flox versions to prevent surprises. + # flox >=0.9 will choose this on its own. + kwargs.setdefault("method", "cohorts") numeric_only = kwargs.pop("numeric_only", None) if numeric_only: diff --git a/xarray/tests/test_groupby.py b/xarray/tests/test_groupby.py index e45d8ed0bef..25fabd5e2b9 100644 --- a/xarray/tests/test_groupby.py +++ b/xarray/tests/test_groupby.py @@ -3,10 +3,12 @@ import datetime import operator import warnings +from unittest import mock import numpy as np import pandas as pd import pytest +from packaging.version import Version import xarray as xr from xarray import DataArray, Dataset, Variable @@ -2499,3 +2501,20 @@ def test_groupby_dim_no_dim_equal(use_flox): actual1 = da.drop_vars("lat").groupby("lat", squeeze=False).sum() actual2 = da.groupby("lat", squeeze=False).sum() assert_identical(actual1, actual2.drop_vars("lat")) + + +@requires_flox +def test_default_flox_method(): + import flox.xarray + + da = xr.DataArray([1, 2, 3], dims="x", coords={"label": ("x", [2, 2, 1])}) + + result = xr.DataArray([3, 3], dims="label", coords={"label": [1, 2]}) + with mock.patch("flox.xarray.xarray_reduce", return_value=result) as mocked_reduce: + da.groupby("label").sum() + + kwargs = mocked_reduce.call_args.kwargs + if Version(flox.__version__) < Version("0.9.0"): + assert kwargs["method"] == "cohorts" + else: + assert "method" not in kwargs From e22b47511f4188e2203c5753de4a0a36094c2e83 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Fri, 26 Jan 2024 18:28:49 -0700 Subject: [PATCH 30/30] Fix NetCDF4 C version detection (#8675) --- xarray/tests/test_backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 85afbc1e147..f3c8d6a12f1 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -5416,7 +5416,7 @@ def test_write_file_from_np_str(str_type, tmpdir) -> None: class TestNCZarr: @property def netcdfc_version(self): - return Version(nc4.getlibversion().split()[0]) + return Version(nc4.getlibversion().split()[0].split("-development")[0]) def _create_nczarr(self, filename): if self.netcdfc_version < Version("4.8.1"):