Skip to content

Commit ffa4c77

Browse files
TomAugspurgerjreback
authored andcommitted
DEPR: Undeprecate period - integer addition (#24352)
1 parent 3e0358d commit ffa4c77

File tree

7 files changed

+94
-141
lines changed

7 files changed

+94
-141
lines changed

doc/source/whatsnew/v0.24.0.rst

+6-18
Original file line numberDiff line numberDiff line change
@@ -1153,25 +1153,19 @@ Deprecations
11531153

11541154
.. _whatsnew_0240.deprecations.datetimelike_int_ops:
11551155

1156-
Integer Addition/Subtraction with Datetime-like Classes Is Deprecated
1157-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1158-
In the past, users could add or subtract integers or integer-dtypes arrays
1159-
from :class:`Period`, :class:`PeriodIndex`, and in some cases
1160-
:class:`Timestamp`, :class:`DatetimeIndex` and :class:`TimedeltaIndex`.
1156+
Integer Addition/Subtraction with Datetimes and Timedeltas is Deprecated
1157+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1158+
1159+
In the past, users could—in some cases—add or subtract integers or integer-dtype
1160+
arrays from :class:`Timestamp`, :class:`DatetimeIndex` and :class:`TimedeltaIndex`.
11611161

11621162
This usage is now deprecated. Instead add or subtract integer multiples of
1163-
the object's ``freq`` attribute. The result of subtraction of :class:`Period`
1164-
objects will be agnostic of the multiplier of the objects' ``freq`` attribute
1165-
(:issue:`21939`, :issue:`23878`).
1163+
the object's ``freq`` attribute (:issue:`21939`, :issue:`23878`).
11661164

11671165
*Previous Behavior*:
11681166

11691167
.. code-block:: ipython
11701168
1171-
In [3]: per = pd.Period('2016Q1')
1172-
In [4]: per + 3
1173-
Out[4]: Period('2016Q4', 'Q-DEC')
1174-
11751169
In [5]: ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour())
11761170
In [6]: ts + 2
11771171
Out[6]: Timestamp('1994-05-06 14:15:16', freq='H')
@@ -1189,12 +1183,6 @@ objects will be agnostic of the multiplier of the objects' ``freq`` attribute
11891183
.. ipython:: python
11901184
:okwarning:
11911185
1192-
per = pd.Period('2016Q1')
1193-
per + 3
1194-
1195-
per = pd.Period('2016Q1')
1196-
per + 3 * per.freq
1197-
11981186
ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour())
11991187
ts + 2 * ts.freq
12001188

pandas/_libs/tslibs/period.pyx

+1-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ cdef extern from "src/datetime/np_datetime.h":
3232
cimport util
3333
from util cimport is_period_object, is_string_object
3434

35-
from timestamps import Timestamp, maybe_integer_op_deprecated
35+
from timestamps import Timestamp
3636
from timezones cimport is_utc, is_tzlocal, get_dst_info
3737
from timedeltas import Timedelta
3838
from timedeltas cimport delta_to_nanoseconds
@@ -1655,8 +1655,6 @@ cdef class _Period(object):
16551655
elif other is NaT:
16561656
return NaT
16571657
elif util.is_integer_object(other):
1658-
maybe_integer_op_deprecated(self)
1659-
16601658
ordinal = self.ordinal + other * self.freq.n
16611659
return Period(ordinal=ordinal, freq=self.freq)
16621660
elif (PyDateTime_Check(other) or
@@ -1683,8 +1681,6 @@ cdef class _Period(object):
16831681
neg_other = -other
16841682
return self + neg_other
16851683
elif util.is_integer_object(other):
1686-
maybe_integer_op_deprecated(self)
1687-
16881684
ordinal = self.ordinal - other * self.freq.n
16891685
return Period(ordinal=ordinal, freq=self.freq)
16901686
elif is_period_object(other):

pandas/core/arrays/datetimelike.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,8 @@ def __add__(self, other):
905905
elif lib.is_integer(other):
906906
# This check must come after the check for np.timedelta64
907907
# as is_integer returns True for these
908-
maybe_integer_op_deprecated(self)
908+
if not is_period_dtype(self):
909+
maybe_integer_op_deprecated(self)
909910
result = self._time_shift(other)
910911

911912
# array-like others
@@ -919,7 +920,8 @@ def __add__(self, other):
919920
# DatetimeIndex, ndarray[datetime64]
920921
return self._add_datetime_arraylike(other)
921922
elif is_integer_dtype(other):
922-
maybe_integer_op_deprecated(self)
923+
if not is_period_dtype(self):
924+
maybe_integer_op_deprecated(self)
923925
result = self._addsub_int_array(other, operator.add)
924926
elif is_float_dtype(other):
925927
# Explicitly catch invalid dtypes
@@ -966,7 +968,8 @@ def __sub__(self, other):
966968
elif lib.is_integer(other):
967969
# This check must come after the check for np.timedelta64
968970
# as is_integer returns True for these
969-
maybe_integer_op_deprecated(self)
971+
if not is_period_dtype(self):
972+
maybe_integer_op_deprecated(self)
970973
result = self._time_shift(-other)
971974

972975
elif isinstance(other, Period):
@@ -986,7 +989,8 @@ def __sub__(self, other):
986989
# PeriodIndex
987990
result = self._sub_period_array(other)
988991
elif is_integer_dtype(other):
989-
maybe_integer_op_deprecated(self)
992+
if not is_period_dtype(self):
993+
maybe_integer_op_deprecated(self)
990994
result = self._addsub_int_array(other, operator.sub)
991995
elif isinstance(other, ABCIndexClass):
992996
raise TypeError("cannot subtract {cls} and {typ}"

pandas/tests/arithmetic/test_period.py

+49-67
Original file line numberDiff line numberDiff line change
@@ -659,14 +659,10 @@ def test_pi_sub_offset_array(self, box):
659659
def test_pi_add_iadd_int(self, one):
660660
# Variants of `one` for #19012
661661
rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10)
662-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
663-
clear=[pd.core.arrays.datetimelike]):
664-
result = rng + one
662+
result = rng + one
665663
expected = pd.period_range('2000-01-01 10:00', freq='H', periods=10)
666664
tm.assert_index_equal(result, expected)
667-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
668-
clear=[pd.core.arrays.datetimelike]):
669-
rng += one
665+
rng += one
670666
tm.assert_index_equal(rng, expected)
671667

672668
def test_pi_sub_isub_int(self, one):
@@ -675,24 +671,18 @@ def test_pi_sub_isub_int(self, one):
675671
the integer 1, e.g. int, long, np.int64, np.uint8, ...
676672
"""
677673
rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10)
678-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
679-
clear=[pd.core.arrays.datetimelike]):
680-
result = rng - one
674+
result = rng - one
681675
expected = pd.period_range('2000-01-01 08:00', freq='H', periods=10)
682676
tm.assert_index_equal(result, expected)
683-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
684-
clear=[pd.core.arrays.datetimelike]):
685-
rng -= one
677+
rng -= one
686678
tm.assert_index_equal(rng, expected)
687679

688680
@pytest.mark.parametrize('five', [5, np.array(5, dtype=np.int64)])
689681
def test_pi_sub_intlike(self, five):
690682
rng = period_range('2007-01', periods=50)
691683

692-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
693-
clear=[pd.core.arrays.datetimelike]):
694-
result = rng - five
695-
exp = rng + (-five)
684+
result = rng - five
685+
exp = rng + (-five)
696686
tm.assert_index_equal(result, exp)
697687

698688
def test_pi_sub_isub_offset(self):
@@ -757,9 +747,8 @@ def test_pi_add_intarray(self, int_holder, op):
757747
# GH#19959
758748
pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')])
759749
other = int_holder([4, -1])
760-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
761-
clear=[pd.core.arrays.datetimelike]):
762-
result = op(pi, other)
750+
751+
result = op(pi, other)
763752
expected = pd.PeriodIndex([pd.Period('2016Q1'), pd.Period('NaT')])
764753
tm.assert_index_equal(result, expected)
765754

@@ -768,16 +757,13 @@ def test_pi_sub_intarray(self, int_holder):
768757
# GH#19959
769758
pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')])
770759
other = int_holder([4, -1])
771-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
772-
clear=[pd.core.arrays.datetimelike]):
773-
result = pi - other
760+
761+
result = pi - other
774762
expected = pd.PeriodIndex([pd.Period('2014Q1'), pd.Period('NaT')])
775763
tm.assert_index_equal(result, expected)
776764

777765
with pytest.raises(TypeError):
778-
with tm.assert_produces_warning(FutureWarning,
779-
check_stacklevel=False):
780-
other - pi
766+
other - pi
781767

782768
# ---------------------------------------------------------------
783769
# Timedelta-like (timedelta, timedelta64, Timedelta, Tick)
@@ -1039,12 +1025,11 @@ def test_pi_ops(self):
10391025

10401026
expected = PeriodIndex(['2011-03', '2011-04', '2011-05', '2011-06'],
10411027
freq='M', name='idx')
1042-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1043-
clear=[pd.core.arrays.datetimelike]):
1044-
self._check(idx, lambda x: x + 2, expected)
1045-
self._check(idx, lambda x: 2 + x, expected)
10461028

1047-
self._check(idx + 2, lambda x: x - 2, idx)
1029+
self._check(idx, lambda x: x + 2, expected)
1030+
self._check(idx, lambda x: 2 + x, expected)
1031+
1032+
self._check(idx + 2, lambda x: x - 2, idx)
10481033

10491034
result = idx - Period('2011-01', freq='M')
10501035
off = idx.freq
@@ -1089,53 +1074,50 @@ def test_pi_ops_nat(self):
10891074
freq='M', name='idx')
10901075
expected = PeriodIndex(['2011-03', '2011-04', 'NaT', '2011-06'],
10911076
freq='M', name='idx')
1092-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1093-
clear=[pd.core.arrays.datetimelike]):
1094-
self._check(idx, lambda x: x + 2, expected)
1095-
self._check(idx, lambda x: 2 + x, expected)
1096-
self._check(idx, lambda x: np.add(x, 2), expected)
10971077

1098-
self._check(idx + 2, lambda x: x - 2, idx)
1099-
self._check(idx + 2, lambda x: np.subtract(x, 2), idx)
1078+
self._check(idx, lambda x: x + 2, expected)
1079+
self._check(idx, lambda x: 2 + x, expected)
1080+
self._check(idx, lambda x: np.add(x, 2), expected)
1081+
1082+
self._check(idx + 2, lambda x: x - 2, idx)
1083+
self._check(idx + 2, lambda x: np.subtract(x, 2), idx)
11001084

11011085
# freq with mult
11021086
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
11031087
freq='2M', name='idx')
11041088
expected = PeriodIndex(['2011-07', '2011-08', 'NaT', '2011-10'],
11051089
freq='2M', name='idx')
1106-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1107-
clear=[pd.core.arrays.datetimelike]):
1108-
self._check(idx, lambda x: x + 3, expected)
1109-
self._check(idx, lambda x: 3 + x, expected)
1110-
self._check(idx, lambda x: np.add(x, 3), expected)
11111090

1112-
self._check(idx + 3, lambda x: x - 3, idx)
1113-
self._check(idx + 3, lambda x: np.subtract(x, 3), idx)
1091+
self._check(idx, lambda x: x + 3, expected)
1092+
self._check(idx, lambda x: 3 + x, expected)
1093+
self._check(idx, lambda x: np.add(x, 3), expected)
1094+
1095+
self._check(idx + 3, lambda x: x - 3, idx)
1096+
self._check(idx + 3, lambda x: np.subtract(x, 3), idx)
11141097

11151098
def test_pi_ops_array_int(self):
1116-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1117-
clear=[pd.core.arrays.datetimelike]):
1118-
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
1119-
freq='M', name='idx')
1120-
f = lambda x: x + np.array([1, 2, 3, 4])
1121-
exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'],
1122-
freq='M', name='idx')
1123-
self._check(idx, f, exp)
1124-
1125-
f = lambda x: np.add(x, np.array([4, -1, 1, 2]))
1126-
exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'],
1127-
freq='M', name='idx')
1128-
self._check(idx, f, exp)
1129-
1130-
f = lambda x: x - np.array([1, 2, 3, 4])
1131-
exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'],
1132-
freq='M', name='idx')
1133-
self._check(idx, f, exp)
1134-
1135-
f = lambda x: np.subtract(x, np.array([3, 2, 3, -2]))
1136-
exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'],
1137-
freq='M', name='idx')
1138-
self._check(idx, f, exp)
1099+
1100+
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
1101+
freq='M', name='idx')
1102+
f = lambda x: x + np.array([1, 2, 3, 4])
1103+
exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'],
1104+
freq='M', name='idx')
1105+
self._check(idx, f, exp)
1106+
1107+
f = lambda x: np.add(x, np.array([4, -1, 1, 2]))
1108+
exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'],
1109+
freq='M', name='idx')
1110+
self._check(idx, f, exp)
1111+
1112+
f = lambda x: x - np.array([1, 2, 3, 4])
1113+
exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'],
1114+
freq='M', name='idx')
1115+
self._check(idx, f, exp)
1116+
1117+
f = lambda x: np.subtract(x, np.array([3, 2, 3, -2]))
1118+
exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'],
1119+
freq='M', name='idx')
1120+
self._check(idx, f, exp)
11391121

11401122
def test_pi_ops_offset(self):
11411123
idx = PeriodIndex(['2011-01-01', '2011-02-01', '2011-03-01',

pandas/tests/indexes/period/test_period.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,9 @@ def test_is_(self):
339339
assert not index.is_(index[:])
340340
assert not index.is_(index.asfreq('M'))
341341
assert not index.is_(index.asfreq('A'))
342-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
343-
# GH#22535
344-
assert not index.is_(index - 2)
345-
assert not index.is_(index - 0)
342+
343+
assert not index.is_(index - 2)
344+
assert not index.is_(index - 0)
346345

347346
def test_contains(self):
348347
rng = period_range('2007-01', freq='M', periods=10)

pandas/tests/scalar/period/test_asfreq.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from pandas.errors import OutOfBoundsDatetime
66

77
from pandas import Period, offsets
8-
from pandas.util import testing as tm
98

109

1110
class TestFreqConversion(object):
@@ -16,17 +15,15 @@ def test_asfreq_near_zero(self, freq):
1615
per = Period('0001-01-01', freq=freq)
1716
tup1 = (per.year, per.hour, per.day)
1817

19-
with tm.assert_produces_warning(FutureWarning):
20-
prev = per - 1
18+
prev = per - 1
2119
assert prev.ordinal == per.ordinal - 1
2220
tup2 = (prev.year, prev.month, prev.day)
2321
assert tup2 < tup1
2422

2523
def test_asfreq_near_zero_weekly(self):
2624
# GH#19834
27-
with tm.assert_produces_warning(FutureWarning):
28-
per1 = Period('0001-01-01', 'D') + 6
29-
per2 = Period('0001-01-01', 'D') - 6
25+
per1 = Period('0001-01-01', 'D') + 6
26+
per2 = Period('0001-01-01', 'D') - 6
3027
week1 = per1.asfreq('W')
3128
week2 = per2.asfreq('W')
3229
assert week1 != week2

0 commit comments

Comments
 (0)