Skip to content

Commit 8b8f0d0

Browse files
authored
DEPR: Tick.delta (#56558)
* DEPR: Tick.delta * update doc * typo fixup
1 parent ae86022 commit 8b8f0d0

File tree

8 files changed

+49
-25
lines changed

8 files changed

+49
-25
lines changed

doc/source/user_guide/timeseries.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -1770,7 +1770,8 @@ We can instead only resample those groups where we have points as follows:
17701770
def round(t, freq):
17711771
# round a Timestamp to a specified freq
17721772
freq = to_offset(freq)
1773-
return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
1773+
td = pd.Timedelta(freq)
1774+
return pd.Timestamp((t.value // td.value) * td.value)
17741775
17751776
ts.groupby(partial(round, freq="3min")).sum()
17761777

doc/source/whatsnew/v2.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ Set the following option to opt into the future behavior:
438438
Other Deprecations
439439
^^^^^^^^^^^^^^^^^^
440440
- Changed :meth:`Timedelta.resolution_string` to return ``h``, ``min``, ``s``, ``ms``, ``us``, and ``ns`` instead of ``H``, ``T``, ``S``, ``L``, ``U``, and ``N``, for compatibility with respective deprecations in frequency aliases (:issue:`52536`)
441+
- Deprecated :attr:`offsets.Day.delta`, :attr:`offsets.Hour.delta`, :attr:`offsets.Minute.delta`, :attr:`offsets.Second.delta`, :attr:`offsets.Milli.delta`, :attr:`offsets.Micro.delta`, :attr:`offsets.Nano.delta`, use ``pd.Timedelta(obj)`` instead (:issue:`55498`)
441442
- Deprecated :func:`pandas.api.types.is_interval` and :func:`pandas.api.types.is_period`, use ``isinstance(obj, pd.Interval)`` and ``isinstance(obj, pd.Period)`` instead (:issue:`55264`)
442443
- Deprecated :func:`pd.core.internals.api.make_block`, use public APIs instead (:issue:`40226`)
443444
- Deprecated :func:`read_gbq` and :meth:`DataFrame.to_gbq`. Use ``pandas_gbq.read_gbq`` and ``pandas_gbq.to_gbq`` instead https://pandas-gbq.readthedocs.io/en/latest/api.html (:issue:`55525`)

pandas/_libs/tslibs/offsets.pyx

+22-11
Original file line numberDiff line numberDiff line change
@@ -913,8 +913,19 @@ cdef class Tick(SingleConstructorOffset):
913913
# Since cdef classes have no __dict__, we need to override
914914
return ""
915915

916+
@cache_readonly
917+
def _as_pd_timedelta(self):
918+
return Timedelta(self)
919+
916920
@property
917921
def delta(self):
922+
warnings.warn(
923+
# GH#55498
924+
f"{type(self).__name__}.delta is deprecated and will be removed in "
925+
"a future version. Use pd.Timedelta(obj) instead",
926+
FutureWarning,
927+
stacklevel=find_stack_level(),
928+
)
918929
try:
919930
return self.n * Timedelta(self._nanos_inc)
920931
except OverflowError as err:
@@ -962,22 +973,22 @@ cdef class Tick(SingleConstructorOffset):
962973
except ValueError:
963974
# e.g. "infer"
964975
return False
965-
return self.delta == other
976+
return self._as_pd_timedelta == other
966977

967978
def __ne__(self, other):
968979
return not (self == other)
969980

970981
def __le__(self, other):
971-
return self.delta.__le__(other)
982+
return self._as_pd_timedelta.__le__(other)
972983

973984
def __lt__(self, other):
974-
return self.delta.__lt__(other)
985+
return self._as_pd_timedelta.__lt__(other)
975986

976987
def __ge__(self, other):
977-
return self.delta.__ge__(other)
988+
return self._as_pd_timedelta.__ge__(other)
978989

979990
def __gt__(self, other):
980-
return self.delta.__gt__(other)
991+
return self._as_pd_timedelta.__gt__(other)
981992

982993
def __mul__(self, other):
983994
if is_float_object(other):
@@ -997,21 +1008,21 @@ cdef class Tick(SingleConstructorOffset):
9971008
def __truediv__(self, other):
9981009
if not isinstance(self, Tick):
9991010
# cython semantics mean the args are sometimes swapped
1000-
result = other.delta.__rtruediv__(self)
1011+
result = other._as_pd_timedelta.__rtruediv__(self)
10011012
else:
1002-
result = self.delta.__truediv__(other)
1013+
result = self._as_pd_timedelta.__truediv__(other)
10031014
return _wrap_timedelta_result(result)
10041015

10051016
def __rtruediv__(self, other):
1006-
result = self.delta.__rtruediv__(other)
1017+
result = self._as_pd_timedelta.__rtruediv__(other)
10071018
return _wrap_timedelta_result(result)
10081019

10091020
def __add__(self, other):
10101021
if isinstance(other, Tick):
10111022
if type(self) is type(other):
10121023
return type(self)(self.n + other.n)
10131024
else:
1014-
return delta_to_tick(self.delta + other.delta)
1025+
return delta_to_tick(self._as_pd_timedelta + other._as_pd_timedelta)
10151026
try:
10161027
return self._apply(other)
10171028
except ApplyTypeError:
@@ -1029,15 +1040,15 @@ cdef class Tick(SingleConstructorOffset):
10291040
# Timestamp can handle tz and nano sec, thus no need to use apply_wraps
10301041
if isinstance(other, _Timestamp):
10311042
# GH#15126
1032-
return other + self.delta
1043+
return other + self._as_pd_timedelta
10331044
elif other is NaT:
10341045
return NaT
10351046
elif cnp.is_datetime64_object(other) or PyDate_Check(other):
10361047
# PyDate_Check includes date, datetime
10371048
return Timestamp(other) + self
10381049

10391050
if cnp.is_timedelta64_object(other) or PyDelta_Check(other):
1040-
return other + self.delta
1051+
return other + self._as_pd_timedelta
10411052

10421053
raise ApplyTypeError(f"Unhandled type: {type(other).__name__}")
10431054

pandas/core/indexes/datetimelike.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ def _as_range_index(self) -> RangeIndex:
533533
# Convert our i8 representations to RangeIndex
534534
# Caller is responsible for checking isinstance(self.freq, Tick)
535535
freq = cast(Tick, self.freq)
536-
tick = freq.delta._value
536+
tick = Timedelta(freq).as_unit("ns")._value
537537
rng = range(self[0]._value, self[-1]._value + tick, tick)
538538
return RangeIndex(rng)
539539

pandas/core/indexes/datetimes.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
)
1818
from pandas._libs.tslibs import (
1919
Resolution,
20+
Tick,
21+
Timedelta,
2022
periods_per_day,
2123
timezones,
2224
to_offset,
@@ -393,10 +395,11 @@ def _is_dates_only(self) -> bool:
393395
-------
394396
bool
395397
"""
396-
delta = getattr(self.freq, "delta", None)
398+
if isinstance(self.freq, Tick):
399+
delta = Timedelta(self.freq)
397400

398-
if delta and delta % dt.timedelta(days=1) != dt.timedelta(days=0):
399-
return False
401+
if delta % dt.timedelta(days=1) != dt.timedelta(days=0):
402+
return False
400403

401404
return self._values._is_dates_only
402405

pandas/tests/arithmetic/test_numeric.py

+6
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box_with_array
299299
expected = expected.astype(dtype)
300300
elif type(three_days) is timedelta:
301301
expected = expected.astype("m8[us]")
302+
elif isinstance(
303+
three_days,
304+
(pd.offsets.Day, pd.offsets.Hour, pd.offsets.Minute, pd.offsets.Second),
305+
):
306+
# closest reso is Second
307+
expected = expected.astype("m8[s]")
302308

303309
index = tm.box_expected(index, box)
304310
expected = tm.box_expected(expected, box)

pandas/tests/scalar/timedelta/test_arithmetic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ def test_compare_tick(self, tick_classes):
10341034
cls = tick_classes
10351035

10361036
off = cls(4)
1037-
td = off.delta
1037+
td = off._as_pd_timedelta
10381038
assert isinstance(td, Timedelta)
10391039

10401040
assert td == off

pandas/tests/tseries/offsets/test_ticks.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,10 @@ def test_tick_delta_overflow():
242242
# GH#55503 raise OutOfBoundsTimedelta, not OverflowError
243243
tick = offsets.Day(10**9)
244244
msg = "Cannot cast 1000000000 days 00:00:00 to unit='ns' without overflow"
245+
depr_msg = "Day.delta is deprecated"
245246
with pytest.raises(OutOfBoundsTimedelta, match=msg):
246-
tick.delta
247+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
248+
tick.delta
247249

248250

249251
@pytest.mark.parametrize("cls", tick_classes)
@@ -254,24 +256,24 @@ def test_tick_division(cls):
254256
assert off / 2 == cls(5)
255257
assert off / 2.0 == cls(5)
256258

257-
assert off / off.delta == 1
258-
assert off / off.delta.to_timedelta64() == 1
259+
assert off / off._as_pd_timedelta == 1
260+
assert off / off._as_pd_timedelta.to_timedelta64() == 1
259261

260-
assert off / Nano(1) == off.delta / Nano(1).delta
262+
assert off / Nano(1) == off._as_pd_timedelta / Nano(1)._as_pd_timedelta
261263

262264
if cls is not Nano:
263265
# A case where we end up with a smaller class
264266
result = off / 1000
265267
assert isinstance(result, offsets.Tick)
266268
assert not isinstance(result, cls)
267-
assert result.delta == off.delta / 1000
269+
assert result._as_pd_timedelta == off._as_pd_timedelta / 1000
268270

269271
if cls._nanos_inc < Timedelta(seconds=1)._value:
270272
# Case where we end up with a bigger class
271273
result = off / 0.001
272274
assert isinstance(result, offsets.Tick)
273275
assert not isinstance(result, cls)
274-
assert result.delta == off.delta / 0.001
276+
assert result._as_pd_timedelta == off._as_pd_timedelta / 0.001
275277

276278

277279
def test_tick_mul_float():
@@ -293,7 +295,7 @@ def test_tick_mul_float():
293295
@pytest.mark.parametrize("cls", tick_classes)
294296
def test_tick_rdiv(cls):
295297
off = cls(10)
296-
delta = off.delta
298+
delta = off._as_pd_timedelta
297299
td64 = delta.to_timedelta64()
298300
instance__type = ".".join([cls.__module__, cls.__name__])
299301
msg = (
@@ -385,7 +387,7 @@ def test_compare_ticks_to_strs(cls):
385387
def test_compare_ticks_to_timedeltalike(cls):
386388
off = cls(19)
387389

388-
td = off.delta
390+
td = off._as_pd_timedelta
389391

390392
others = [td, td.to_timedelta64()]
391393
if cls is not Nano:

0 commit comments

Comments
 (0)