Skip to content

Commit e88ad28

Browse files
API/DEPR: Remove +/- as setops for DatetimeIndex/PeriodIndex (GH9630) (#14164)
API/DEPR: Remove +/- as setops for DatetimeIndex/PeriodIndex (GH9630) xref #13777, deprecations put in place in #9630
1 parent 844d5fb commit e88ad28

File tree

4 files changed

+165
-95
lines changed

4 files changed

+165
-95
lines changed

Diff for: doc/source/whatsnew/v0.19.0.txt

+22-3
Original file line numberDiff line numberDiff line change
@@ -932,14 +932,16 @@ New Behavior:
932932
Index ``+`` / ``-`` no longer used for set operations
933933
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
934934

935-
Addition and subtraction of the base Index type (not the numeric subclasses)
935+
Addition and subtraction of the base Index type and of DatetimeIndex
936+
(not the numeric index types)
936937
previously performed set operations (set union and difference). This
937938
behaviour was already deprecated since 0.15.0 (in favor using the specific
938939
``.union()`` and ``.difference()`` methods), and is now disabled. When
939940
possible, ``+`` and ``-`` are now used for element-wise operations, for
940-
example for concatenating strings (:issue:`8227`, :issue:`14127`).
941+
example for concatenating strings or subtracting datetimes
942+
(:issue:`8227`, :issue:`14127`).
941943

942-
Previous Behavior:
944+
Previous behavior:
943945

944946
.. code-block:: ipython
945947

@@ -962,6 +964,23 @@ For example, the behaviour of adding two integer Indexes:
962964

963965
is unchanged. The base ``Index`` is now made consistent with this behaviour.
964966

967+
Further, because of this change, it is now possible to subtract two
968+
DatetimeIndex objects resulting in a TimedeltaIndex:
969+
970+
Previous behavior:
971+
972+
.. code-block:: ipython
973+
974+
In [1]: pd.DatetimeIndex(['2016-01-01', '2016-01-02']) - pd.DatetimeIndex(['2016-01-02', '2016-01-03'])
975+
FutureWarning: using '-' to provide set differences with datetimelike Indexes is deprecated, use .difference()
976+
Out[1]: DatetimeIndex(['2016-01-01'], dtype='datetime64[ns]', freq=None)
977+
978+
New behavior:
979+
980+
.. ipython:: python
981+
982+
pd.DatetimeIndex(['2016-01-01', '2016-01-02']) - pd.DatetimeIndex(['2016-01-02', '2016-01-03'])
983+
965984

966985
.. _whatsnew_0190.api.difference:
967986

Diff for: pandas/tseries/base.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Base and utility classes for tseries type pandas objects.
33
"""
44

5-
import warnings
65
from datetime import datetime, timedelta
76

87
from pandas import compat
@@ -628,10 +627,9 @@ def __add__(self, other):
628627
raise TypeError("cannot add TimedeltaIndex and {typ}"
629628
.format(typ=type(other)))
630629
elif isinstance(other, Index):
631-
warnings.warn("using '+' to provide set union with "
632-
"datetimelike Indexes is deprecated, "
633-
"use .union()", FutureWarning, stacklevel=2)
634-
return self.union(other)
630+
raise TypeError("cannot add {typ1} and {typ2}"
631+
.format(typ1=type(self).__name__,
632+
typ2=type(other).__name__))
635633
elif isinstance(other, (DateOffset, timedelta, np.timedelta64,
636634
tslib.Timedelta)):
637635
return self._add_delta(other)
@@ -646,20 +644,22 @@ def __add__(self, other):
646644

647645
def __sub__(self, other):
648646
from pandas.core.index import Index
647+
from pandas.tseries.index import DatetimeIndex
649648
from pandas.tseries.tdi import TimedeltaIndex
650649
from pandas.tseries.offsets import DateOffset
651650
if isinstance(other, TimedeltaIndex):
652651
return self._add_delta(-other)
653652
elif isinstance(self, TimedeltaIndex) and isinstance(other, Index):
654653
if not isinstance(other, TimedeltaIndex):
655654
raise TypeError("cannot subtract TimedeltaIndex and {typ}"
656-
.format(typ=type(other)))
655+
.format(typ=type(other).__name__))
657656
return self._add_delta(-other)
657+
elif isinstance(other, DatetimeIndex):
658+
return self._sub_datelike(other)
658659
elif isinstance(other, Index):
659-
warnings.warn("using '-' to provide set differences with "
660-
"datetimelike Indexes is deprecated, "
661-
"use .difference()", FutureWarning, stacklevel=2)
662-
return self.difference(other)
660+
raise TypeError("cannot subtract {typ1} and {typ2}"
661+
.format(typ1=type(self).__name__,
662+
typ2=type(other).__name__))
663663
elif isinstance(other, (DateOffset, timedelta, np.timedelta64,
664664
tslib.Timedelta)):
665665
return self._add_delta(-other)

Diff for: pandas/tseries/index.py

+34-10
Original file line numberDiff line numberDiff line change
@@ -731,19 +731,43 @@ def _add_datelike(self, other):
731731
def _sub_datelike(self, other):
732732
# subtract a datetime from myself, yielding a TimedeltaIndex
733733
from pandas import TimedeltaIndex
734-
other = Timestamp(other)
735-
if other is tslib.NaT:
736-
result = self._nat_new(box=False)
737-
# require tz compat
738-
elif not self._has_same_tz(other):
739-
raise TypeError("Timestamp subtraction must have the same "
740-
"timezones or no timezones")
734+
if isinstance(other, DatetimeIndex):
735+
# require tz compat
736+
if not self._has_same_tz(other):
737+
raise TypeError("DatetimeIndex subtraction must have the same "
738+
"timezones or no timezones")
739+
result = self._sub_datelike_dti(other)
740+
elif isinstance(other, (tslib.Timestamp, datetime)):
741+
other = Timestamp(other)
742+
if other is tslib.NaT:
743+
result = self._nat_new(box=False)
744+
# require tz compat
745+
elif not self._has_same_tz(other):
746+
raise TypeError("Timestamp subtraction must have the same "
747+
"timezones or no timezones")
748+
else:
749+
i8 = self.asi8
750+
result = i8 - other.value
751+
result = self._maybe_mask_results(result,
752+
fill_value=tslib.iNaT)
741753
else:
742-
i8 = self.asi8
743-
result = i8 - other.value
744-
result = self._maybe_mask_results(result, fill_value=tslib.iNaT)
754+
raise TypeError("cannot subtract DatetimeIndex and {typ}"
755+
.format(typ=type(other).__name__))
745756
return TimedeltaIndex(result, name=self.name, copy=False)
746757

758+
def _sub_datelike_dti(self, other):
759+
"""subtraction of two DatetimeIndexes"""
760+
if not len(self) == len(other):
761+
raise ValueError("cannot add indices of unequal length")
762+
763+
self_i8 = self.asi8
764+
other_i8 = other.asi8
765+
new_values = self_i8 - other_i8
766+
if self.hasnans or other.hasnans:
767+
mask = (self._isnan) | (other._isnan)
768+
new_values[mask] = tslib.iNaT
769+
return new_values.view('i8')
770+
747771
def _maybe_update_attributes(self, attrs):
748772
""" Update Index attributes (e.g. freq) depending on op """
749773
freq = attrs.get('freq', None)

0 commit comments

Comments
 (0)