From d21a893d9f80949d5a5292160da77df2fe04ed86 Mon Sep 17 00:00:00 2001 From: JustinZhengBC Date: Tue, 25 Dec 2018 11:30:00 -0800 Subject: [PATCH 1/4] BUG-24408 Series.dt updates index when needed --- doc/source/whatsnew/v0.24.0.rst | 1 + pandas/core/indexes/accessors.py | 8 +++----- pandas/tests/series/test_datetime_values.py | 7 +++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 018820b09dba4..3240a2845fbd5 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -1321,6 +1321,7 @@ Datetimelike - Bug in :class:`DatetimeIndex` and :class:`TimedeltaIndex` where indexing with ``Ellipsis`` would incorrectly lose the index's ``freq`` attribute (:issue:`21282`) - Clarified error message produced when passing an incorrect ``freq`` argument to :class:`DatetimeIndex` with ``NaT`` as the first entry in the passed data (:issue:`11587`) - Bug in :func:`to_datetime` where ``box`` and ``utc`` arguments were ignored when passing a :class:`DataFrame` or ``dict`` of unit mappings (:issue:`23760`) +- Bug in :attr:`Series.dt`` where the cache would not update properly after an in-place operation (:issue:`24408`) Timedelta ^^^^^^^^^ diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 670c4a2d8a414..791ef8223de98 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -27,7 +27,6 @@ def __init__(self, data, orig): self._parent = data self.orig = orig self.name = getattr(data, 'name', None) - self.index = getattr(data, 'index', None) self._freeze() def _get_values(self): @@ -71,8 +70,7 @@ def _delegate_property_get(self, name): result = take_1d(result, self.orig.cat.codes) index = self.orig.index else: - index = self.index - + index = self._parent.index # return the result as a Series, which is by definition a copy result = Series(result, index=index, name=self.name) @@ -98,7 +96,7 @@ def _delegate_method(self, name, *args, **kwargs): if not is_list_like(result): return result - result = Series(result, index=self.index, name=self.name) + result = Series(result, index=self._parent.index, name=self.name) # setting this object will show a SettingWithCopyWarning/Error result._is_copy = ("modifications to a method of a datetimelike " @@ -261,7 +259,7 @@ def components(self): 3 0 0 0 3 0 0 0 4 0 0 0 4 0 0 0 """ # noqa: E501 - return self._get_values().components.set_index(self.index) + return self._get_values().components.set_index(self._parent.index) @property def freq(self): diff --git a/pandas/tests/series/test_datetime_values.py b/pandas/tests/series/test_datetime_values.py index 745a9eee6c300..895717835c630 100644 --- a/pandas/tests/series/test_datetime_values.py +++ b/pandas/tests/series/test_datetime_values.py @@ -485,6 +485,13 @@ def test_dt_accessor_invalid(self, ser): ser.dt assert not hasattr(ser, 'dt') + def test_dt_accessor_updates_on_inplace(self): + s = Series(pd.date_range('2018-01-01', periods=10)) + s[2] = None + s.fillna(pd.Timestamp('2018-01-01'), inplace=True) + result = s.dt.date + assert result[0] == result[2] + def test_between(self): s = Series(bdate_range('1/1/2000', periods=20).astype(object)) s[::2] = np.nan From 7cdc95419279028c0eb3835eca73a6613cbccd69 Mon Sep 17 00:00:00 2001 From: JustinZhengBC Date: Tue, 25 Dec 2018 17:10:58 -0800 Subject: [PATCH 2/4] BUG-24408 add tests for other accessors --- pandas/tests/plotting/test_series.py | 12 ++++++++++++ pandas/tests/series/test_api.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py index b5c69bb9e6443..b857979005f5e 100644 --- a/pandas/tests/plotting/test_series.py +++ b/pandas/tests/plotting/test_series.py @@ -890,3 +890,15 @@ def test_misc_bindings(self, mock): assert s.plot.lag() == 2 assert s.plot.autocorrelation() == 2 assert s.plot.bootstrap() == 2 + + @pytest.mark.xfail + def test_plot_accessor_updates_on_inplace(self): + s = Series([1, 2, 3, 4]) + _, ax = self.plt.subplots() + ax = s.plot(ax=ax) + before = ax.xaxis.get_ticklocs() + + s.drop([0, 1], inplace=True) + _, ax = self.plt.subplots() + after = ax.xaxis.get_ticklocs() + tm.assert_numpy_array_equal(before, after) diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index 65f5c59deba36..122e9fef1a3ad 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -204,6 +204,11 @@ def test_from_array_deprecated(self): with tm.assert_produces_warning(FutureWarning): self.series_klass.from_array([1, 2, 3]) + def test_sparse_accessor_updates_on_inplace(self): + s = pd.Series([1, 1, 2, 3], dtype="Sparse[int]") + s.drop([0, 1], inplace=True) + assert s.sparse.density == 0 + class TestSeriesMisc(TestData, SharedWithSparse): @@ -447,6 +452,11 @@ def f(x): exp = Series([], dtype='float64', index=Index([], dtype='float64')) tm.assert_series_equal(result, exp) + def test_str_accessor_updates_on_inplace(self): + s = pd.Series(list('abc')) + s.drop([0], inplace=True) + assert len(s.str.lower()) == 2 + def test_str_attribute(self): # GH9068 methods = ['strip', 'rstrip', 'lstrip'] @@ -535,6 +545,12 @@ def test_cat_accessor_no_new_attributes(self): match="You cannot add any new attribute"): c.cat.xlabel = "a" + def test_cat_accessor_updates_on_inplace(self): + s = Series(list('abc')).astype('category') + s.drop(0, inplace=True) + s.cat.remove_unused_categories() + assert len(s.cat.categories) == 2 + def test_categorical_delegations(self): # invalid accessor From 0790af10ef509900ae06db78e2354bd46036ba22 Mon Sep 17 00:00:00 2001 From: Justin Zheng Date: Tue, 25 Dec 2018 17:12:32 -0800 Subject: [PATCH 3/4] fix typo --- doc/source/whatsnew/v0.24.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 3def18fd95241..2b8cb81b78c4d 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -1321,7 +1321,7 @@ Datetimelike - Bug in :class:`DatetimeIndex` and :class:`TimedeltaIndex` where indexing with ``Ellipsis`` would incorrectly lose the index's ``freq`` attribute (:issue:`21282`) - Clarified error message produced when passing an incorrect ``freq`` argument to :class:`DatetimeIndex` with ``NaT`` as the first entry in the passed data (:issue:`11587`) - Bug in :func:`to_datetime` where ``box`` and ``utc`` arguments were ignored when passing a :class:`DataFrame` or ``dict`` of unit mappings (:issue:`23760`) -- Bug in :attr:`Series.dt`` where the cache would not update properly after an in-place operation (:issue:`24408`) +- Bug in :attr:`Series.dt` where the cache would not update properly after an in-place operation (:issue:`24408`) - Bug in :class:`PeriodIndex` where comparisons against an array-like object with length 1 failed to raise ``ValueError`` (:issue:`23078`) Timedelta From 5dc04ac987a56687fc4e4d2f7d446d0d1d676e74 Mon Sep 17 00:00:00 2001 From: JustinZhengBC Date: Tue, 25 Dec 2018 17:15:08 -0800 Subject: [PATCH 4/4] BUG-24408 fix typo in test --- pandas/tests/series/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index 122e9fef1a3ad..09e556af883c1 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -207,7 +207,7 @@ def test_from_array_deprecated(self): def test_sparse_accessor_updates_on_inplace(self): s = pd.Series([1, 1, 2, 3], dtype="Sparse[int]") s.drop([0, 1], inplace=True) - assert s.sparse.density == 0 + assert s.sparse.density == 1.0 class TestSeriesMisc(TestData, SharedWithSparse): @@ -548,7 +548,7 @@ def test_cat_accessor_no_new_attributes(self): def test_cat_accessor_updates_on_inplace(self): s = Series(list('abc')).astype('category') s.drop(0, inplace=True) - s.cat.remove_unused_categories() + s.cat.remove_unused_categories(inplace=True) assert len(s.cat.categories) == 2 def test_categorical_delegations(self):