Skip to content

Commit 9e82b66

Browse files
authored
Better cftime handling (#426)
1 parent 8b4eeac commit 9e82b66

File tree

2 files changed

+22
-3
lines changed

2 files changed

+22
-3
lines changed

flox/core.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import copy
4+
import datetime
45
import itertools
56
import logging
67
import math
@@ -2509,6 +2510,7 @@ def groupby_reduce(
25092510
(func not in ["count", "any", "all"] and not is_first_last)
25102511
# Flox's count works with non-numeric and its faster than converting.
25112512
or (func == "count" and engine != "flox")
2513+
# TODO: needed for npg, move to aggregate_npg
25122514
or (is_first_last and is_cftime)
25132515
)
25142516
if requires_numeric:
@@ -2710,7 +2712,11 @@ def groupby_reduce(
27102712
if is_npdatetime:
27112713
result = result.astype(datetime_dtype)
27122714
elif is_cftime:
2713-
result = _to_pytimedelta(result, unit="us") + offset
2715+
asdelta = _to_pytimedelta(result, unit="us")
2716+
nanmask = np.isnan(result)
2717+
asdelta[nanmask] = datetime.timedelta(microseconds=0)
2718+
result = asdelta + offset
2719+
result[nanmask] = np.timedelta64("NaT")
27142720

27152721
return (result, *groups)
27162722

tests/test_xarray.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
xr = pytest.importorskip("xarray")
77
# isort: on
88

9+
from flox import xrdtypes as dtypes
910
from flox.xarray import rechunk_for_blockwise, xarray_reduce
1011

1112
from . import (
@@ -193,13 +194,25 @@ def test_validate_expected_groups(expected_groups):
193194

194195

195196
@requires_cftime
197+
@pytest.mark.parametrize("indexer", [slice(None), pytest.param(slice(12), id="missing-group")])
198+
@pytest.mark.parametrize("expected_groups", [None, [0, 1, 2, 3]])
196199
@pytest.mark.parametrize("func", ["first", "last", "min", "max", "count"])
197-
def test_xarray_reduce_cftime_var(engine, func):
200+
def test_xarray_reduce_cftime_var(engine, indexer, expected_groups, func):
198201
times = xr.date_range("1980-09-01 00:00", "1982-09-18 00:00", freq="ME", calendar="noleap")
199202
ds = xr.Dataset({"var": ("time", times)}, coords={"time": np.repeat(np.arange(4), 6)})
203+
ds = ds.isel(time=indexer)
200204

201-
actual = xarray_reduce(ds, ds.time, func=func)
205+
actual = xarray_reduce(
206+
ds,
207+
ds.time,
208+
func=func,
209+
fill_value=dtypes.NA if func in ["first", "last"] else np.nan,
210+
engine=engine,
211+
expected_groups=expected_groups,
212+
)
202213
expected = getattr(ds.groupby("time"), func)()
214+
if expected_groups is not None:
215+
expected = expected.reindex(time=expected_groups)
203216
xr.testing.assert_identical(actual, expected)
204217

205218

0 commit comments

Comments
 (0)