From a59582da8c9fa410ac05e25ee117a7fef996247f Mon Sep 17 00:00:00 2001 From: Sachin Yadav <40866128+yadav-sachin@users.noreply.github.com> Date: Mon, 11 Apr 2022 01:04:27 +0530 Subject: [PATCH] DEPR: non-keyword arguments in any (#44896) * tighten return type in any * correct overload definitions * add overload def for NDFrame input * remove overload defs and define function any in sub-classes * add overload defs for level * correct default val in overload defs * deprecate non-keyword args * add whatsnew note * modify return types and add tests * move non-keyword deprecation to generic * correct deprecation decorators * remove imports in test assertions * place deprecate_nonkeyword at correct place * remove changes from frame.py, series.py * readd changes in frame, series without actual implementations * place deprecate_nonkeyword at other place * add name argument to deprecate_non_keyword_args decorator * add test for name in deprecate_nonkeyword_args * remove changes from frame.py, series.py * correct stacklevel in warning * correct stacklevel * set stacklevel to default * move deprecation message to whatsnew v1.5.0.rst * add name parameter in deprecate_non_keyword_args docstring * correct whitespace in deprecate_nonkeyword_args docstring * update any non-keyword args in other tests * update any in doc * update remaining any() calls in pandas/core * correct docstring of isocalendar in pandas/core/indexes/accessors.py Co-authored-by: Marco Edward Gorelli --- doc/source/whatsnew/v0.13.0.rst | 2 +- doc/source/whatsnew/v1.5.0.rst | 1 + pandas/core/generic.py | 10 +++++++-- pandas/core/missing.py | 2 +- pandas/tests/frame/test_reductions.py | 6 ++--- pandas/tests/groupby/test_any_all.py | 22 +++++++++++++++++++ .../test_deprecate_nonkeyword_arguments.py | 17 +++++++++++++- pandas/util/_decorators.py | 8 ++++++- 8 files changed, 59 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v0.13.0.rst b/doc/source/whatsnew/v0.13.0.rst index b2596358d0c9d..8265ad58f7ea3 100644 --- a/doc/source/whatsnew/v0.13.0.rst +++ b/doc/source/whatsnew/v0.13.0.rst @@ -664,7 +664,7 @@ Enhancements other = pd.DataFrame({'A': [1, 3, 3, 7], 'B': ['e', 'f', 'f', 'e']}) mask = dfi.isin(other) mask - dfi[mask.any(1)] + dfi[mask.any(axis=1)] - ``Series`` now supports a ``to_frame`` method to convert it to a single-column DataFrame (:issue:`5164`) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 71c27a8577666..4e685c918c21a 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -429,6 +429,7 @@ Other Deprecations - Deprecated behavior of method :meth:`DataFrame.quantile`, attribute ``numeric_only`` will default False. Including datetime/timedelta columns in the result (:issue:`7308`). - Deprecated :attr:`Timedelta.freq` and :attr:`Timedelta.is_populated` (:issue:`46430`) - Deprecated :attr:`Timedelta.delta` (:issue:`46476`) +- Deprecated passing arguments as positional in :meth:`DataFrame.any` and :meth:`Series.any` (:issue:`44802`) - Deprecated the ``closed`` argument in :meth:`interval_range` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`) - Deprecated the methods :meth:`DataFrame.mad`, :meth:`Series.mad`, and the corresponding groupby methods (:issue:`11787`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index bdde89e4f24ac..473435bae48eb 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7224,7 +7224,7 @@ def asof(self, where, subset=None): if not isinstance(where, Index): where = Index(where) if is_list else Index([where]) - nulls = self.isna() if is_series else self[subset].isna().any(1) + nulls = self.isna() if is_series else self[subset].isna().any(axis=1) if nulls.all(): if is_series: self = cast("Series", self) @@ -10517,7 +10517,7 @@ def any( skipna: bool_t = True, level: Level | None = None, **kwargs, - ) -> Series | bool_t: + ) -> DataFrame | Series | bool_t: return self._logical_func( "any", nanops.nanany, axis, bool_only, skipna, level, **kwargs ) @@ -10941,6 +10941,12 @@ def _add_numeric_operations(cls): """ axis_descr, name1, name2 = _doc_params(cls) + @deprecate_nonkeyword_arguments( + version=None, + allowed_args=["self"], + stacklevel=find_stack_level() - 1, + name="DataFrame.any and Series.any", + ) @doc( _bool_doc, desc=_any_desc, diff --git a/pandas/core/missing.py b/pandas/core/missing.py index 46ea23e431d15..b0bfbf13fbb2c 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -191,7 +191,7 @@ def find_valid_index(values, *, how: str) -> int | None: is_valid = ~isna(values) if values.ndim == 2: - is_valid = is_valid.any(1) # reduce axis 1 + is_valid = is_valid.any(axis=1) # reduce axis 1 if how == "first": idxpos = is_valid[::].argmax() diff --git a/pandas/tests/frame/test_reductions.py b/pandas/tests/frame/test_reductions.py index d2c47498b2fe5..b915d104d6365 100644 --- a/pandas/tests/frame/test_reductions.py +++ b/pandas/tests/frame/test_reductions.py @@ -1066,11 +1066,11 @@ def test_any_all_extra(self): }, index=["a", "b", "c"], ) - result = df[["A", "B"]].any(1) + result = df[["A", "B"]].any(axis=1) expected = Series([True, True, False], index=["a", "b", "c"]) tm.assert_series_equal(result, expected) - result = df[["A", "B"]].any(1, bool_only=True) + result = df[["A", "B"]].any(axis=1, bool_only=True) tm.assert_series_equal(result, expected) result = df.all(1) @@ -1119,7 +1119,7 @@ def test_any_datetime(self): ] df = DataFrame({"A": float_data, "B": datetime_data}) - result = df.any(1) + result = df.any(axis=1) expected = Series([True, True, True, False]) tm.assert_series_equal(result, expected) diff --git a/pandas/tests/groupby/test_any_all.py b/pandas/tests/groupby/test_any_all.py index 3f61a4ece66c0..784ff1069e7ff 100644 --- a/pandas/tests/groupby/test_any_all.py +++ b/pandas/tests/groupby/test_any_all.py @@ -61,6 +61,28 @@ def test_any(): tm.assert_frame_equal(result, expected) +def test_any_non_keyword_deprecation(): + df = DataFrame({"A": [1, 2], "B": [0, 2], "C": [0, 0]}) + msg = ( + "In a future version of pandas all arguments of " + "DataFrame.any and Series.any will be keyword-only." + ) + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.any("index", None) + expected = Series({"A": True, "B": True, "C": False}) + tm.assert_series_equal(result, expected) + + s = Series([False, False, False]) + msg = ( + "In a future version of pandas all arguments of " + "DataFrame.any and Series.any will be keyword-only." + ) + with tm.assert_produces_warning(FutureWarning, match=msg): + result = s.any("index") + expected = False + tm.assert_equal(result, expected) + + @pytest.mark.parametrize("bool_agg_func", ["any", "all"]) def test_bool_aggs_dup_column_labels(bool_agg_func): # 21668 diff --git a/pandas/tests/util/test_deprecate_nonkeyword_arguments.py b/pandas/tests/util/test_deprecate_nonkeyword_arguments.py index c03639811d9d5..346561e0ba7ff 100644 --- a/pandas/tests/util/test_deprecate_nonkeyword_arguments.py +++ b/pandas/tests/util/test_deprecate_nonkeyword_arguments.py @@ -9,7 +9,9 @@ import pandas._testing as tm -@deprecate_nonkeyword_arguments(version="1.1", allowed_args=["a", "b"]) +@deprecate_nonkeyword_arguments( + version="1.1", allowed_args=["a", "b"], name="f_add_inputs" +) def f(a, b=0, c=0, d=0): return a + b + c + d @@ -44,6 +46,19 @@ def test_four_arguments(): assert f(1, 2, 3, 4) == 10 +def test_three_arguments_with_name_in_warning(): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + assert f(6, 3, 3) == 12 + assert len(w) == 1 + for actual_warning in w: + assert actual_warning.category == FutureWarning + assert str(actual_warning.message) == ( + "Starting with pandas version 1.1 all arguments of f_add_inputs " + "except for the arguments 'a' and 'b' will be keyword-only." + ) + + @deprecate_nonkeyword_arguments(version="1.1") def g(a, b=0, c=0, d=0): with tm.assert_produces_warning(None): diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 2b160d2946b2b..6f8889e9017d0 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -261,6 +261,7 @@ def deprecate_nonkeyword_arguments( version: str | None, allowed_args: list[str] | None = None, stacklevel: int = 2, + name: str | None = None, ) -> Callable[[F], F]: """ Decorator to deprecate a use of non-keyword arguments of a function. @@ -281,6 +282,11 @@ def deprecate_nonkeyword_arguments( stacklevel : int, default=2 The stack level for warnings.warn + + name : str, optional + The specific name of the function to show in the warning + message. If None, then the Qualified name of the function + is used. """ def decorate(func): @@ -296,7 +302,7 @@ def decorate(func): num_allow_args = len(allow_args) msg = ( f"{future_version_msg(version)} all arguments of " - f"{func.__qualname__}{{arguments}} will be keyword-only." + f"{name or func.__qualname__}{{arguments}} will be keyword-only." ) @wraps(func)