From a476597bef1c8bb1a2ab5a08ecf1267a1bb68c48 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Dec 2019 18:20:13 -0800 Subject: [PATCH 1/2] BUG: DTA/TDA/PA iadd/isub should actually be inplace --- doc/source/whatsnew/v1.0.0.rst | 2 +- pandas/core/arrays/datetimelike.py | 23 ++++++++++++++++------- pandas/tests/arrays/test_datetimelike.py | 13 +++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 4671170fa79ae..c6c5562af3951 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -716,7 +716,7 @@ Datetimelike - Bug in :class:`DatetimeIndex` addition when adding a non-optimized :class:`DateOffset` incorrectly dropping timezone information (:issue:`30336`) - Bug in :meth:`DataFrame.drop` where attempting to drop non-existent values from a DatetimeIndex would yield a confusing error message (:issue:`30399`) - Bug in :meth:`DataFrame.append` would remove the timezone-awareness of new data (:issue:`30238`) - +- Bug in :class:`DatetimeArray`, :class:`TimedeltaArray`, and :class:`PeriodArray` where inplace addition and subtraction did not actually operate inplace (:issue:`24115`) Timedelta ^^^^^^^^^ diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index ff21a66928294..1dde5c72d1f40 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1311,14 +1311,23 @@ def __rsub__(self, other): return -(self - other) - # FIXME: DTA/TDA/PA inplace methods should actually be inplace, GH#24115 - def __iadd__(self, other): # type: ignore - # alias for __add__ - return self.__add__(other) + def __iadd__(self, other): + result = self + other + self[:] = result[:] + + if not is_period_dtype(self): + # restore freq, which is invalidated by setitem + self._freq = result._freq + return self + + def __isub__(self, other): + result = self - other + self[:] = result[:] - def __isub__(self, other): # type: ignore - # alias for __sub__ - return self.__sub__(other) + if not is_period_dtype(self): + # restore freq, which is invalidated by setitem + self._freq = result._freq + return self # -------------------------------------------------------------- # Comparison Methods diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 84b6d45b78fe8..e9c64d04ec860 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -225,6 +225,19 @@ def test_setitem_raises(self): with pytest.raises(TypeError, match="'value' should be a.* 'object'"): arr[0] = object() + def test_inplace_arithmetic(self): + # GH#24115 check that iadd and isub are actually in-place + data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 + arr = self.array_cls(data, freq="D") + + expected = arr + pd.Timedelta(days=1) + arr += pd.Timedelta(days=1) + tm.assert_equal(arr, expected) + + expected = arr - pd.Timedelta(days=1) + arr -= pd.Timedelta(days=1) + tm.assert_equal(arr, expected) + class TestDatetimeArray(SharedTests): index_cls = pd.DatetimeIndex From 76f9f6a0fdf2159bc9bdd137a66eac4ae510a1ee Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Dec 2019 19:02:11 -0800 Subject: [PATCH 2/2] reignore --- pandas/core/arrays/datetimelike.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 1dde5c72d1f40..66f0ad2500c54 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1311,7 +1311,7 @@ def __rsub__(self, other): return -(self - other) - def __iadd__(self, other): + def __iadd__(self, other): # type: ignore result = self + other self[:] = result[:] @@ -1320,7 +1320,7 @@ def __iadd__(self, other): self._freq = result._freq return self - def __isub__(self, other): + def __isub__(self, other): # type: ignore result = self - other self[:] = result[:]