From 0889d07fb94b3e5f0e2aa8f8a565c5fa894107f0 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 31 Oct 2018 17:50:32 -0700 Subject: [PATCH] DEPR: deprecate integer add/sub with DTI/TDI/PI/Timestamp/Period (#22535) --- doc/source/whatsnew/v0.24.0.txt | 50 ++++++++ pandas/_libs/tslibs/period.pyx | 11 +- pandas/_libs/tslibs/timestamps.pyx | 16 ++- pandas/core/arrays/datetimelike.py | 6 + pandas/core/arrays/period.py | 7 +- pandas/core/resample.py | 11 +- pandas/plotting/_converter.py | 8 +- pandas/tests/arithmetic/test_datetime64.py | 72 +++++++---- pandas/tests/arithmetic/test_period.py | 112 +++++++++++------- pandas/tests/frame/test_timeseries.py | 4 +- pandas/tests/indexes/datetimelike.py | 7 +- .../indexes/datetimes/test_arithmetic.py | 10 +- pandas/tests/indexes/period/test_period.py | 6 +- .../indexes/timedeltas/test_arithmetic.py | 46 ++++--- pandas/tests/indexing/test_partial.py | 13 +- pandas/tests/scalar/period/test_asfreq.py | 10 +- pandas/tests/scalar/period/test_period.py | 65 ++++++---- .../tests/scalar/timestamp/test_arithmetic.py | 14 ++- pandas/tests/test_resample.py | 7 +- pandas/tests/tseries/offsets/test_offsets.py | 58 +++++++-- pandas/tseries/offsets.py | 41 +++++-- 21 files changed, 406 insertions(+), 168 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 11f850a026caa3..9f1d3574257faa 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -956,6 +956,56 @@ Deprecations - :meth:`Timestamp.tz_localize`, :meth:`DatetimeIndex.tz_localize`, and :meth:`Series.tz_localize` have deprecated the ``errors`` argument in favor of the ``nonexistent`` argument (:issue:`8917`) - The class ``FrozenNDArray`` has been deprecated. When unpickling, ``FrozenNDArray`` will be unpickled to ``np.ndarray`` once this class is removed (:issue:`9031`) +.. _whatsnew_0240.deprecations.datetimelike_int_ops: + +Integer Addition/Subtraction with Datetime-like Classes Is Deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In the past, users could add or subtract integers or integer-dtypes arrays +from :class:`Period`, :class:`PeriodIndex`, and in some cases +:class:`Timestamp`, :class:`DatetimeIndex` and :class:`TimedeltaIndex`. + +This usage is now deprecated. Instead add or subtract integer multiples of +the object's ``freq`` attribute (:issue:`21939`) + +Previous Behavior: + +.. code-block:: ipython + + In [3]: per = pd.Period('2016Q1') + In [4]: per + 3 + Out[4]: Period('2016Q4', 'Q-DEC') + + In [5]: ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour()) + In [6]: ts + 2 + Out[6]: Timestamp('1994-05-06 14:15:16', freq='H') + + In [7]: tdi = pd.timedelta_range('1D', periods=2) + In [8]: tdi - np.array([2, 1]) + Out[8]: TimedeltaIndex(['-1 days', '1 days'], dtype='timedelta64[ns]', freq=None) + + In [9]: dti = pd.date_range('2001-01-01', periods=2, freq='7D') + In [10]: dti + pd.Index([1, 2]) + Out[10]: DatetimeIndex(['2001-01-08', '2001-01-22'], dtype='datetime64[ns]', freq=None) + +Current Behavior: + +.. ipython:: python + :okwarning: + per = pd.Period('2016Q1') + per + 3 + + per = pd.Period('2016Q1') + per + 3 * per.freq + + ts = pd.Timestamp('1994-05-06 12:15:16', freq=pd.offsets.Hour()) + ts + 2 * ts.freq + + tdi = pd.timedelta_range('1D', periods=2) + tdi - np.array([2 * tdi.freq, 1 * tdi.freq]) + + dti = pd.date_range('2001-01-01', periods=2, freq='7D') + dti + pd.Index([1 * dti.freq, 2 * dti.freq]) + .. _whatsnew_0240.prior_deprecations: Removal of prior version deprecations/changes diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 43dc415bfd4640..73fccb9125a85f 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -33,7 +33,7 @@ cdef extern from "src/datetime/np_datetime.h": cimport util from util cimport is_period_object, is_string_object -from timestamps import Timestamp +from timestamps import Timestamp, maybe_integer_op_deprecated from timezones cimport is_utc, is_tzlocal, get_dst_info from timedeltas import Timedelta from timedeltas cimport delta_to_nanoseconds @@ -1645,6 +1645,8 @@ cdef class _Period(object): elif other is NaT: return NaT elif util.is_integer_object(other): + maybe_integer_op_deprecated(self) + ordinal = self.ordinal + other * self.freq.n return Period(ordinal=ordinal, freq=self.freq) elif (PyDateTime_Check(other) or @@ -1671,6 +1673,8 @@ cdef class _Period(object): neg_other = -other return self + neg_other elif util.is_integer_object(other): + maybe_integer_op_deprecated(self) + ordinal = self.ordinal - other * self.freq.n return Period(ordinal=ordinal, freq=self.freq) elif is_period_object(other): @@ -1756,7 +1760,7 @@ cdef class _Period(object): def end_time(self): # freq.n can't be negative or 0 # ordinal = (self + self.freq.n).start_time.value - 1 - ordinal = (self + 1).start_time.value - 1 + ordinal = (self + self.freq).start_time.value - 1 return Timestamp(ordinal) def to_timestamp(self, freq=None, how='start', tz=None): @@ -1783,7 +1787,8 @@ cdef class _Period(object): end = how == 'E' if end: - return (self + 1).to_timestamp(how='start') - Timedelta(1, 'ns') + endpoint = (self + self.freq).to_timestamp(how='start') + return endpoint - Timedelta(1, 'ns') if freq is None: base, mult = get_freq_code(self.freq) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 08b0c5472549ea..93fa99ce1bd872 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -40,8 +40,19 @@ from timezones cimport ( _zero_time = datetime_time(0, 0) _no_input = object() + # ---------------------------------------------------------------------- +def maybe_integer_op_deprecated(obj): + # GH#22535 add/sub of integers and int-arrays is deprecated + if obj.freq is not None: + warnings.warn("Addition/subtraction of integers and integer-arrays " + "to {cls} is deprecated, will be removed in a future " + "version. Instead of adding/subtracting `n`, use " + "`n * self.freq`" + .format(cls=type(obj).__name__), + FutureWarning) + cdef inline object create_timestamp_from_ts(int64_t value, npy_datetimestruct dts, @@ -315,7 +326,8 @@ cdef class _Timestamp(datetime): return np.datetime64(self.value, 'ns') def __add__(self, other): - cdef int64_t other_int, nanos + cdef: + int64_t other_int, nanos if is_timedelta64_object(other): other_int = other.astype('timedelta64[ns]').view('i8') @@ -323,6 +335,8 @@ cdef class _Timestamp(datetime): tz=self.tzinfo, freq=self.freq) elif is_integer_object(other): + maybe_integer_op_deprecated(self) + if self is NaT: # to be compat with Period return NaT diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index dc7cf51ca109d0..2266c7be53523f 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -8,6 +8,7 @@ from pandas._libs import lib, iNaT, NaT from pandas._libs.tslibs import timezones from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds, Timedelta +from pandas._libs.tslibs.timestamps import maybe_integer_op_deprecated from pandas._libs.tslibs.period import ( Period, DIFFERENT_FREQ_INDEX, IncompatibleFrequency) @@ -634,6 +635,7 @@ def __add__(self, other): elif lib.is_integer(other): # This check must come after the check for np.timedelta64 # as is_integer returns True for these + maybe_integer_op_deprecated(self) result = self._time_shift(other) # array-like others @@ -647,6 +649,7 @@ def __add__(self, other): # DatetimeIndex, ndarray[datetime64] return self._add_datetime_arraylike(other) elif is_integer_dtype(other): + maybe_integer_op_deprecated(self) result = self._addsub_int_array(other, operator.add) elif is_float_dtype(other): # Explicitly catch invalid dtypes @@ -692,7 +695,9 @@ def __sub__(self, other): elif lib.is_integer(other): # This check must come after the check for np.timedelta64 # as is_integer returns True for these + maybe_integer_op_deprecated(self) result = self._time_shift(-other) + elif isinstance(other, Period): result = self._sub_period(other) @@ -710,6 +715,7 @@ def __sub__(self, other): # PeriodIndex result = self._sub_period_array(other) elif is_integer_dtype(other): + maybe_integer_op_deprecated(self) result = self._addsub_int_array(other, operator.sub) elif isinstance(other, ABCIndexClass): raise TypeError("cannot subtract {cls} and {typ}" diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 31bcac2f4f529b..1bbad4b73953df 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -592,7 +592,7 @@ def to_timestamp(self, freq=None, how='start'): return self.to_timestamp(how='start') + adjust else: adjust = Timedelta(1, 'ns') - return (self + 1).to_timestamp(how='start') - adjust + return (self + self.freq).to_timestamp(how='start') - adjust if freq is None: base, mult = frequencies.get_freq_code(self.freq) @@ -718,10 +718,11 @@ def _sub_period(self, other): @Appender(dtl.DatetimeLikeArrayMixin._addsub_int_array.__doc__) def _addsub_int_array( self, - other, # type: Union[Index, ExtensionArray, np.ndarray[int]] - op, # type: Callable[Any, Any] + other, # type: Union[Index, ExtensionArray, np.ndarray[int]] + op # type: Callable[Any, Any] ): # type: (...) -> PeriodArray + assert op in [operator.add, operator.sub] if op is operator.sub: other = -other diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 70a8deb33b7f27..36476a8ecb6578 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1429,7 +1429,7 @@ def _get_time_delta_bins(self, ax): freq=self.freq, name=ax.name) - end_stamps = labels + 1 + end_stamps = labels + self.freq bins = ax.searchsorted(end_stamps, side='left') # Addresses GH #10530 @@ -1443,17 +1443,18 @@ def _get_time_period_bins(self, ax): raise TypeError('axis must be a DatetimeIndex, but got ' 'an instance of %r' % type(ax).__name__) + freq = self.freq + if not len(ax): - binner = labels = PeriodIndex( - data=[], freq=self.freq, name=ax.name) + binner = labels = PeriodIndex(data=[], freq=freq, name=ax.name) return binner, [], labels labels = binner = PeriodIndex(start=ax[0], end=ax[-1], - freq=self.freq, + freq=freq, name=ax.name) - end_stamps = (labels + 1).asfreq(self.freq, 's').to_timestamp() + end_stamps = (labels + freq).asfreq(freq, 's').to_timestamp() if ax.tzinfo: end_stamps = end_stamps.tz_localize(ax.tzinfo) bins = ax.searchsorted(end_stamps, side='left') diff --git a/pandas/plotting/_converter.py b/pandas/plotting/_converter.py index fe773a6054db52..444b742ae706e4 100644 --- a/pandas/plotting/_converter.py +++ b/pandas/plotting/_converter.py @@ -574,7 +574,7 @@ def period_break(dates, period): Name of the period to monitor. """ current = getattr(dates, period) - previous = getattr(dates - 1, period) + previous = getattr(dates - 1 * dates.freq, period) return np.nonzero(current - previous)[0] @@ -660,7 +660,7 @@ def first_label(label_flags): def _hour_finder(label_interval, force_year_start): _hour = dates_.hour - _prev_hour = (dates_ - 1).hour + _prev_hour = (dates_ - 1 * dates_.freq).hour hour_start = (_hour - _prev_hour) != 0 info_maj[day_start] = True info_min[hour_start & (_hour % label_interval == 0)] = True @@ -674,7 +674,7 @@ def _hour_finder(label_interval, force_year_start): def _minute_finder(label_interval): hour_start = period_break(dates_, 'hour') _minute = dates_.minute - _prev_minute = (dates_ - 1).minute + _prev_minute = (dates_ - 1 * dates_.freq).minute minute_start = (_minute - _prev_minute) != 0 info_maj[hour_start] = True info_min[minute_start & (_minute % label_interval == 0)] = True @@ -687,7 +687,7 @@ def _minute_finder(label_interval): def _second_finder(label_interval): minute_start = period_break(dates_, 'minute') _second = dates_.second - _prev_second = (dates_ - 1).second + _prev_second = (dates_ - 1 * dates_.freq).second second_start = (_second - _prev_second) != 0 info['maj'][minute_start] = True info['min'][second_start & (_second % label_interval == 0)] = True diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 5435ec643f813a..b71ad08cb523e8 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1055,7 +1055,8 @@ def test_dti_add_int(self, tz_naive_fixture, one): tz = tz_naive_fixture rng = pd.date_range('2000-01-01 09:00', freq='H', periods=10, tz=tz) - result = rng + one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + result = rng + one expected = pd.date_range('2000-01-01 10:00', freq='H', periods=10, tz=tz) tm.assert_index_equal(result, expected) @@ -1066,14 +1067,16 @@ def test_dti_iadd_int(self, tz_naive_fixture, one): periods=10, tz=tz) expected = pd.date_range('2000-01-01 10:00', freq='H', periods=10, tz=tz) - rng += one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + rng += one tm.assert_index_equal(rng, expected) def test_dti_sub_int(self, tz_naive_fixture, one): tz = tz_naive_fixture rng = pd.date_range('2000-01-01 09:00', freq='H', periods=10, tz=tz) - result = rng - one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + result = rng - one expected = pd.date_range('2000-01-01 08:00', freq='H', periods=10, tz=tz) tm.assert_index_equal(result, expected) @@ -1084,7 +1087,8 @@ def test_dti_isub_int(self, tz_naive_fixture, one): periods=10, tz=tz) expected = pd.date_range('2000-01-01 08:00', freq='H', periods=10, tz=tz) - rng -= one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + rng -= one tm.assert_index_equal(rng, expected) # ------------------------------------------------------------- @@ -1096,10 +1100,15 @@ def test_dti_add_intarray_tick(self, box, freq): # GH#19959 dti = pd.date_range('2016-01-01', periods=2, freq=freq) other = box([4, -1]) - expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))]) - result = dti + other + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + expected = DatetimeIndex([dti[n] + other[n] + for n in range(len(dti))]) + result = dti + other tm.assert_index_equal(result, expected) - result = other + dti + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + result = other + dti tm.assert_index_equal(result, expected) @pytest.mark.parametrize('freq', ['W', 'M', 'MS', 'Q']) @@ -1108,11 +1117,21 @@ def test_dti_add_intarray_non_tick(self, box, freq): # GH#19959 dti = pd.date_range('2016-01-01', periods=2, freq=freq) other = box([4, -1]) - expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))]) - with tm.assert_produces_warning(PerformanceWarning): + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + expected = DatetimeIndex([dti[n] + other[n] + for n in range(len(dti))]) + + # tm.assert_produces_warning does not handle cases where we expect + # two warnings, in this case PerformanceWarning and FutureWarning. + # Until that is fixed, we don't catch either + with warnings.catch_warnings(): + warnings.simplefilter("ignore") result = dti + other tm.assert_index_equal(result, expected) - with tm.assert_produces_warning(PerformanceWarning): + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") result = other + dti tm.assert_index_equal(result, expected) @@ -1646,13 +1665,15 @@ def test_dti_add_offset_array(self, tz_naive_fixture): dti = pd.date_range('2017-01-01', periods=2, tz=tz) other = np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res = dti + other expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))], name=dti.name, freq='infer') tm.assert_index_equal(res, expected) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res2 = other + dti tm.assert_index_equal(res2, expected) @@ -1666,13 +1687,15 @@ def test_dti_add_offset_index(self, tz_naive_fixture, names): other = pd.Index([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)], name=names[1]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res = dti + other expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))], name=names[2], freq='infer') tm.assert_index_equal(res, expected) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res2 = other + dti tm.assert_index_equal(res2, expected) @@ -1682,7 +1705,8 @@ def test_dti_sub_offset_array(self, tz_naive_fixture): dti = pd.date_range('2017-01-01', periods=2, tz=tz) other = np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res = dti - other expected = DatetimeIndex([dti[n] - other[n] for n in range(len(dti))], name=dti.name, freq='infer') @@ -1698,7 +1722,8 @@ def test_dti_sub_offset_index(self, tz_naive_fixture, names): other = pd.Index([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)], name=names[1]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res = dti - other expected = DatetimeIndex([dti[n] - other[n] for n in range(len(dti))], name=names[2], freq='infer') @@ -1717,18 +1742,21 @@ def test_dti_with_offset_series(self, tz_naive_fixture, names): expected_add = Series([dti[n] + other[n] for n in range(len(dti))], name=names[2]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res = dti + other tm.assert_series_equal(res, expected_add) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res2 = other + dti tm.assert_series_equal(res2, expected_add) expected_sub = Series([dti[n] - other[n] for n in range(len(dti))], name=names[2]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): res3 = dti - other tm.assert_series_equal(res3, expected_sub) @@ -1762,11 +1790,11 @@ def test_dt64_with_offset_array(klass): # GH#10699 # array of offsets box = Series if klass is Series else pd.Index - dti = DatetimeIndex([Timestamp('2000-1-1'), Timestamp('2000-2-1')]) - s = klass(dti) + s = klass([Timestamp('2000-1-1'), Timestamp('2000-2-1')]) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(PerformanceWarning, + clear=[pd.core.arrays.datetimelike]): result = s + box([pd.offsets.DateOffset(years=1), pd.offsets.MonthEnd()]) exp = klass([Timestamp('2001-1-1'), Timestamp('2000-2-29')]) diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index 184e76cfa490f5..d2d725b6dc5956 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -509,10 +509,14 @@ def test_pi_sub_offset_array(self, box): def test_pi_add_iadd_int(self, one): # Variants of `one` for #19012 rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10) - result = rng + one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + result = rng + one expected = pd.period_range('2000-01-01 10:00', freq='H', periods=10) tm.assert_index_equal(result, expected) - rng += one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + rng += one tm.assert_index_equal(rng, expected) def test_pi_sub_isub_int(self, one): @@ -521,18 +525,24 @@ def test_pi_sub_isub_int(self, one): the integer 1, e.g. int, long, np.int64, np.uint8, ... """ rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10) - result = rng - one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + result = rng - one expected = pd.period_range('2000-01-01 08:00', freq='H', periods=10) tm.assert_index_equal(result, expected) - rng -= one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + rng -= one tm.assert_index_equal(rng, expected) @pytest.mark.parametrize('five', [5, np.array(5, dtype=np.int64)]) def test_pi_sub_intlike(self, five): rng = period_range('2007-01', periods=50) - result = rng - five - exp = rng + (-five) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + result = rng - five + exp = rng + (-five) tm.assert_index_equal(result, exp) def test_pi_sub_isub_offset(self): @@ -594,7 +604,9 @@ def test_pi_add_intarray(self, box, op): # GH#19959 pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')]) other = box([4, -1]) - result = op(pi, other) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + result = op(pi, other) expected = pd.PeriodIndex([pd.Period('2016Q1'), pd.Period('NaT')]) tm.assert_index_equal(result, expected) @@ -603,12 +615,16 @@ def test_pi_sub_intarray(self, box): # GH#19959 pi = pd.PeriodIndex([pd.Period('2015Q1'), pd.Period('NaT')]) other = box([4, -1]) - result = pi - other + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + result = pi - other expected = pd.PeriodIndex([pd.Period('2014Q1'), pd.Period('NaT')]) tm.assert_index_equal(result, expected) with pytest.raises(TypeError): - other - pi + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False): + other - pi # --------------------------------------------------------------- # Timedelta-like (timedelta, timedelta64, Timedelta, Tick) @@ -850,10 +866,13 @@ def test_pi_ops(self): expected = PeriodIndex(['2011-03', '2011-04', '2011-05', '2011-06'], freq='M', name='idx') - self._check(idx, lambda x: x + 2, expected) - self._check(idx, lambda x: 2 + x, expected) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + self._check(idx, lambda x: x + 2, expected) + self._check(idx, lambda x: 2 + x, expected) + + self._check(idx + 2, lambda x: x - 2, idx) - self._check(idx + 2, lambda x: x - 2, idx) result = idx - Period('2011-01', freq='M') off = idx.freq exp = pd.Index([0 * off, 1 * off, 2 * off, 3 * off], name='idx') @@ -870,7 +889,6 @@ def test_pi_ops_errors(self, ng, box): obj = tm.box_expected(idx, box) msg = r"unsupported operand type\(s\)" - with tm.assert_raises_regex(TypeError, msg): obj + ng @@ -898,47 +916,53 @@ def test_pi_ops_nat(self): freq='M', name='idx') expected = PeriodIndex(['2011-03', '2011-04', 'NaT', '2011-06'], freq='M', name='idx') - self._check(idx, lambda x: x + 2, expected) - self._check(idx, lambda x: 2 + x, expected) - self._check(idx, lambda x: np.add(x, 2), expected) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + self._check(idx, lambda x: x + 2, expected) + self._check(idx, lambda x: 2 + x, expected) + self._check(idx, lambda x: np.add(x, 2), expected) - self._check(idx + 2, lambda x: x - 2, idx) - self._check(idx + 2, lambda x: np.subtract(x, 2), idx) + self._check(idx + 2, lambda x: x - 2, idx) + self._check(idx + 2, lambda x: np.subtract(x, 2), idx) # freq with mult idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'], freq='2M', name='idx') expected = PeriodIndex(['2011-07', '2011-08', 'NaT', '2011-10'], freq='2M', name='idx') - self._check(idx, lambda x: x + 3, expected) - self._check(idx, lambda x: 3 + x, expected) - self._check(idx, lambda x: np.add(x, 3), expected) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + self._check(idx, lambda x: x + 3, expected) + self._check(idx, lambda x: 3 + x, expected) + self._check(idx, lambda x: np.add(x, 3), expected) - self._check(idx + 3, lambda x: x - 3, idx) - self._check(idx + 3, lambda x: np.subtract(x, 3), idx) + self._check(idx + 3, lambda x: x - 3, idx) + self._check(idx + 3, lambda x: np.subtract(x, 3), idx) def test_pi_ops_array_int(self): - idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'], - freq='M', name='idx') - f = lambda x: x + np.array([1, 2, 3, 4]) - exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'], - freq='M', name='idx') - self._check(idx, f, exp) - - f = lambda x: np.add(x, np.array([4, -1, 1, 2])) - exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'], - freq='M', name='idx') - self._check(idx, f, exp) - - f = lambda x: x - np.array([1, 2, 3, 4]) - exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'], - freq='M', name='idx') - self._check(idx, f, exp) - - f = lambda x: np.subtract(x, np.array([3, 2, 3, -2])) - exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'], - freq='M', name='idx') - self._check(idx, f, exp) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False, + clear=[pd.core.arrays.datetimelike]): + idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'], + freq='M', name='idx') + f = lambda x: x + np.array([1, 2, 3, 4]) + exp = PeriodIndex(['2011-02', '2011-04', 'NaT', '2011-08'], + freq='M', name='idx') + self._check(idx, f, exp) + + f = lambda x: np.add(x, np.array([4, -1, 1, 2])) + exp = PeriodIndex(['2011-05', '2011-01', 'NaT', '2011-06'], + freq='M', name='idx') + self._check(idx, f, exp) + + f = lambda x: x - np.array([1, 2, 3, 4]) + exp = PeriodIndex(['2010-12', '2010-12', 'NaT', '2010-12'], + freq='M', name='idx') + self._check(idx, f, exp) + + f = lambda x: np.subtract(x, np.array([3, 2, 3, -2])) + exp = PeriodIndex(['2010-10', '2010-12', 'NaT', '2011-06'], + freq='M', name='idx') + self._check(idx, f, exp) def test_pi_ops_offset(self): idx = PeriodIndex(['2011-01-01', '2011-02-01', '2011-03-01', diff --git a/pandas/tests/frame/test_timeseries.py b/pandas/tests/frame/test_timeseries.py index 40089c8e9e4775..eecbdc0130f02f 100644 --- a/pandas/tests/frame/test_timeseries.py +++ b/pandas/tests/frame/test_timeseries.py @@ -423,8 +423,8 @@ def test_truncate(self): assert_frame_equal(truncated, expected) pytest.raises(ValueError, ts.truncate, - before=ts.index[-1] - 1, - after=ts.index[0] + 1) + before=ts.index[-1] - ts.index.freq, + after=ts.index[0] + ts.index.freq) def test_truncate_copy(self): index = self.tsframe.index diff --git a/pandas/tests/indexes/datetimelike.py b/pandas/tests/indexes/datetimelike.py index bb51b47a7fd0a5..b798ac34255f12 100644 --- a/pandas/tests/indexes/datetimelike.py +++ b/pandas/tests/indexes/datetimelike.py @@ -61,9 +61,8 @@ def test_view(self, indices): tm.assert_index_equal(result, i_view) def test_map_callable(self): - - expected = self.index + 1 - result = self.index.map(lambda x: x + 1) + expected = self.index + self.index.freq + result = self.index.map(lambda x: x + x.freq) tm.assert_index_equal(result, expected) # map to NaT @@ -77,7 +76,7 @@ def test_map_callable(self): lambda values, index: {i: e for e, i in zip(values, index)}, lambda values, index: pd.Series(values, index)]) def test_map_dictlike(self, mapper): - expected = self.index + 1 + expected = self.index + self.index.freq # don't compare the freqs if isinstance(expected, pd.DatetimeIndex): diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index de51120baeb582..1b75d6bd34764b 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -58,11 +58,17 @@ def test_dti_shift_freqs(self): def test_dti_shift_int(self): rng = date_range('1/1/2000', periods=20) - result = rng + 5 + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng + 5 + expected = rng.shift(5) tm.assert_index_equal(result, expected) - result = rng - 5 + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng - 5 + expected = rng.shift(-5) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 8b2e91450c8c0d..8d10cb8e42a943 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -338,8 +338,10 @@ def test_is_(self): assert not index.is_(index[:]) assert not index.is_(index.asfreq('M')) assert not index.is_(index.asfreq('A')) - assert not index.is_(index - 2) - assert not index.is_(index - 0) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + assert not index.is_(index - 2) + assert not index.is_(index - 0) def test_contains(self): rng = period_range('2007-01', freq='M', periods=10) diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index 82654a3533132c..a03698c9ea0de8 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -130,26 +130,34 @@ def test_ufunc_coercions(self): def test_tdi_add_int(self, one): # Variants of `one` for #19012 rng = timedelta_range('1 days 09:00:00', freq='H', periods=10) - result = rng + one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng + one expected = timedelta_range('1 days 10:00:00', freq='H', periods=10) tm.assert_index_equal(result, expected) def test_tdi_iadd_int(self, one): rng = timedelta_range('1 days 09:00:00', freq='H', periods=10) expected = timedelta_range('1 days 10:00:00', freq='H', periods=10) - rng += one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + rng += one tm.assert_index_equal(rng, expected) def test_tdi_sub_int(self, one): rng = timedelta_range('1 days 09:00:00', freq='H', periods=10) - result = rng - one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng - one expected = timedelta_range('1 days 08:00:00', freq='H', periods=10) tm.assert_index_equal(result, expected) def test_tdi_isub_int(self, one): rng = timedelta_range('1 days 09:00:00', freq='H', periods=10) expected = timedelta_range('1 days 08:00:00', freq='H', periods=10) - rng -= one + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + rng -= one tm.assert_index_equal(rng, expected) # ------------------------------------------------------------- @@ -161,10 +169,15 @@ def test_tdi_add_integer_array(self, box): rng = timedelta_range('1 days 09:00:00', freq='H', periods=3) other = box([4, 3, 2]) expected = TimedeltaIndex(['1 day 13:00:00'] * 3) - result = rng + other - tm.assert_index_equal(result, expected) - result = other + rng - tm.assert_index_equal(result, expected) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng + other + tm.assert_index_equal(result, expected) + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = other + rng + tm.assert_index_equal(result, expected) @pytest.mark.parametrize('box', [np.array, pd.Index]) def test_tdi_sub_integer_array(self, box): @@ -172,10 +185,15 @@ def test_tdi_sub_integer_array(self, box): rng = timedelta_range('9H', freq='H', periods=3) other = box([4, 3, 2]) expected = TimedeltaIndex(['5H', '7H', '9H']) - result = rng - other - tm.assert_index_equal(result, expected) - result = other - rng - tm.assert_index_equal(result, -expected) + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = rng - other + tm.assert_index_equal(result, expected) + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + # GH#22535 + result = other - rng + tm.assert_index_equal(result, -expected) @pytest.mark.parametrize('box', [np.array, pd.Index]) def test_tdi_addsub_integer_array_no_freq(self, box): @@ -522,12 +540,12 @@ def test_timedelta_ops_with_missing_values(self): def test_tdi_ops_attributes(self): rng = timedelta_range('2 days', periods=5, freq='2D', name='x') - result = rng + 1 + result = rng + 1 * rng.freq exp = timedelta_range('4 days', periods=5, freq='2D', name='x') tm.assert_index_equal(result, exp) assert result.freq == '2D' - result = rng - 2 + result = rng - 2 * rng.freq exp = timedelta_range('-2 days', periods=5, freq='2D', name='x') tm.assert_index_equal(result, exp) assert result.freq == '2D' diff --git a/pandas/tests/indexing/test_partial.py b/pandas/tests/indexing/test_partial.py index 0d596e713fcfc1..f27b556366d880 100644 --- a/pandas/tests/indexing/test_partial.py +++ b/pandas/tests/indexing/test_partial.py @@ -159,23 +159,24 @@ def f(): columns=['A', 'B', 'C', 'D']) expected = pd.concat([df_orig, - DataFrame({'A': 7}, index=[dates[-1] + 1])], + DataFrame({'A': 7}, + index=[dates[-1] + dates.freq])], sort=True) df = df_orig.copy() - df.loc[dates[-1] + 1, 'A'] = 7 + df.loc[dates[-1] + dates.freq, 'A'] = 7 tm.assert_frame_equal(df, expected) df = df_orig.copy() - df.at[dates[-1] + 1, 'A'] = 7 + df.at[dates[-1] + dates.freq, 'A'] = 7 tm.assert_frame_equal(df, expected) - exp_other = DataFrame({0: 7}, index=[dates[-1] + 1]) + exp_other = DataFrame({0: 7}, index=[dates[-1] + dates.freq]) expected = pd.concat([df_orig, exp_other], axis=1) df = df_orig.copy() - df.loc[dates[-1] + 1, 0] = 7 + df.loc[dates[-1] + dates.freq, 0] = 7 tm.assert_frame_equal(df, expected) df = df_orig.copy() - df.at[dates[-1] + 1, 0] = 7 + df.at[dates[-1] + dates.freq, 0] = 7 tm.assert_frame_equal(df, expected) def test_partial_setting_mixed_dtype(self): diff --git a/pandas/tests/scalar/period/test_asfreq.py b/pandas/tests/scalar/period/test_asfreq.py index 2e3867db65604f..432d55ef5967a9 100644 --- a/pandas/tests/scalar/period/test_asfreq.py +++ b/pandas/tests/scalar/period/test_asfreq.py @@ -16,15 +16,17 @@ def test_asfreq_near_zero(self, freq): per = Period('0001-01-01', freq=freq) tup1 = (per.year, per.hour, per.day) - prev = per - 1 - assert (per - 1).ordinal == per.ordinal - 1 + with tm.assert_produces_warning(FutureWarning): + prev = per - 1 + assert prev.ordinal == per.ordinal - 1 tup2 = (prev.year, prev.month, prev.day) assert tup2 < tup1 def test_asfreq_near_zero_weekly(self): # GH#19834 - per1 = Period('0001-01-01', 'D') + 6 - per2 = Period('0001-01-01', 'D') - 6 + with tm.assert_produces_warning(FutureWarning): + per1 = Period('0001-01-01', 'D') + 6 + per2 = Period('0001-01-01', 'D') - 6 week1 = per1.asfreq('W') week2 = per2.asfreq('W') assert week1 != week2 diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index e360500d443eab..7171b15acbfa1a 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -74,7 +74,9 @@ def test_period_cons_annual(self, month): exp = Period('1989', freq=freq) stamp = exp.to_timestamp('D', how='end') + timedelta(days=30) p = Period(stamp, freq=freq) - assert p == exp + 1 + + with tm.assert_produces_warning(FutureWarning): + assert p == exp + 1 assert isinstance(p, Period) @pytest.mark.parametrize('day', DAYS) @@ -127,13 +129,16 @@ def test_period_cons_mult(self): assert p2.freq == offsets.MonthEnd() assert p2.freqstr == 'M' - result = p1 + 1 - assert result.ordinal == (p2 + 3).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p1 + 1 + assert result.ordinal == (p2 + 3).ordinal + assert result.freq == p1.freq assert result.freqstr == '3M' - result = p1 - 1 - assert result.ordinal == (p2 - 3).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p1 - 1 + assert result.ordinal == (p2 - 3).ordinal assert result.freq == p1.freq assert result.freqstr == '3M' @@ -167,23 +172,27 @@ def test_period_cons_combined(self): assert p3.freq == offsets.Hour() assert p3.freqstr == 'H' - result = p1 + 1 - assert result.ordinal == (p3 + 25).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p1 + 1 + assert result.ordinal == (p3 + 25).ordinal assert result.freq == p1.freq assert result.freqstr == '25H' - result = p2 + 1 - assert result.ordinal == (p3 + 25).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p2 + 1 + assert result.ordinal == (p3 + 25).ordinal assert result.freq == p2.freq assert result.freqstr == '25H' - result = p1 - 1 - assert result.ordinal == (p3 - 25).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p1 - 1 + assert result.ordinal == (p3 - 25).ordinal assert result.freq == p1.freq assert result.freqstr == '25H' - result = p2 - 1 - assert result.ordinal == (p3 - 25).ordinal + with tm.assert_produces_warning(FutureWarning): + result = p2 - 1 + assert result.ordinal == (p3 - 25).ordinal assert result.freq == p2.freq assert result.freqstr == '25H' @@ -598,7 +607,7 @@ def test_to_timestamp(self): from_lst = ['A', 'Q', 'M', 'W', 'B', 'D', 'H', 'Min', 'S'] def _ex(p): - return Timestamp((p + 1).start_time.value - 1) + return Timestamp((p + p.freq).start_time.value - 1) for i, fcode in enumerate(from_lst): p = Period('1982', freq=fcode) @@ -717,14 +726,16 @@ def test_properties_quarterly(self): # for x in range(3): for qd in (qedec_date, qejan_date, qejun_date): - assert (qd + x).qyear == 2007 - assert (qd + x).quarter == x + 1 + with tm.assert_produces_warning(FutureWarning): + assert (qd + x).qyear == 2007 + assert (qd + x).quarter == x + 1 def test_properties_monthly(self): # Test properties on Periods with daily frequency. m_date = Period(freq='M', year=2007, month=1) for x in range(11): - m_ival_x = m_date + x + with tm.assert_produces_warning(FutureWarning): + m_ival_x = m_date + x assert m_ival_x.year == 2007 if 1 <= x + 1 <= 3: assert m_ival_x.quarter == 1 @@ -744,7 +755,8 @@ def test_properties_weekly(self): assert w_date.quarter == 1 assert w_date.month == 1 assert w_date.week == 1 - assert (w_date - 1).week == 52 + with tm.assert_produces_warning(FutureWarning): + assert (w_date - 1).week == 52 assert w_date.days_in_month == 31 assert Period(freq='W', year=2012, month=2, day=1).days_in_month == 29 @@ -756,7 +768,8 @@ def test_properties_weekly_legacy(self): assert w_date.quarter == 1 assert w_date.month == 1 assert w_date.week == 1 - assert (w_date - 1).week == 52 + with tm.assert_produces_warning(FutureWarning): + assert (w_date - 1).week == 52 assert w_date.days_in_month == 31 exp = Period(freq='W', year=2012, month=2, day=1) @@ -897,10 +910,11 @@ def test_multiples(self): assert result1.freq == offsets.YearEnd(2) assert result2.freq == offsets.YearEnd() - assert (result1 + 1).ordinal == result1.ordinal + 2 - assert (1 + result1).ordinal == result1.ordinal + 2 - assert (result1 - 1).ordinal == result2.ordinal - 2 - assert (-1 + result1).ordinal == result2.ordinal - 2 + with tm.assert_produces_warning(FutureWarning): + assert (result1 + 1).ordinal == result1.ordinal + 2 + assert (1 + result1).ordinal == result1.ordinal + 2 + assert (result1 - 1).ordinal == result2.ordinal - 2 + assert (-1 + result1).ordinal == result2.ordinal - 2 def test_round_trip(self): @@ -1006,8 +1020,9 @@ class TestMethods(object): def test_add(self): dt1 = Period(freq='D', year=2008, month=1, day=1) dt2 = Period(freq='D', year=2008, month=1, day=2) - assert dt1 + 1 == dt2 - assert 1 + dt1 == dt2 + with tm.assert_produces_warning(FutureWarning): + assert dt1 + 1 == dt2 + assert 1 + dt1 == dt2 def test_add_pdnat(self): p = pd.Period('2011-01', freq='M') diff --git a/pandas/tests/scalar/timestamp/test_arithmetic.py b/pandas/tests/scalar/timestamp/test_arithmetic.py index 8f4809c93e28b8..0f8ddc53734f66 100644 --- a/pandas/tests/scalar/timestamp/test_arithmetic.py +++ b/pandas/tests/scalar/timestamp/test_arithmetic.py @@ -4,6 +4,7 @@ import pytest import numpy as np +import pandas.util.testing as tm from pandas.compat import long from pandas.tseries import offsets from pandas import Timestamp, Timedelta @@ -46,8 +47,10 @@ def test_addition_subtraction_types(self): # addition/subtraction of integers ts = Timestamp(dt, freq='D') - assert type(ts + 1) == Timestamp - assert type(ts - 1) == Timestamp + with tm.assert_produces_warning(FutureWarning): + # GH#22535 add/sub with integers is deprecated + assert type(ts + 1) == Timestamp + assert type(ts - 1) == Timestamp # Timestamp + datetime not supported, though subtraction is supported # and yields timedelta more tests in tseries/base/tests/test_base.py @@ -66,8 +69,11 @@ def test_addition_subtraction_preserve_frequency(self): td = timedelta(days=1) original_freq = ts.freq - assert (ts + 1).freq == original_freq - assert (ts - 1).freq == original_freq + with tm.assert_produces_warning(FutureWarning): + # GH#22535 add/sub with integers is deprecated + assert (ts + 1).freq == original_freq + assert (ts - 1).freq == original_freq + assert (ts + td).freq == original_freq assert (ts - td).freq == original_freq diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index 5cd31e08e0a9b2..69a0613c95475c 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -2245,7 +2245,7 @@ def test_asfreq(self, series_and_frame, freq, kind): expected = obj.to_timestamp().resample(freq).asfreq() else: start = obj.index[0].to_timestamp(how='start') - end = (obj.index[-1] + 1).to_timestamp(how='start') + end = (obj.index[-1] + obj.index.freq).to_timestamp(how='start') new_index = date_range(start=start, end=end, freq=freq, closed='left') expected = obj.to_timestamp().reindex(new_index).to_period(freq) @@ -2467,7 +2467,8 @@ def test_with_local_timezone_pytz(self): # Create the expected series # Index is moved back a day with the timezone conversion from UTC to # Pacific - expected_index = (pd.period_range(start=start, end=end, freq='D') - 1) + expected_index = (pd.period_range(start=start, end=end, freq='D') - + offsets.Day()) expected = Series(1, index=expected_index) assert_series_equal(result, expected) @@ -2503,7 +2504,7 @@ def test_with_local_timezone_dateutil(self): # Index is moved back a day with the timezone conversion from UTC to # Pacific expected_index = (pd.period_range(start=start, end=end, freq='D', - name='idx') - 1) + name='idx') - offsets.Day()) expected = Series(1, index=expected_index) assert_series_equal(result, expected) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index a0cff6f74b9791..cbd3e0903b713e 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -2403,7 +2403,11 @@ def test_offset_whole_year(self): # ensure .apply_index works as expected s = DatetimeIndex(dates[:-1]) - result = SemiMonthEnd().apply_index(s) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = SemiMonthEnd().apply_index(s) + exp = DatetimeIndex(dates[1:]) tm.assert_index_equal(result, exp) @@ -2499,7 +2503,11 @@ def test_offset(self, case): def test_apply_index(self, case): offset, cases = case s = DatetimeIndex(cases.keys()) - result = offset.apply_index(s) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = offset.apply_index(s) + exp = DatetimeIndex(cases.values()) tm.assert_index_equal(result, exp) @@ -2519,8 +2527,12 @@ def test_vectorized_offset_addition(self, klass): s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'), Timestamp('2000-02-15', tz='US/Central')], name='a') - result = s + SemiMonthEnd() - result2 = SemiMonthEnd() + s + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthEnd() + result2 = SemiMonthEnd() + s + exp = klass([Timestamp('2000-01-31 00:15:00', tz='US/Central'), Timestamp('2000-02-29', tz='US/Central')], name='a') tm.assert_equal(result, exp) @@ -2528,8 +2540,13 @@ def test_vectorized_offset_addition(self, klass): s = klass([Timestamp('2000-01-01 00:15:00', tz='US/Central'), Timestamp('2000-02-01', tz='US/Central')], name='a') - result = s + SemiMonthEnd() - result2 = SemiMonthEnd() + s + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthEnd() + result2 = SemiMonthEnd() + s + exp = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'), Timestamp('2000-02-15', tz='US/Central')], name='a') tm.assert_equal(result, exp) @@ -2573,7 +2590,11 @@ def test_offset_whole_year(self): # ensure .apply_index works as expected s = DatetimeIndex(dates[:-1]) - result = SemiMonthBegin().apply_index(s) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = SemiMonthBegin().apply_index(s) + exp = DatetimeIndex(dates[1:]) tm.assert_index_equal(result, exp) @@ -2673,7 +2694,12 @@ def test_offset(self, case): def test_apply_index(self, case): offset, cases = case s = DatetimeIndex(cases.keys()) - result = offset.apply_index(s) + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = offset.apply_index(s) + exp = DatetimeIndex(cases.values()) tm.assert_index_equal(result, exp) @@ -2692,8 +2718,12 @@ def test_onOffset(self, case): def test_vectorized_offset_addition(self, klass): s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'), Timestamp('2000-02-15', tz='US/Central')], name='a') - result = s + SemiMonthBegin() - result2 = SemiMonthBegin() + s + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthBegin() + result2 = SemiMonthBegin() + s + exp = klass([Timestamp('2000-02-01 00:15:00', tz='US/Central'), Timestamp('2000-03-01', tz='US/Central')], name='a') tm.assert_equal(result, exp) @@ -2701,8 +2731,12 @@ def test_vectorized_offset_addition(self, klass): s = klass([Timestamp('2000-01-01 00:15:00', tz='US/Central'), Timestamp('2000-02-01', tz='US/Central')], name='a') - result = s + SemiMonthBegin() - result2 = SemiMonthBegin() + s + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthBegin() + result2 = SemiMonthBegin() + s + exp = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'), Timestamp('2000-02-15', tz='US/Central')], name='a') tm.assert_equal(result, exp) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 650c4d5b21d7f5..6fb562e301ac2e 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -288,8 +288,11 @@ def apply_index(self, i): weeks = (kwds.get('weeks', 0)) * self.n if weeks: - i = (i.to_period('W') + weeks).to_timestamp() + \ - i.to_perioddelta('W') + # integer addition on PeriodIndex is deprecated, + # so we directly use _time_shift instead + asper = i.to_period('W') + shifted = asper._data._time_shift(weeks) + i = shifted.to_timestamp() + i.to_perioddelta('W') timedelta_kwds = {k: v for k, v in kwds.items() if k in ['days', 'hours', 'minutes', @@ -536,13 +539,21 @@ def apply_index(self, i): time = i.to_perioddelta('D') # to_period rolls forward to next BDay; track and # reduce n where it does when rolling forward - shifted = (i.to_perioddelta('B') - time).asi8 != 0 + asper = i.to_period('B') if self.n > 0: + shifted = (i.to_perioddelta('B') - time).asi8 != 0 + + # Integer-array addition is deprecated, so we use + # _time_shift directly roll = np.where(shifted, self.n - 1, self.n) + shifted = asper._data._addsub_int_array(roll, operator.add) else: + # Integer addition is deprecated, so we use _time_shift directly roll = self.n + shifted = asper._data._time_shift(roll) - return (i.to_period('B') + roll).to_timestamp() + time + result = shifted.to_timestamp() + time + return result def onOffset(self, dt): if self.normalize and not _is_normalized(dt): @@ -1091,6 +1102,7 @@ def _apply(self, n, other): @apply_index_wraps def apply_index(self, i): # determine how many days away from the 1st of the month we are + dti = i days_from_start = i.to_perioddelta('M').asi8 delta = Timedelta(days=self.day_of_month - 1).value @@ -1107,7 +1119,12 @@ def apply_index(self, i): time = i.to_perioddelta('D') # apply the correct number of months - i = (i.to_period('M') + (roll // 2)).to_timestamp() + + # integer-array addition on PeriodIndex is deprecated, + # so we use _addsub_int_array directly + asper = i.to_period('M') + shifted = asper._data._addsub_int_array(roll // 2, operator.add) + i = type(dti)(shifted.to_timestamp()) # apply the correct day i = self._apply_index_days(i, roll) @@ -1288,8 +1305,10 @@ def apply(self, other): @apply_index_wraps def apply_index(self, i): if self.weekday is None: - return ((i.to_period('W') + self.n).to_timestamp() + - i.to_perioddelta('W')) + # integer addition on PeriodIndex is deprecated, + # so we use _time_shift directly + shifted = i.to_period('W')._data._time_shift(self.n) + return shifted.to_timestamp() + i.to_perioddelta('W') else: return self._end_apply_index(i) @@ -1314,10 +1333,16 @@ def _end_apply_index(self, dtindex): normed = dtindex - off + Timedelta(1, 'D') - Timedelta(1, 'ns') roll = np.where(base_period.to_timestamp(how='end') == normed, self.n, self.n - 1) + # integer-array addition on PeriodIndex is deprecated, + # so we use _addsub_int_array directly + shifted = base_period._data._addsub_int_array(roll, operator.add) + base = shifted.to_timestamp(how='end') else: + # integer addition on PeriodIndex is deprecated, + # so we use _time_shift directly roll = self.n + base = base_period._data._time_shift(roll).to_timestamp(how='end') - base = (base_period + roll).to_timestamp(how='end') return base + off + Timedelta(1, 'ns') - Timedelta(1, 'D') def onOffset(self, dt):