Skip to content

Commit 39d5b39

Browse files
Delete base and loffset parameters to resample (#9233)
* Remove `base`, `loffset` in Resampler * resample: Remove how/dim checks * lint * cleanups * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_cftimeindex_resample.py * cleanup --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 3013fb4 commit 39d5b39

File tree

9 files changed

+34
-391
lines changed

9 files changed

+34
-391
lines changed

doc/whats-new.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ New Features
4242

4343
Breaking changes
4444
~~~~~~~~~~~~~~~~
45+
- The ``base`` and ``loffset`` parameters to :py:meth:`Dataset.resample` and :py:meth:`DataArray.resample`
46+
is now removed. These parameters has been deprecated since v2023.03.0. Using the
47+
``origin`` or ``offset`` parameters is recommended as a replacement for using
48+
the ``base`` parameter and using time offset arithmetic is recommended as a
49+
replacement for using the ``loffset`` parameter.
4550

4651

4752
Deprecations

xarray/core/common.py

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,8 @@ def _resample(
881881
skipna: bool | None,
882882
closed: SideOptions | None,
883883
label: SideOptions | None,
884-
base: int | None,
885884
offset: pd.Timedelta | datetime.timedelta | str | None,
886885
origin: str | DatetimeLike,
887-
loffset: datetime.timedelta | str | None,
888886
restore_coord_dims: bool | None,
889887
**indexer_kwargs: str | Resampler,
890888
) -> T_Resample:
@@ -906,16 +904,6 @@ def _resample(
906904
Side of each interval to treat as closed.
907905
label : {"left", "right"}, optional
908906
Side of each interval to use for labeling.
909-
base : int, optional
910-
For frequencies that evenly subdivide 1 day, the "origin" of the
911-
aggregated intervals. For example, for "24H" frequency, base could
912-
range from 0 through 23.
913-
914-
.. deprecated:: 2023.03.0
915-
Following pandas, the ``base`` parameter is deprecated in favor
916-
of the ``origin`` and ``offset`` parameters, and will be removed
917-
in a future version of xarray.
918-
919907
origin : {'epoch', 'start', 'start_day', 'end', 'end_day'}, pd.Timestamp, datetime.datetime, np.datetime64, or cftime.datetime, default 'start_day'
920908
The datetime on which to adjust the grouping. The timezone of origin
921909
must match the timezone of the index.
@@ -928,15 +916,6 @@ def _resample(
928916
- 'end_day': `origin` is the ceiling midnight of the last day
929917
offset : pd.Timedelta, datetime.timedelta, or str, default is None
930918
An offset timedelta added to the origin.
931-
loffset : timedelta or str, optional
932-
Offset used to adjust the resampled time labels. Some pandas date
933-
offset strings are supported.
934-
935-
.. deprecated:: 2023.03.0
936-
Following pandas, the ``loffset`` parameter is deprecated in favor
937-
of using time offset arithmetic, and will be removed in a future
938-
version of xarray.
939-
940919
restore_coord_dims : bool, optional
941920
If True, also restore the dimension order of multi-dimensional
942921
coordinates.
@@ -1072,18 +1051,6 @@ def _resample(
10721051
from xarray.core.groupers import Resampler, TimeResampler
10731052
from xarray.core.resample import RESAMPLE_DIM
10741053

1075-
# note: the second argument (now 'skipna') use to be 'dim'
1076-
if (
1077-
(skipna is not None and not isinstance(skipna, bool))
1078-
or ("how" in indexer_kwargs and "how" not in self.dims)
1079-
or ("dim" in indexer_kwargs and "dim" not in self.dims)
1080-
):
1081-
raise TypeError(
1082-
"resample() no longer supports the `how` or "
1083-
"`dim` arguments. Instead call methods on resample "
1084-
"objects, e.g., data.resample(time='1D').mean()"
1085-
)
1086-
10871054
indexer = either_dict_or_kwargs(indexer, indexer_kwargs, "resample")
10881055
if len(indexer) != 1:
10891056
raise ValueError("Resampling only supported along single dimensions.")
@@ -1093,22 +1060,13 @@ def _resample(
10931060
dim_coord = self[dim]
10941061

10951062
group = DataArray(
1096-
dim_coord,
1097-
coords=dim_coord.coords,
1098-
dims=dim_coord.dims,
1099-
name=RESAMPLE_DIM,
1063+
dim_coord, coords=dim_coord.coords, dims=dim_coord.dims, name=RESAMPLE_DIM
11001064
)
11011065

11021066
grouper: Resampler
11031067
if isinstance(freq, str):
11041068
grouper = TimeResampler(
1105-
freq=freq,
1106-
closed=closed,
1107-
label=label,
1108-
origin=origin,
1109-
offset=offset,
1110-
loffset=loffset,
1111-
base=base,
1069+
freq=freq, closed=closed, label=label, origin=origin, offset=offset
11121070
)
11131071
elif isinstance(freq, Resampler):
11141072
grouper = freq

xarray/core/dataarray.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7245,10 +7245,8 @@ def resample(
72457245
skipna: bool | None = None,
72467246
closed: SideOptions | None = None,
72477247
label: SideOptions | None = None,
7248-
base: int | None = None,
72497248
offset: pd.Timedelta | datetime.timedelta | str | None = None,
72507249
origin: str | DatetimeLike = "start_day",
7251-
loffset: datetime.timedelta | str | None = None,
72527250
restore_coord_dims: bool | None = None,
72537251
**indexer_kwargs: str | Resampler,
72547252
) -> DataArrayResample:
@@ -7270,10 +7268,6 @@ def resample(
72707268
Side of each interval to treat as closed.
72717269
label : {"left", "right"}, optional
72727270
Side of each interval to use for labeling.
7273-
base : int, optional
7274-
For frequencies that evenly subdivide 1 day, the "origin" of the
7275-
aggregated intervals. For example, for "24H" frequency, base could
7276-
range from 0 through 23.
72777271
origin : {'epoch', 'start', 'start_day', 'end', 'end_day'}, pd.Timestamp, datetime.datetime, np.datetime64, or cftime.datetime, default 'start_day'
72787272
The datetime on which to adjust the grouping. The timezone of origin
72797273
must match the timezone of the index.
@@ -7286,15 +7280,6 @@ def resample(
72867280
- 'end_day': `origin` is the ceiling midnight of the last day
72877281
offset : pd.Timedelta, datetime.timedelta, or str, default is None
72887282
An offset timedelta added to the origin.
7289-
loffset : timedelta or str, optional
7290-
Offset used to adjust the resampled time labels. Some pandas date
7291-
offset strings are supported.
7292-
7293-
.. deprecated:: 2023.03.0
7294-
Following pandas, the ``loffset`` parameter is deprecated in favor
7295-
of using time offset arithmetic, and will be removed in a future
7296-
version of xarray.
7297-
72987283
restore_coord_dims : bool, optional
72997284
If True, also restore the dimension order of multi-dimensional
73007285
coordinates.
@@ -7399,10 +7384,8 @@ def resample(
73997384
skipna=skipna,
74007385
closed=closed,
74017386
label=label,
7402-
base=base,
74037387
offset=offset,
74047388
origin=origin,
7405-
loffset=loffset,
74067389
restore_coord_dims=restore_coord_dims,
74077390
**indexer_kwargs,
74087391
)

xarray/core/dataset.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10631,10 +10631,8 @@ def resample(
1063110631
skipna: bool | None = None,
1063210632
closed: SideOptions | None = None,
1063310633
label: SideOptions | None = None,
10634-
base: int | None = None,
1063510634
offset: pd.Timedelta | datetime.timedelta | str | None = None,
1063610635
origin: str | DatetimeLike = "start_day",
10637-
loffset: datetime.timedelta | str | None = None,
1063810636
restore_coord_dims: bool | None = None,
1063910637
**indexer_kwargs: str | Resampler,
1064010638
) -> DatasetResample:
@@ -10656,10 +10654,6 @@ def resample(
1065610654
Side of each interval to treat as closed.
1065710655
label : {"left", "right"}, optional
1065810656
Side of each interval to use for labeling.
10659-
base : int, optional
10660-
For frequencies that evenly subdivide 1 day, the "origin" of the
10661-
aggregated intervals. For example, for "24H" frequency, base could
10662-
range from 0 through 23.
1066310657
origin : {'epoch', 'start', 'start_day', 'end', 'end_day'}, pd.Timestamp, datetime.datetime, np.datetime64, or cftime.datetime, default 'start_day'
1066410658
The datetime on which to adjust the grouping. The timezone of origin
1066510659
must match the timezone of the index.
@@ -10672,15 +10666,6 @@ def resample(
1067210666
- 'end_day': `origin` is the ceiling midnight of the last day
1067310667
offset : pd.Timedelta, datetime.timedelta, or str, default is None
1067410668
An offset timedelta added to the origin.
10675-
loffset : timedelta or str, optional
10676-
Offset used to adjust the resampled time labels. Some pandas date
10677-
offset strings are supported.
10678-
10679-
.. deprecated:: 2023.03.0
10680-
Following pandas, the ``loffset`` parameter is deprecated in favor
10681-
of using time offset arithmetic, and will be removed in a future
10682-
version of xarray.
10683-
1068410669
restore_coord_dims : bool, optional
1068510670
If True, also restore the dimension order of multi-dimensional
1068610671
coordinates.
@@ -10713,10 +10698,8 @@ def resample(
1071310698
skipna=skipna,
1071410699
closed=closed,
1071510700
label=label,
10716-
base=base,
1071710701
offset=offset,
1071810702
origin=origin,
10719-
loffset=loffset,
1072010703
restore_coord_dims=restore_coord_dims,
1072110704
**indexer_kwargs,
1072210705
)

xarray/core/groupers.py

Lines changed: 13 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import datetime
1010
from abc import ABC, abstractmethod
1111
from dataclasses import dataclass, field
12-
from typing import TYPE_CHECKING, Any, Literal, cast
12+
from typing import Any, Literal, cast
1313

1414
import numpy as np
1515
import pandas as pd
@@ -21,12 +21,8 @@
2121
from xarray.core.indexes import safe_cast_to_index
2222
from xarray.core.resample_cftime import CFTimeGrouper
2323
from xarray.core.types import Bins, DatetimeLike, GroupIndices, SideOptions
24-
from xarray.core.utils import emit_user_level_warning
2524
from xarray.core.variable import Variable
2625

27-
if TYPE_CHECKING:
28-
pass
29-
3026
__all__ = [
3127
"EncodedGroups",
3228
"Grouper",
@@ -299,17 +295,7 @@ class TimeResampler(Resampler):
299295
Side of each interval to treat as closed.
300296
label : {"left", "right"}, optional
301297
Side of each interval to use for labeling.
302-
base : int, optional
303-
For frequencies that evenly subdivide 1 day, the "origin" of the
304-
aggregated intervals. For example, for "24H" frequency, base could
305-
range from 0 through 23.
306-
307-
.. deprecated:: 2023.03.0
308-
Following pandas, the ``base`` parameter is deprecated in favor
309-
of the ``origin`` and ``offset`` parameters, and will be removed
310-
in a future version of xarray.
311-
312-
origin : {"epoch", "start", "start_day", "end", "end_day"}, pandas.Timestamp, datetime.datetime, numpy.datetime64, or cftime.datetime, default: "start_day"
298+
origin : {'epoch', 'start', 'start_day', 'end', 'end_day'}, pandas.Timestamp, datetime.datetime, numpy.datetime64, or cftime.datetime, default 'start_day'
313299
The datetime on which to adjust the grouping. The timezone of origin
314300
must match the timezone of the index.
315301
@@ -321,60 +307,22 @@ class TimeResampler(Resampler):
321307
- 'end_day': `origin` is the ceiling midnight of the last day
322308
offset : pd.Timedelta, datetime.timedelta, or str, default is None
323309
An offset timedelta added to the origin.
324-
loffset : timedelta or str, optional
325-
Offset used to adjust the resampled time labels. Some pandas date
326-
offset strings are supported.
327-
328-
.. deprecated:: 2023.03.0
329-
Following pandas, the ``loffset`` parameter is deprecated in favor
330-
of using time offset arithmetic, and will be removed in a future
331-
version of xarray.
332-
333310
"""
334311

335312
freq: str
336313
closed: SideOptions | None = field(default=None)
337314
label: SideOptions | None = field(default=None)
338315
origin: str | DatetimeLike = field(default="start_day")
339316
offset: pd.Timedelta | datetime.timedelta | str | None = field(default=None)
340-
loffset: datetime.timedelta | str | None = field(default=None)
341-
base: int | None = field(default=None)
342317

343318
index_grouper: CFTimeGrouper | pd.Grouper = field(init=False, repr=False)
344319
group_as_index: pd.Index = field(init=False, repr=False)
345320

346-
def __post_init__(self):
347-
if self.loffset is not None:
348-
emit_user_level_warning(
349-
"Following pandas, the `loffset` parameter to resample is deprecated. "
350-
"Switch to updating the resampled dataset time coordinate using "
351-
"time offset arithmetic. For example:\n"
352-
" >>> offset = pd.tseries.frequencies.to_offset(freq) / 2\n"
353-
' >>> resampled_ds["time"] = resampled_ds.get_index("time") + offset',
354-
FutureWarning,
355-
)
356-
357-
if self.base is not None:
358-
emit_user_level_warning(
359-
"Following pandas, the `base` parameter to resample will be deprecated in "
360-
"a future version of xarray. Switch to using `origin` or `offset` instead.",
361-
FutureWarning,
362-
)
363-
364-
if self.base is not None and self.offset is not None:
365-
raise ValueError("base and offset cannot be present at the same time")
366-
367321
def _init_properties(self, group: T_Group) -> None:
368322
from xarray import CFTimeIndex
369-
from xarray.core.pdcompat import _convert_base_to_offset
370323

371324
group_as_index = safe_cast_to_index(group)
372-
373-
if self.base is not None:
374-
# grouper constructor verifies that grouper.offset is None at this point
375-
offset = _convert_base_to_offset(self.base, self.freq, group_as_index)
376-
else:
377-
offset = self.offset
325+
offset = self.offset
378326

379327
if not group_as_index.is_monotonic_increasing:
380328
# TODO: sort instead of raising an error
@@ -389,7 +337,6 @@ def _init_properties(self, group: T_Group) -> None:
389337
label=self.label,
390338
origin=self.origin,
391339
offset=offset,
392-
loffset=self.loffset,
393340
)
394341
else:
395342
self.index_grouper = pd.Grouper(
@@ -419,18 +366,16 @@ def first_items(self) -> tuple[pd.Series, np.ndarray]:
419366
return self.index_grouper.first_items(
420367
cast(CFTimeIndex, self.group_as_index)
421368
)
422-
423-
s = pd.Series(np.arange(self.group_as_index.size), self.group_as_index)
424-
grouped = s.groupby(self.index_grouper)
425-
first_items = grouped.first()
426-
counts = grouped.count()
427-
# This way we generate codes for the final output index: full_index.
428-
# So for _flox_reduce we avoid one reindex and copy by avoiding
429-
# _maybe_restore_empty_groups
430-
codes = np.repeat(np.arange(len(first_items)), counts)
431-
if self.loffset is not None:
432-
_apply_loffset(self.loffset, first_items)
433-
return first_items, codes
369+
else:
370+
s = pd.Series(np.arange(self.group_as_index.size), self.group_as_index)
371+
grouped = s.groupby(self.index_grouper)
372+
first_items = grouped.first()
373+
counts = grouped.count()
374+
# This way we generate codes for the final output index: full_index.
375+
# So for _flox_reduce we avoid one reindex and copy by avoiding
376+
# _maybe_restore_empty_groups
377+
codes = np.repeat(np.arange(len(first_items)), counts)
378+
return first_items, codes
434379

435380
def factorize(self, group: T_Group) -> EncodedGroups:
436381
self._init_properties(group)
@@ -454,43 +399,6 @@ def factorize(self, group: T_Group) -> EncodedGroups:
454399
)
455400

456401

457-
def _apply_loffset(
458-
loffset: str | pd.DateOffset | datetime.timedelta | pd.Timedelta,
459-
result: pd.Series | pd.DataFrame,
460-
):
461-
"""
462-
(copied from pandas)
463-
if loffset is set, offset the result index
464-
465-
This is NOT an idempotent routine, it will be applied
466-
exactly once to the result.
467-
468-
Parameters
469-
----------
470-
result : Series or DataFrame
471-
the result of resample
472-
"""
473-
# pd.Timedelta is a subclass of datetime.timedelta so we do not need to
474-
# include it in instance checks.
475-
if not isinstance(loffset, (str, pd.DateOffset, datetime.timedelta)):
476-
raise ValueError(
477-
f"`loffset` must be a str, pd.DateOffset, datetime.timedelta, or pandas.Timedelta object. "
478-
f"Got {loffset}."
479-
)
480-
481-
if isinstance(loffset, str):
482-
loffset = pd.tseries.frequencies.to_offset(loffset) # type: ignore[assignment]
483-
484-
needs_offset = (
485-
isinstance(loffset, (pd.DateOffset, datetime.timedelta))
486-
and isinstance(result.index, pd.DatetimeIndex)
487-
and len(result.index) > 0
488-
)
489-
490-
if needs_offset:
491-
result.index = result.index + loffset
492-
493-
494402
def unique_value_groups(
495403
ar, sort: bool = True
496404
) -> tuple[np.ndarray | pd.Index, np.ndarray]:

0 commit comments

Comments
 (0)