From cc01bd55e09d5a95fe6967bf671e9998965937a5 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 8 Jan 2018 09:44:14 -0800 Subject: [PATCH 1/3] Make TimedeltaIndex +/- pd.NaT return TimedeltaIndex --- doc/source/whatsnew/v0.23.0.txt | 1 + pandas/core/indexes/timedeltas.py | 10 ++++++---- pandas/tests/scalar/test_nat.py | 14 +++++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index d7a3f0d077302..e45b3e1ac8c20 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -270,6 +270,7 @@ Other API Changes - Subtraction of :class:`Series` with timezone-aware ``dtype='datetime64[ns]'`` with mis-matched timezones will raise ``TypeError`` instead of ``ValueError`` (issue:`18817`) - :class:`IntervalIndex` and ``IntervalDtype`` no longer support categorical, object, and string subtypes (:issue:`19016`) - The default ``Timedelta`` constructor now accepts an ``ISO 8601 Duration`` string as an argument (:issue:`19040`) +- Addition or subtraction of ``NaT`` from :class:`TimedeltaIndex` will return ``TimedeltaIndex`` instead of ``DatetimeIndex`` (:issue:`19124`) .. _whatsnew_0230.deprecations: diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 12ca26cfe0266..866329b16c830 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -403,7 +403,8 @@ def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if other is NaT: - result = self._nat_new(box=False) + # GH#19124 pd.NaT is treated like a timedelta + return self._nat_new() else: other = Timestamp(other) i8 = self.asi8 @@ -413,12 +414,13 @@ def _add_datelike(self, other): return DatetimeIndex(result, name=self.name, copy=False) def _sub_datelike(self, other): - from pandas import DatetimeIndex + # GH#19124 Timedelta - datetime is not in general well-defined. + # We make an exception for pd.NaT, which in this case quacks + # like a timedelta. if other is NaT: - result = self._nat_new(box=False) + return self._nat_new() else: raise TypeError("cannot subtract a datelike from a TimedeltaIndex") - return DatetimeIndex(result, name=self.name, copy=False) def _add_offset_array(self, other): # Array/Index of DateOffset objects diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index d0d204253e3f1..5f64ea399caef 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -302,14 +302,14 @@ def test_nat_arithmetic_index(): tm.assert_index_equal(left - right, exp) tm.assert_index_equal(right - left, exp) - # timedelta + # timedelta # GH#19124 tdi = TimedeltaIndex(['1 day', '2 day'], name='x') - exp = DatetimeIndex([NaT, NaT], name='x') - for (left, right) in [(NaT, tdi)]: - tm.assert_index_equal(left + right, exp) - tm.assert_index_equal(right + left, exp) - tm.assert_index_equal(left - right, exp) - tm.assert_index_equal(right - left, exp) + tdi_nat = TimedeltaIndex([NaT, NaT], name='x') + + tm.assert_index_equal(tdi + NaT, tdi_nat) + tm.assert_index_equal(NaT + tdi, tdi_nat) + tm.assert_index_equal(tdi - NaT, tdi_nat) + tm.assert_index_equal(NaT - tdi, tdi_nat) def test_nat_pinned_docstrings(): From 6eec080187e500d161564134f65323a2a187120d Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 9 Jan 2018 08:10:40 -0800 Subject: [PATCH 2/3] Add Series+/-NaT tests --- pandas/tests/scalar/test_nat.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index 5f64ea399caef..dd87e1369d726 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -312,6 +312,22 @@ def test_nat_arithmetic_index(): tm.assert_index_equal(NaT - tdi, tdi_nat) +@pytest.mark.xfail(reason='NaT - Series returns NaT. This behavior was ' + 'introduced somewhere between 0.22.0 and ' + '23fb3392adedd3a') +def test_nat_arithmetic_series(): + # GH#19124 + tdi = TimedeltaIndex(['1 day', '2 day'], name='x') + tdi_nat = TimedeltaIndex([NaT, NaT], name='x') + ser = Series(tdi) + ser_nat = Series(tdi_nat) + + tm.assert_series_equal(ser + NaT, ser_nat) + tm.assert_series_equal(NaT + ser, ser_nat) + tm.assert_series_equal(ser - NaT, ser_nat) + tm.assert_series_equal(NaT - ser, ser_nat) + + def test_nat_pinned_docstrings(): # GH17327 assert NaT.ctime.__doc__ == datetime.ctime.__doc__ From 0fdd795953e9eeb99582cdf2e8fe53b034faf058 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 9 Jan 2018 16:49:40 -0800 Subject: [PATCH 3/3] requested simplification --- pandas/tests/scalar/test_nat.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index dd87e1369d726..b9fc9e1b6816e 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -317,10 +317,8 @@ def test_nat_arithmetic_index(): '23fb3392adedd3a') def test_nat_arithmetic_series(): # GH#19124 - tdi = TimedeltaIndex(['1 day', '2 day'], name='x') - tdi_nat = TimedeltaIndex([NaT, NaT], name='x') - ser = Series(tdi) - ser_nat = Series(tdi_nat) + ser = Series(['1 day', '2 day'], dtype='timedelta64[ns]') + ser_nat = Series([NaT, NaT], dtype='timedelta64[ns]') tm.assert_series_equal(ser + NaT, ser_nat) tm.assert_series_equal(NaT + ser, ser_nat)