9
9
import datetime
10
10
from abc import ABC , abstractmethod
11
11
from dataclasses import dataclass , field
12
- from typing import TYPE_CHECKING , Any , Literal , cast
12
+ from typing import Any , Literal , cast
13
13
14
14
import numpy as np
15
15
import pandas as pd
21
21
from xarray .core .indexes import safe_cast_to_index
22
22
from xarray .core .resample_cftime import CFTimeGrouper
23
23
from xarray .core .types import Bins , DatetimeLike , GroupIndices , SideOptions
24
- from xarray .core .utils import emit_user_level_warning
25
24
from xarray .core .variable import Variable
26
25
27
- if TYPE_CHECKING :
28
- pass
29
-
30
26
__all__ = [
31
27
"EncodedGroups" ,
32
28
"Grouper" ,
@@ -299,17 +295,7 @@ class TimeResampler(Resampler):
299
295
Side of each interval to treat as closed.
300
296
label : {"left", "right"}, optional
301
297
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'
313
299
The datetime on which to adjust the grouping. The timezone of origin
314
300
must match the timezone of the index.
315
301
@@ -321,60 +307,22 @@ class TimeResampler(Resampler):
321
307
- 'end_day': `origin` is the ceiling midnight of the last day
322
308
offset : pd.Timedelta, datetime.timedelta, or str, default is None
323
309
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
-
333
310
"""
334
311
335
312
freq : str
336
313
closed : SideOptions | None = field (default = None )
337
314
label : SideOptions | None = field (default = None )
338
315
origin : str | DatetimeLike = field (default = "start_day" )
339
316
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 )
342
317
343
318
index_grouper : CFTimeGrouper | pd .Grouper = field (init = False , repr = False )
344
319
group_as_index : pd .Index = field (init = False , repr = False )
345
320
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
-
367
321
def _init_properties (self , group : T_Group ) -> None :
368
322
from xarray import CFTimeIndex
369
- from xarray .core .pdcompat import _convert_base_to_offset
370
323
371
324
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
378
326
379
327
if not group_as_index .is_monotonic_increasing :
380
328
# TODO: sort instead of raising an error
@@ -389,7 +337,6 @@ def _init_properties(self, group: T_Group) -> None:
389
337
label = self .label ,
390
338
origin = self .origin ,
391
339
offset = offset ,
392
- loffset = self .loffset ,
393
340
)
394
341
else :
395
342
self .index_grouper = pd .Grouper (
@@ -419,18 +366,16 @@ def first_items(self) -> tuple[pd.Series, np.ndarray]:
419
366
return self .index_grouper .first_items (
420
367
cast (CFTimeIndex , self .group_as_index )
421
368
)
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
434
379
435
380
def factorize (self , group : T_Group ) -> EncodedGroups :
436
381
self ._init_properties (group )
@@ -454,43 +399,6 @@ def factorize(self, group: T_Group) -> EncodedGroups:
454
399
)
455
400
456
401
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
-
494
402
def unique_value_groups (
495
403
ar , sort : bool = True
496
404
) -> tuple [np .ndarray | pd .Index , np .ndarray ]:
0 commit comments