diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 613a1e82d461f..cd384857153c0 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -638,6 +638,7 @@ Deprecations - :meth:`Index.holds_integer` has been deprecated. Use :func:`pandas.api.types.infer_dtype` instead (:issue:`50243`) - :meth:`Index.is_categorical` has been deprecated. Use :func:`pandas.api.types.is_categorical_dtype` instead (:issue:`50042`) - :meth:`Index.is_interval` has been deprecated. Use :func:`pandas.api.types.is_intterval_dtype` instead (:issue:`50042`) +- Deprecated ``all`` and ``any`` reductions with ``datetime64`` and :class:`DatetimeTZDtype` dtypes, use e.g. ``(obj != pd.Timestamp(0), tz=obj.tz).all()`` instead (:issue:`34479`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 816d8abec1428..4b26528e6661c 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -2034,11 +2034,12 @@ def ceil( # Reductions def any(self, *, axis: AxisInt | None = None, skipna: bool = True) -> bool: - # GH#34479 discussion of desired behavior long-term + # GH#34479 the nanops call will issue a FutureWarning for non-td64 dtype return nanops.nanany(self._ndarray, axis=axis, skipna=skipna, mask=self.isna()) def all(self, *, axis: AxisInt | None = None, skipna: bool = True) -> bool: - # GH#34479 discussion of desired behavior long-term + # GH#34479 the nanops call will issue a FutureWarning for non-td64 dtype + return nanops.nanall(self._ndarray, axis=axis, skipna=skipna, mask=self.isna()) # -------------------------------------------------------------- diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index d61668e372e0b..0af851669820e 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -32,6 +32,7 @@ npt, ) from pandas.compat._optional import import_optional_dependency +from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( is_any_int_dtype, @@ -529,6 +530,15 @@ def nanany( >>> nanops.nanany(s) False """ + if needs_i8_conversion(values.dtype) and values.dtype.kind != "m": + # GH#34479 + warnings.warn( + "'any' with datetime64 dtypes is deprecated and will raise in a " + "future version. Use (obj != pd.Timestamp(0)).any() instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + values, _, _, _, _ = _get_values(values, skipna, fill_value=False, mask=mask) # For object type, any won't necessarily return @@ -575,6 +585,15 @@ def nanall( >>> nanops.nanall(s) False """ + if needs_i8_conversion(values.dtype) and values.dtype.kind != "m": + # GH#34479 + warnings.warn( + "'all' with datetime64 dtypes is deprecated and will raise in a " + "future version. Use (obj != pd.Timestamp(0)).all() instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + values, _, _, _, _ = _get_values(values, skipna, fill_value=True, mask=mask) # For object type, all won't necessarily return diff --git a/pandas/tests/frame/test_reductions.py b/pandas/tests/frame/test_reductions.py index 2d395a7cbd608..3e6074971352d 100644 --- a/pandas/tests/frame/test_reductions.py +++ b/pandas/tests/frame/test_reductions.py @@ -1138,6 +1138,10 @@ def test_any_all_object_dtype(self, axis, bool_agg_func, skipna): expected = Series([True, True, True, True]) tm.assert_series_equal(result, expected) + # GH#50947 deprecates this but it is not emitting a warning in some builds. + @pytest.mark.filterwarnings( + "ignore:'any' with datetime64 dtypes is deprecated.*:FutureWarning" + ) def test_any_datetime(self): # GH 23070 @@ -1151,6 +1155,7 @@ def test_any_datetime(self): df = DataFrame({"A": float_data, "B": datetime_data}) result = df.any(axis=1) + expected = Series([True, True, True, False]) tm.assert_series_equal(result, expected) @@ -1245,12 +1250,22 @@ def test_any_all_np_func(self, func, data, expected): ): getattr(DataFrame(data), func.__name__)(axis=None) else: - result = func(data) + msg = "'(any|all)' with datetime64 dtypes is deprecated" + if data.dtypes.apply(lambda x: x.kind == "M").any(): + warn = FutureWarning + else: + warn = None + + with tm.assert_produces_warning(warn, match=msg, check_stacklevel=False): + # GH#34479 + result = func(data) assert isinstance(result, np.bool_) assert result.item() is expected # method version - result = getattr(DataFrame(data), func.__name__)(axis=None) + with tm.assert_produces_warning(warn, match=msg): + # GH#34479 + result = getattr(DataFrame(data), func.__name__)(axis=None) assert isinstance(result, np.bool_) assert result.item() is expected diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index 46cbbbd3e6480..e0ae3da482b35 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -985,27 +985,32 @@ def test_any_all_datetimelike(self): ser = Series(dta) df = DataFrame(ser) - assert dta.all() - assert dta.any() + msg = "'(any|all)' with datetime64 dtypes is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + # GH#34479 + assert dta.all() + assert dta.any() - assert ser.all() - assert ser.any() + assert ser.all() + assert ser.any() - assert df.any().all() - assert df.all().all() + assert df.any().all() + assert df.all().all() dta = dta.tz_localize("UTC") ser = Series(dta) df = DataFrame(ser) - assert dta.all() - assert dta.any() + with tm.assert_produces_warning(FutureWarning, match=msg): + # GH#34479 + assert dta.all() + assert dta.any() - assert ser.all() - assert ser.any() + assert ser.all() + assert ser.any() - assert df.any().all() - assert df.all().all() + assert df.any().all() + assert df.all().all() tda = dta - dta[0] ser = Series(tda)