Skip to content

Commit d21eda2

Browse files
spencerkclarkmathausekmuehlbauerpre-commit-ci[bot]
authored
Fix false timedelta decoding SerializationWarning and improve warning message (#10072)
* Fix false timedelta serialization warning * Optional[PDDatetimeUnitOptions] -> PDDatetimeUnitOptions | None Co-authored-by: Mathias Hauser <mathause@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Mathias Hauser <mathause@users.noreply.github.com> Co-authored-by: Kai Mühlbauer <kai.muehlbauer@uni-bonn.de> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c97399f commit d21eda2

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

doc/whats-new.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ Bug fixes
6262
- Fix DataArray().drop_attrs(deep=False) and add support for attrs to
6363
DataArray()._replace(). (:issue:`10027`, :pull:`10030`). By `Jan
6464
Haacker <https://github.com/j-haacker>`_.
65+
- Prevent false resolution change warnings from being emitted when decoding
66+
timedeltas encoded with floating point values, and make it clearer how to
67+
silence this warning message in the case that it is rightfully emitted
68+
(:issue:`10071`, :pull:`10072`). By `Spencer Clark
69+
<https://github.com/spencerkclark>`_.
6570
- Fix ``isel`` for multi-coordinate Xarray indexes (:issue:`10063`, :pull:`10066`).
6671
By `Benoit Bovy <https://github.com/benbovy>`_.
6772
- Fix dask tokenization when opening each node in :py:func:`xarray.open_datatree`

xarray/coding/times.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def _decode_datetime_with_pandas(
488488
flat_num_dates = flat_num_dates.astype(np.float64)
489489

490490
timedeltas = _numbers_to_timedelta(
491-
flat_num_dates, time_unit, ref_date.unit, "datetime"
491+
flat_num_dates, time_unit, ref_date.unit, "datetimes"
492492
)
493493

494494
# add timedeltas to ref_date
@@ -582,6 +582,7 @@ def _numbers_to_timedelta(
582582
time_unit: NPDatetimeUnitOptions,
583583
ref_unit: PDDatetimeUnitOptions,
584584
datatype: str,
585+
target_unit: PDDatetimeUnitOptions | None = None,
585586
) -> np.ndarray:
586587
"""Transform numbers to np.timedelta64."""
587588
# keep NaT/nan mask
@@ -605,13 +606,23 @@ def _numbers_to_timedelta(
605606
flat_num, cast(PDDatetimeUnitOptions, time_unit)
606607
)
607608
if time_unit != new_time_unit:
608-
msg = (
609-
f"Can't decode floating point {datatype} to {time_unit!r} without "
610-
f"precision loss, decoding to {new_time_unit!r} instead. "
611-
f"To silence this warning use time_unit={new_time_unit!r} in call to "
612-
f"decoding function."
613-
)
614-
emit_user_level_warning(msg, SerializationWarning)
609+
if target_unit is None or np.timedelta64(1, target_unit) > np.timedelta64(
610+
1, new_time_unit
611+
):
612+
if datatype == "datetimes":
613+
kwarg = "decode_times"
614+
coder = "CFDatetimeCoder"
615+
else:
616+
kwarg = "decode_timedelta"
617+
coder = "CFTimedeltaCoder"
618+
formatted_kwarg = f"{kwarg}={coder}(time_unit={new_time_unit!r})"
619+
message = (
620+
f"Can't decode floating point {datatype} to {time_unit!r} "
621+
f"without precision loss; decoding to {new_time_unit!r} "
622+
f"instead. To silence this warning pass {formatted_kwarg} "
623+
f"to your opening function."
624+
)
625+
emit_user_level_warning(message, SerializationWarning)
615626
time_unit = new_time_unit
616627

617628
# Cast input ordinals to integers and properly handle NaN/NaT
@@ -640,7 +651,9 @@ def decode_cf_timedelta(
640651
_check_timedelta_range(np.nanmin(num_timedeltas), unit, time_unit)
641652
_check_timedelta_range(np.nanmax(num_timedeltas), unit, time_unit)
642653

643-
timedeltas = _numbers_to_timedelta(num_timedeltas, unit, "s", "timedelta")
654+
timedeltas = _numbers_to_timedelta(
655+
num_timedeltas, unit, "s", "timedeltas", target_unit=time_unit
656+
)
644657
pd_timedeltas = pd.to_timedelta(ravel(timedeltas))
645658

646659
if np.isnat(timedeltas).all():

xarray/tests/test_coding_times.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ def test_coding_float_datetime_warning(
13371337
values = np.array([num], dtype="float32")
13381338
with pytest.warns(
13391339
SerializationWarning,
1340-
match=f"Can't decode floating point datetime to {time_unit!r}",
1340+
match=f"Can't decode floating point datetimes to {time_unit!r}",
13411341
):
13421342
decode_cf_datetime(values, units, calendar, time_unit=time_unit)
13431343

@@ -1907,3 +1907,11 @@ def test_lazy_decode_timedelta_error() -> None:
19071907
)
19081908
with pytest.raises(OutOfBoundsTimedelta, match="overflow"):
19091909
decoded.load()
1910+
1911+
1912+
def test_decode_floating_point_timedelta_no_serialization_warning() -> None:
1913+
attrs = {"units": "seconds"}
1914+
encoded = Variable(["time"], [0, 0.1, 0.2], attrs=attrs)
1915+
decoded = conventions.decode_cf_variable("foo", encoded, decode_timedelta=True)
1916+
with assert_no_warnings():
1917+
decoded.load()

0 commit comments

Comments
 (0)