From df882709f38293e0ff3f296bd57b13490d3f5864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20M=C3=BChlbauer?= Date: Tue, 13 May 2025 11:04:12 +0200 Subject: [PATCH 1/3] fix roundtripping zero-size timedelta arrays --- xarray/coding/times.py | 15 +++++++++++---- xarray/tests/test_coding_times.py | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/xarray/coding/times.py b/xarray/coding/times.py index fdecfe77ede..51e84a868ed 100644 --- a/xarray/coding/times.py +++ b/xarray/coding/times.py @@ -661,20 +661,24 @@ def decode_cf_timedelta( with warnings.catch_warnings(): warnings.filterwarnings("ignore", "All-NaN slice encountered", RuntimeWarning) - _check_timedelta_range(np.nanmin(num_timedeltas), unit, time_unit) - _check_timedelta_range(np.nanmax(num_timedeltas), unit, time_unit) + if num_timedeltas.size > 0: + _check_timedelta_range(np.nanmin(num_timedeltas), unit, time_unit) + _check_timedelta_range(np.nanmax(num_timedeltas), unit, time_unit) timedeltas = _numbers_to_timedelta( num_timedeltas, unit, "s", "timedeltas", target_unit=time_unit ) pd_timedeltas = pd.to_timedelta(ravel(timedeltas)) - if np.isnat(timedeltas).all(): + if timedeltas.size > 0 and np.isnat(timedeltas).all(): empirical_unit = time_unit else: empirical_unit = pd_timedeltas.unit - if np.timedelta64(1, time_unit) > np.timedelta64(1, empirical_unit): + if ( + np.timedelta64(1, time_unit) > np.timedelta64(1, empirical_unit) + or timedeltas.size == 0 + ): time_unit = empirical_unit if time_unit not in {"s", "ms", "us", "ns"}: @@ -1230,6 +1234,9 @@ def _eagerly_encode_cf_timedelta( data_units = infer_timedelta_units(timedeltas) if units is None: units = data_units + # units take precedence in the case of zero-size array + if timedeltas.size == 0: + data_units = units time_delta = _unit_timedelta_numpy(units) time_deltas = pd.TimedeltaIndex(ravel(timedeltas)) diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py index e4541bad7e6..12b28ccd76e 100644 --- a/xarray/tests/test_coding_times.py +++ b/xarray/tests/test_coding_times.py @@ -1959,3 +1959,19 @@ def test_decode_floating_point_timedelta_no_serialization_warning() -> None: decoded = conventions.decode_cf_variable("foo", encoded, decode_timedelta=True) with assert_no_warnings(): decoded.load() + + +def test_roundtrip_0size_timedelta(time_unit: PDDatetimeUnitOptions) -> None: + # regression test for GitHub issue #10310 + encoding = {"units": "days", "dtype": np.dtype("int64")} + data = np.array([], dtype=f"=m8[{time_unit}]") + decoded = Variable(["time"], data, encoding=encoding) + encoded = conventions.encode_cf_variable(decoded, name="foo") + assert encoded.dtype == encoding["dtype"] + assert encoded.attrs["units"] == encoding["units"] + decoded = conventions.decode_cf_variable("foo", encoded, decode_timedelta=True) + assert decoded.dtype == np.dtype("=m8[ns]") + with assert_no_warnings(): + decoded.load() + assert decoded.dtype == np.dtype("=m8[s]") + assert decoded.encoding == encoding From 3d1c91c76587439f40bd54c85f9ca1cf004428f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20M=C3=BChlbauer?= Date: Wed, 14 May 2025 10:05:03 +0200 Subject: [PATCH 2/3] More speaking variable, add whats-new.rst --- doc/whats-new.rst | 3 ++- xarray/coding/times.py | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index c8fbecf82af..6f0aa6ea76d 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -38,9 +38,10 @@ Bug fixes ~~~~~~~~~ - Fix :py:class:`~xarray.groupers.BinGrouper` when ``labels`` is not specified (:issue:`10284`). By `Deepak Cherian `_. - - Allow accessing arbitrary attributes on Pandas ExtensionArrays. By `Deepak Cherian `_. +- Fix coding empty (zero-size) timedelta64 arrays, ``units`` taking precedence when encoding, fallback to default values when decoding. + By `Kai Mühlbauer `_. Documentation diff --git a/xarray/coding/times.py b/xarray/coding/times.py index 51e84a868ed..71afa7eb3e9 100644 --- a/xarray/coding/times.py +++ b/xarray/coding/times.py @@ -659,9 +659,12 @@ def decode_cf_timedelta( num_timedeltas = to_numpy(num_timedeltas) unit = _netcdf_to_numpy_timeunit(units) + # special case empty arrays + is_empty_array = num_timedeltas.size == 0 + with warnings.catch_warnings(): warnings.filterwarnings("ignore", "All-NaN slice encountered", RuntimeWarning) - if num_timedeltas.size > 0: + if not is_empty_array: _check_timedelta_range(np.nanmin(num_timedeltas), unit, time_unit) _check_timedelta_range(np.nanmax(num_timedeltas), unit, time_unit) @@ -670,14 +673,13 @@ def decode_cf_timedelta( ) pd_timedeltas = pd.to_timedelta(ravel(timedeltas)) - if timedeltas.size > 0 and np.isnat(timedeltas).all(): + if not is_empty_array and np.isnat(timedeltas).all(): empirical_unit = time_unit else: empirical_unit = pd_timedeltas.unit - if ( - np.timedelta64(1, time_unit) > np.timedelta64(1, empirical_unit) - or timedeltas.size == 0 + if is_empty_array or np.timedelta64(1, time_unit) > np.timedelta64( + 1, empirical_unit ): time_unit = empirical_unit From f8748cf613f2d878f3c15ebbb5d801be8320c55e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 10:16:50 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/whats-new.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 1edb9414554..c0130fc93a2 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -44,11 +44,11 @@ Bug fixes By `Deepak Cherian `_. - Allow accessing arbitrary attributes on Pandas ExtensionArrays. By `Deepak Cherian `_. -- Fix coding empty (zero-size) timedelta64 arrays, ``units`` taking precedence when encoding, +- Fix coding empty (zero-size) timedelta64 arrays, ``units`` taking precedence when encoding, fallback to default values when decoding (:issue:`10310`, :pull:`10313`). By `Kai Mühlbauer `_. -- Use dtype from intermediate sum instead of source dtype or "int" for casting of count when - calculating mean in rolling for correct operations (preserve float dtypes, +- Use dtype from intermediate sum instead of source dtype or "int" for casting of count when + calculating mean in rolling for correct operations (preserve float dtypes, correct mean of bool arrays) (:issue:`10340`, :pull:`10341`). By `Kai Mühlbauer `_. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy