Skip to content

Commit 88025e9

Browse files
committed
DEPR: Undeprecate period - integer addition
Closes pandas-dev#24305
1 parent c230f29 commit 88025e9

File tree

6 files changed

+91
-137
lines changed

6 files changed

+91
-137
lines changed

doc/source/whatsnew/v0.24.0.rst

+6-18
Original file line numberDiff line numberDiff line change
@@ -1149,25 +1149,19 @@ Deprecations
11491149

11501150
.. _whatsnew_0240.deprecations.datetimelike_int_ops:
11511151

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

11581158
This usage is now deprecated. Instead add or subtract integer multiples of
1159-
the object's ``freq`` attribute. The result of subtraction of :class:`Period`
1160-
objects will be agnostic of the multiplier of the objects' ``freq`` attribute
1161-
(:issue:`21939`, :issue:`23878`).
1159+
the object's ``freq`` attribute (:issue:`21939`, :issue:`23878`).
11621160

11631161
*Previous Behavior*:
11641162

11651163
.. code-block:: ipython
11661164
1167-
In [3]: per = pd.Period('2016Q1')
1168-
In [4]: per + 3
1169-
Out[4]: Period('2016Q4', 'Q-DEC')
1170-
11711165
In [5]: ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour())
11721166
In [6]: ts + 2
11731167
Out[6]: Timestamp('1994-05-06 14:15:16', freq='H')
@@ -1185,12 +1179,6 @@ objects will be agnostic of the multiplier of the objects' ``freq`` attribute
11851179
.. ipython:: python
11861180
:okwarning:
11871181
1188-
per = pd.Period('2016Q1')
1189-
per + 3
1190-
1191-
per = pd.Period('2016Q1')
1192-
per + 3 * per.freq
1193-
11941182
ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour())
11951183
ts + 2 * ts.freq
11961184

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
@@ -557,14 +557,10 @@ def test_pi_sub_offset_array(self, box):
557557
def test_pi_add_iadd_int(self, one):
558558
# Variants of `one` for #19012
559559
rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10)
560-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
561-
clear=[pd.core.arrays.datetimelike]):
562-
result = rng + one
560+
result = rng + one
563561
expected = pd.period_range('2000-01-01 10:00', freq='H', periods=10)
564562
tm.assert_index_equal(result, expected)
565-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
566-
clear=[pd.core.arrays.datetimelike]):
567-
rng += one
563+
rng += one
568564
tm.assert_index_equal(rng, expected)
569565

570566
def test_pi_sub_isub_int(self, one):
@@ -573,24 +569,18 @@ def test_pi_sub_isub_int(self, one):
573569
the integer 1, e.g. int, long, np.int64, np.uint8, ...
574570
"""
575571
rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10)
576-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
577-
clear=[pd.core.arrays.datetimelike]):
578-
result = rng - one
572+
result = rng - one
579573
expected = pd.period_range('2000-01-01 08:00', freq='H', periods=10)
580574
tm.assert_index_equal(result, expected)
581-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
582-
clear=[pd.core.arrays.datetimelike]):
583-
rng -= one
575+
rng -= one
584576
tm.assert_index_equal(rng, expected)
585577

586578
@pytest.mark.parametrize('five', [5, np.array(5, dtype=np.int64)])
587579
def test_pi_sub_intlike(self, five):
588580
rng = period_range('2007-01', periods=50)
589581

590-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
591-
clear=[pd.core.arrays.datetimelike]):
592-
result = rng - five
593-
exp = rng + (-five)
582+
result = rng - five
583+
exp = rng + (-five)
594584
tm.assert_index_equal(result, exp)
595585

596586
def test_pi_sub_isub_offset(self):
@@ -655,9 +645,8 @@ def test_pi_add_intarray(self, int_holder, op):
655645
# GH#19959
656646
pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')])
657647
other = int_holder([4, -1])
658-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
659-
clear=[pd.core.arrays.datetimelike]):
660-
result = op(pi, other)
648+
649+
result = op(pi, other)
661650
expected = pd.PeriodIndex([pd.Period('2016Q1'), pd.Period('NaT')])
662651
tm.assert_index_equal(result, expected)
663652

@@ -666,16 +655,13 @@ def test_pi_sub_intarray(self, int_holder):
666655
# GH#19959
667656
pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')])
668657
other = int_holder([4, -1])
669-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
670-
clear=[pd.core.arrays.datetimelike]):
671-
result = pi - other
658+
659+
result = pi - other
672660
expected = pd.PeriodIndex([pd.Period('2014Q1'), pd.Period('NaT')])
673661
tm.assert_index_equal(result, expected)
674662

675663
with pytest.raises(TypeError):
676-
with tm.assert_produces_warning(FutureWarning,
677-
check_stacklevel=False):
678-
other - pi
664+
other - pi
679665

680666
# ---------------------------------------------------------------
681667
# Timedelta-like (timedelta, timedelta64, Timedelta, Tick)
@@ -937,12 +923,11 @@ def test_pi_ops(self):
937923

938924
expected = PeriodIndex(['2011-03', '2011-04', '2011-05', '2011-06'],
939925
freq='M', name='idx')
940-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
941-
clear=[pd.core.arrays.datetimelike]):
942-
self._check(idx, lambda x: x + 2, expected)
943-
self._check(idx, lambda x: 2 + x, expected)
944926

945-
self._check(idx + 2, lambda x: x - 2, idx)
927+
self._check(idx, lambda x: x + 2, expected)
928+
self._check(idx, lambda x: 2 + x, expected)
929+
930+
self._check(idx + 2, lambda x: x - 2, idx)
946931

947932
result = idx - Period('2011-01', freq='M')
948933
off = idx.freq
@@ -987,53 +972,50 @@ def test_pi_ops_nat(self):
987972
freq='M', name='idx')
988973
expected = PeriodIndex(['2011-03', '2011-04', 'NaT', '2011-06'],
989974
freq='M', name='idx')
990-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
991-
clear=[pd.core.arrays.datetimelike]):
992-
self._check(idx, lambda x: x + 2, expected)
993-
self._check(idx, lambda x: 2 + x, expected)
994-
self._check(idx, lambda x: np.add(x, 2), expected)
995975

996-
self._check(idx + 2, lambda x: x - 2, idx)
997-
self._check(idx + 2, lambda x: np.subtract(x, 2), idx)
976+
self._check(idx, lambda x: x + 2, expected)
977+
self._check(idx, lambda x: 2 + x, expected)
978+
self._check(idx, lambda x: np.add(x, 2), expected)
979+
980+
self._check(idx + 2, lambda x: x - 2, idx)
981+
self._check(idx + 2, lambda x: np.subtract(x, 2), idx)
998982

999983
# freq with mult
1000984
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
1001985
freq='2M', name='idx')
1002986
expected = PeriodIndex(['2011-07', '2011-08', 'NaT', '2011-10'],
1003987
freq='2M', name='idx')
1004-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1005-
clear=[pd.core.arrays.datetimelike]):
1006-
self._check(idx, lambda x: x + 3, expected)
1007-
self._check(idx, lambda x: 3 + x, expected)
1008-
self._check(idx, lambda x: np.add(x, 3), expected)
1009988

1010-
self._check(idx + 3, lambda x: x - 3, idx)
1011-
self._check(idx + 3, lambda x: np.subtract(x, 3), idx)
989+
self._check(idx, lambda x: x + 3, expected)
990+
self._check(idx, lambda x: 3 + x, expected)
991+
self._check(idx, lambda x: np.add(x, 3), expected)
992+
993+
self._check(idx + 3, lambda x: x - 3, idx)
994+
self._check(idx + 3, lambda x: np.subtract(x, 3), idx)
1012995

1013996
def test_pi_ops_array_int(self):
1014-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False,
1015-
clear=[pd.core.arrays.datetimelike]):
1016-
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
1017-
freq='M', name='idx')
1018-
f = lambda x: x + np.array([1, 2, 3, 4])
1019-
exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'],
1020-
freq='M', name='idx')
1021-
self._check(idx, f, exp)
1022-
1023-
f = lambda x: np.add(x, np.array([4, -1, 1, 2]))
1024-
exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'],
1025-
freq='M', name='idx')
1026-
self._check(idx, f, exp)
1027-
1028-
f = lambda x: x - np.array([1, 2, 3, 4])
1029-
exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'],
1030-
freq='M', name='idx')
1031-
self._check(idx, f, exp)
1032-
1033-
f = lambda x: np.subtract(x, np.array([3, 2, 3, -2]))
1034-
exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'],
1035-
freq='M', name='idx')
1036-
self._check(idx, f, exp)
997+
998+
idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'],
999+
freq='M', name='idx')
1000+
f = lambda x: x + np.array([1, 2, 3, 4])
1001+
exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'],
1002+
freq='M', name='idx')
1003+
self._check(idx, f, exp)
1004+
1005+
f = lambda x: np.add(x, np.array([4, -1, 1, 2]))
1006+
exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'],
1007+
freq='M', name='idx')
1008+
self._check(idx, f, exp)
1009+
1010+
f = lambda x: x - np.array([1, 2, 3, 4])
1011+
exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'],
1012+
freq='M', name='idx')
1013+
self._check(idx, f, exp)
1014+
1015+
f = lambda x: np.subtract(x, np.array([3, 2, 3, -2]))
1016+
exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'],
1017+
freq='M', name='idx')
1018+
self._check(idx, f, exp)
10371019

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

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)