diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 1f656f267783f..9f358a3e9dbdd 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -399,6 +399,7 @@ Other API changes ^^^^^^^^^^^^^^^^^ - :meth:`Index.get_indexer_for` no longer accepts keyword arguments (other than 'target'); in the past these would be silently ignored if the index was not unique (:issue:`42310`) - Change in the position of the ``min_rows`` argument in :meth:`DataFrame.to_string` due to change in the docstring (:issue:`44304`) +- Reduction operations for :class:`DataFrame` or :class:`Series` now raising a ``ValueError`` when ``None`` is passed for ``skipna`` (:issue:`44178`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 888376ea8e1dc..91216ef03c8b3 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -10303,6 +10303,7 @@ def _logical_func( self, name: str, func, axis=0, bool_only=None, skipna=True, level=None, **kwargs ): nv.validate_logical_func((), kwargs, fname=name) + validate_bool_kwarg(skipna, "skipna", none_allowed=False) if level is not None: warnings.warn( "Using the level keyword in DataFrame and Series aggregations is " @@ -10397,6 +10398,7 @@ def _stat_function_ddof( **kwargs, ): nv.validate_stat_ddof_func((), kwargs, fname=name) + validate_bool_kwarg(skipna, "skipna", none_allowed=False) if axis is None: axis = self._stat_axis_number if level is not None: @@ -10450,6 +10452,9 @@ def _stat_function( nv.validate_median((), kwargs) else: nv.validate_stat_func((), kwargs, fname=name) + + validate_bool_kwarg(skipna, "skipna", none_allowed=False) + if axis is None: axis = self._stat_axis_number if level is not None: @@ -10517,6 +10522,9 @@ def _min_count_stat_function( nv.validate_prod((), kwargs) else: nv.validate_stat_func((), kwargs, fname=name) + + validate_bool_kwarg(skipna, "skipna", none_allowed=False) + if axis is None: axis = self._stat_axis_number if level is not None: @@ -10669,7 +10677,7 @@ def all(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs): see_also="", examples="", ) - def mad(self, axis=None, skipna=None, level=None): + def mad(self, axis=None, skipna=True, level=None): return NDFrame.mad(self, axis, skipna, level) setattr(cls, "mad", mad) diff --git a/pandas/tests/apply/test_str.py b/pandas/tests/apply/test_str.py index 6c6b674ef6aab..a292b05ee444d 100644 --- a/pandas/tests/apply/test_str.py +++ b/pandas/tests/apply/test_str.py @@ -26,7 +26,7 @@ pytest.param([1], {}, id="axis_from_args"), pytest.param([], {"axis": 1}, id="axis_from_kwds"), pytest.param([], {"numeric_only": True}, id="optional_kwds"), - pytest.param([1, None], {"numeric_only": True}, id="args_and_kwds"), + pytest.param([1, True], {"numeric_only": True}, id="args_and_kwds"), ], ) @pytest.mark.parametrize("how", ["agg", "apply"]) diff --git a/pandas/tests/frame/conftest.py b/pandas/tests/frame/conftest.py index 3729f921f59ad..b512664b57ade 100644 --- a/pandas/tests/frame/conftest.py +++ b/pandas/tests/frame/conftest.py @@ -259,3 +259,26 @@ def frame_of_index_cols(): } ) return df + + +@pytest.fixture( + params=[ + "any", + "all", + "count", + "sum", + "prod", + "max", + "min", + "mean", + "median", + "skew", + "kurt", + "sem", + "var", + "std", + "mad", + ] +) +def reduction_functions(request): + return request.param diff --git a/pandas/tests/frame/test_reductions.py b/pandas/tests/frame/test_reductions.py index fc2c138538ac9..8efae54dc7c11 100644 --- a/pandas/tests/frame/test_reductions.py +++ b/pandas/tests/frame/test_reductions.py @@ -1478,33 +1478,23 @@ def test_frame_any_with_timedelta(self): expected = Series(data=[False, True]) tm.assert_series_equal(result, expected) - @pytest.mark.parametrize( - "func", - [ - "any", - "all", - "count", - "sum", - "prod", - "max", - "min", - "mean", - "median", - "skew", - "kurt", - "sem", - "var", - "std", - "mad", - ], - ) - def test_reductions_deprecation_level_argument(self, frame_or_series, func): + def test_reductions_deprecation_level_argument( + self, frame_or_series, reduction_functions + ): # GH#39983 obj = frame_or_series( [1, 2, 3], index=MultiIndex.from_arrays([[1, 2, 3], [4, 5, 6]]) ) with tm.assert_produces_warning(FutureWarning, match="level"): - getattr(obj, func)(level=0) + getattr(obj, reduction_functions)(level=0) + + def test_reductions_skipna_none_raises(self, frame_or_series, reduction_functions): + if reduction_functions in ["count", "mad"]: + pytest.skip("Count does not accept skipna. Mad needs a depreaction cycle.") + obj = frame_or_series([1, 2, 3]) + msg = 'For argument "skipna" expected type bool, received type NoneType.' + with pytest.raises(ValueError, match=msg): + getattr(obj, reduction_functions)(skipna=None) class TestNuisanceColumns: