From 82366678e79b6f3c15c016e783e2dfda6efd146b Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 2 Apr 2021 15:12:56 -0700 Subject: [PATCH 1/2] BUG: Series.__delitem__ converting EAs to ndarrays --- doc/source/whatsnew/v1.3.0.rst | 1 + pandas/core/internals/blocks.py | 36 ++++++++++++-------- pandas/tests/series/indexing/test_delitem.py | 21 ++++++++++++ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 75b2dee4a5822..49ddeb23e3193 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -642,6 +642,7 @@ Indexing - Bug in setting ``numpy.timedelta64`` values into an object-dtype :class:`Series` using a boolean indexer (:issue:`39488`) - Bug in setting numeric values into a into a boolean-dtypes :class:`Series` using ``at`` or ``iat`` failing to cast to object-dtype (:issue:`39582`) - Bug in :meth:`DataFrame.loc.__setitem__` when setting-with-expansion incorrectly raising when the index in the expanding axis contains duplicates (:issue:`40096`) +- Bug in :meth:`Series.__delitem__` with ``ExtensionDtype`` incorrectly casting to ``ndarray`` (:issue:`40386`) Missing ^^^^^^^ diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 7c2a31e63eeb3..2eb0d90d5b730 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1338,7 +1338,26 @@ def quantile( return new_block(result, placement=self._mgr_locs, ndim=2) -class ExtensionBlock(Block): +class EABackedBlockMixin: + """ + Mixin for Block subclasses backed by ExtensionArray. + """ + + def delete(self, loc) -> None: + """ + Delete given loc(-s) from block in-place. + """ + # This will be unnecessary if/when __array_function__ is implemented + self.values = self.values.delete(loc) + self.mgr_locs = self._mgr_locs.delete(loc) + try: + self._cache.clear() + except AttributeError: + # _cache not yet initialized + pass + + +class ExtensionBlock(EABackedBlockMixin, Block): """ Block for holding extension types. @@ -1647,7 +1666,7 @@ class NumericBlock(Block): is_numeric = True -class NDArrayBackedExtensionBlock(Block): +class NDArrayBackedExtensionBlock(EABackedBlockMixin, Block): """ Block backed by an NDArrayBackedExtensionArray """ @@ -1754,19 +1773,6 @@ def fillna( new_values = values.fillna(value=value, limit=limit) return [self.make_block_same_class(values=new_values)] - def delete(self, loc) -> None: - """ - Delete given loc(-s) from block in-place. - """ - # This will be unnecessary if/when __array_function__ is implemented - self.values = self.values.delete(loc, axis=0) - self.mgr_locs = self._mgr_locs.delete(loc) - try: - self._cache.clear() - except AttributeError: - # _cache not yet initialized - pass - class DatetimeLikeBlock(NDArrayBackedExtensionBlock): """Mixin class for DatetimeLikeBlock, DatetimeTZBlock.""" diff --git a/pandas/tests/series/indexing/test_delitem.py b/pandas/tests/series/indexing/test_delitem.py index 019cb92d780ef..af6b3910baec0 100644 --- a/pandas/tests/series/indexing/test_delitem.py +++ b/pandas/tests/series/indexing/test_delitem.py @@ -3,6 +3,7 @@ from pandas import ( Index, Series, + date_range, ) import pandas._testing as tm @@ -50,3 +51,23 @@ def test_delitem_missing_key(self): with pytest.raises(KeyError, match=r"^0$"): del s[0] + + def test_delitem_extension_dtype(self): + # GH#40386 + # DatetimeTZDtype + dti = date_range("2016-01-01", periods=3, tz="US/Pacific") + ser = Series(dti) + + expected = ser[[0, 2]] + del ser[1] + assert ser.dtype == dti.dtype + tm.assert_series_equal(ser, expected) + + # PeriodDtype + pi = dti.tz_localize(None).to_period("D") + ser = Series(pi) + + expected = ser[:2] + del ser[2] + assert ser.dtype == pi.dtype + tm.assert_series_equal(ser, expected) From 0e15c58d02a17bbbad0834db6a262c169622132e Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 2 Apr 2021 17:40:40 -0700 Subject: [PATCH 2/2] mypy fixup --- pandas/core/internals/blocks.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 2eb0d90d5b730..9a2b3be4b66e2 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1338,11 +1338,13 @@ def quantile( return new_block(result, placement=self._mgr_locs, ndim=2) -class EABackedBlockMixin: +class EABackedBlock(Block): """ Mixin for Block subclasses backed by ExtensionArray. """ + values: ExtensionArray + def delete(self, loc) -> None: """ Delete given loc(-s) from block in-place. @@ -1357,7 +1359,7 @@ def delete(self, loc) -> None: pass -class ExtensionBlock(EABackedBlockMixin, Block): +class ExtensionBlock(EABackedBlock): """ Block for holding extension types. @@ -1666,7 +1668,7 @@ class NumericBlock(Block): is_numeric = True -class NDArrayBackedExtensionBlock(EABackedBlockMixin, Block): +class NDArrayBackedExtensionBlock(EABackedBlock): """ Block backed by an NDArrayBackedExtensionArray """