From 0c2f267be22740fe03e53faf4d68473b1d7e4f10 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 31 Dec 2021 15:37:58 -0800 Subject: [PATCH 1/2] DEPR: special-casing PeriodDtype in Series.where/Series/mask fallback --- doc/source/whatsnew/v1.4.0.rst | 1 + pandas/core/internals/blocks.py | 13 ++++++++++--- pandas/tests/frame/indexing/test_where.py | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index d770782d5dc62..2fd7d370b3693 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -636,6 +636,7 @@ Other Deprecations - Deprecated casting behavior when setting timezone-aware value(s) into a timezone-aware :class:`Series` or :class:`DataFrame` column when the timezones do not match. Previously this cast to object dtype. In a future version, the values being inserted will be converted to the series or column's existing timezone (:issue:`37605`) - Deprecated casting behavior when passing an item with mismatched-timezone to :meth:`DatetimeIndex.insert`, :meth:`DatetimeIndex.putmask`, :meth:`DatetimeIndex.where` :meth:`DatetimeIndex.fillna`, :meth:`Series.mask`, :meth:`Series.where`, :meth:`Series.fillna`, :meth:`Series.shift`, :meth:`Series.replace`, :meth:`Series.reindex` (and :class:`DataFrame` column analogues). In the past this has cast to object dtype. In a future version, these will cast the passed item to the index or series's timezone (:issue:`37605`,:issue:`44940`) - Deprecated the 'errors' keyword argument in :meth:`Series.where`, :meth:`DataFrame.where`, :meth:`Series.mask`, and :meth:`DataFrame.mask`; in a future version the argument will be removed (:issue:`44294`) +- Deprecated the fallback behavior for :meth:`Series.where`, :meth:`Series.mask`, :meth:`DataFrame.where` and :meth:`DataFrame.mask` with ``PeriodDtype`` when an incompatible value is passed. In a future version, this will coerce to a common dtype instead of raising, matching other datetime-like dtypes (:issue:`??`) - Deprecated the ``prefix`` keyword argument in :func:`read_csv` and :func:`read_table`, in a future version the argument will be removed (:issue:`43396`) - Deprecated passing non boolean argument to sort in :func:`concat` (:issue:`41518`) - Deprecated passing arguments as positional for :func:`read_fwf` other than ``filepath_or_buffer`` (:issue:`41485`): diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 9f242226739ec..0cc4d51a75782 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1369,9 +1369,16 @@ def where(self, other, cond) -> list[Block]: # NB: not (yet) the same as # isinstance(values, NDArrayBackedExtensionArray) if isinstance(self.dtype, PeriodDtype): - # TODO: don't special-case - # Note: this is the main place where the fallback logic - # is different from EABackedBlock.putmask. + # TODO(2.0): once this deprecation is enforced, we can + # share fallback logic with EABackedBlock.putmask. + warnings.warn( + "The fallback behavior of .where and .mask with PeriodDtype is " + "deprecated. In a future version, when an incompatible " + "value is passed, the array will be upcast to a common dtype " + "(matching the behavior of other datetimelike dtypes).", + FutureWarning, + stacklevel=find_stack_level(), + ) raise blk = self.coerce_to_target_dtype(other) nbs = blk.where(other, cond) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 197c3ac9bd225..01911a001a4ec 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -886,11 +886,15 @@ def test_where_period_invalid_na(frame_or_series, as_cat, request): else: msg = "value should be a 'Period'" + warn = None if as_cat else FutureWarning + wmsg = "The fallback behavior of .where with PeriodDtype" with pytest.raises(TypeError, match=msg): - obj.where(mask, tdnat) + with tm.assert_produces_warning(warn, match=wmsg): + obj.where(mask, tdnat) with pytest.raises(TypeError, match=msg): - obj.mask(mask, tdnat) + with tm.assert_produces_warning(warn, match=wmsg): + obj.mask(mask, tdnat) def test_where_nullable_invalid_na(frame_or_series, any_numeric_ea_dtype): From 979ff489f7ccdeef1f6d9fed3728905c4e59bda0 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 31 Dec 2021 15:40:57 -0800 Subject: [PATCH 2/2] GH ref --- doc/source/whatsnew/v1.4.0.rst | 2 +- pandas/core/internals/blocks.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 2fd7d370b3693..fea94dc694ad3 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -636,7 +636,7 @@ Other Deprecations - Deprecated casting behavior when setting timezone-aware value(s) into a timezone-aware :class:`Series` or :class:`DataFrame` column when the timezones do not match. Previously this cast to object dtype. In a future version, the values being inserted will be converted to the series or column's existing timezone (:issue:`37605`) - Deprecated casting behavior when passing an item with mismatched-timezone to :meth:`DatetimeIndex.insert`, :meth:`DatetimeIndex.putmask`, :meth:`DatetimeIndex.where` :meth:`DatetimeIndex.fillna`, :meth:`Series.mask`, :meth:`Series.where`, :meth:`Series.fillna`, :meth:`Series.shift`, :meth:`Series.replace`, :meth:`Series.reindex` (and :class:`DataFrame` column analogues). In the past this has cast to object dtype. In a future version, these will cast the passed item to the index or series's timezone (:issue:`37605`,:issue:`44940`) - Deprecated the 'errors' keyword argument in :meth:`Series.where`, :meth:`DataFrame.where`, :meth:`Series.mask`, and :meth:`DataFrame.mask`; in a future version the argument will be removed (:issue:`44294`) -- Deprecated the fallback behavior for :meth:`Series.where`, :meth:`Series.mask`, :meth:`DataFrame.where` and :meth:`DataFrame.mask` with ``PeriodDtype`` when an incompatible value is passed. In a future version, this will coerce to a common dtype instead of raising, matching other datetime-like dtypes (:issue:`??`) +- Deprecated the fallback behavior for :meth:`Series.where`, :meth:`Series.mask`, :meth:`DataFrame.where` and :meth:`DataFrame.mask` with ``PeriodDtype`` when an incompatible value is passed. In a future version, this will coerce to a common dtype instead of raising, matching other datetime-like dtypes (:issue:`45148`) - Deprecated the ``prefix`` keyword argument in :func:`read_csv` and :func:`read_table`, in a future version the argument will be removed (:issue:`43396`) - Deprecated passing non boolean argument to sort in :func:`concat` (:issue:`41518`) - Deprecated passing arguments as positional for :func:`read_fwf` other than ``filepath_or_buffer`` (:issue:`41485`): diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 0cc4d51a75782..02c1054a1900d 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1371,6 +1371,7 @@ def where(self, other, cond) -> list[Block]: if isinstance(self.dtype, PeriodDtype): # TODO(2.0): once this deprecation is enforced, we can # share fallback logic with EABackedBlock.putmask. + # GH#45148 warnings.warn( "The fallback behavior of .where and .mask with PeriodDtype is " "deprecated. In a future version, when an incompatible "