diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index f225d384888e3..c5d032f9dace5 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -259,6 +259,7 @@ Removal of prior version deprecations/changes - Removed the :class:`Grouper` attributes ``ax``, ``groups``, ``indexer``, and ``obj`` (:issue:`51206`, :issue:`51182`) - Removed deprecated keyword ``verbose`` on :func:`read_csv` and :func:`read_table` (:issue:`56556`) - Removed the attribute ``dtypes`` from :class:`.DataFrameGroupBy` (:issue:`51997`) +- Enforced deprecation of ``argmin``, ``argmax``, ``idxmin``, and ``idxmax`` returning a result when ``skipna=False`` and an NA value is encountered or all values are NA values; these operations will now raise in such cases (:issue:`33941`, :issue:`51276`) .. --------------------------------------------------------------------------- .. _whatsnew_300.performance: diff --git a/pandas/core/base.py b/pandas/core/base.py index 987136ffdff7d..80919130cab63 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -14,7 +14,6 @@ final, overload, ) -import warnings import numpy as np @@ -35,7 +34,6 @@ cache_readonly, doc, ) -from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.cast import can_hold_element from pandas.core.dtypes.common import ( @@ -686,7 +684,8 @@ def argmax( axis : {{None}} Unused. Parameter needed for compatibility with DataFrame. skipna : bool, default True - Exclude NA/null values when showing the result. + Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` + and there is an NA value, this method will raise a ``ValueError``. *args, **kwargs Additional arguments and keywords for compatibility with NumPy. @@ -736,28 +735,15 @@ def argmax( nv.validate_minmax_axis(axis) skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) + if skipna and len(delegate) > 0 and isna(delegate).all(): + raise ValueError("Encountered all NA values") + elif not skipna and isna(delegate).any(): + raise ValueError("Encountered an NA value with skipna=False") + if isinstance(delegate, ExtensionArray): - if not skipna and delegate.isna().any(): - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) - return -1 - else: - return delegate.argmax() + return delegate.argmax() else: result = nanops.nanargmax(delegate, skipna=skipna) - if result == -1: - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) # error: Incompatible return value type (got "Union[int, ndarray]", expected # "int") return result # type: ignore[return-value] @@ -770,28 +756,15 @@ def argmin( nv.validate_minmax_axis(axis) skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) + if skipna and len(delegate) > 0 and isna(delegate).all(): + raise ValueError("Encountered all NA values") + elif not skipna and isna(delegate).any(): + raise ValueError("Encountered an NA value with skipna=False") + if isinstance(delegate, ExtensionArray): - if not skipna and delegate.isna().any(): - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) - return -1 - else: - return delegate.argmin() + return delegate.argmin() else: result = nanops.nanargmin(delegate, skipna=skipna) - if result == -1: - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) # error: Incompatible return value type (got "Union[int, ndarray]", expected # "int") return result # type: ignore[return-value] diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9a537c71f3cd0..3cb37e037ecd3 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6976,16 +6976,10 @@ def argmin(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: if not self._is_multi and self.hasnans: # Take advantage of cache - mask = self._isnan - if not skipna or mask.all(): - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) - return -1 + if self._isnan.all(): + raise ValueError("Encountered all NA values") + elif not skipna: + raise ValueError("Encountered an NA value with skipna=False") return super().argmin(skipna=skipna) @Appender(IndexOpsMixin.argmax.__doc__) @@ -6995,16 +6989,10 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: if not self._is_multi and self.hasnans: # Take advantage of cache - mask = self._isnan - if not skipna or mask.all(): - warnings.warn( - f"The behavior of {type(self).__name__}.argmax/argmin " - "with skipna=False and NAs, or with all-NAs is deprecated. " - "In a future version this will raise ValueError.", - FutureWarning, - stacklevel=find_stack_level(), - ) - return -1 + if self._isnan.all(): + raise ValueError("Encountered all NA values") + elif not skipna: + raise ValueError("Encountered an NA value with skipna=False") return super().argmax(skipna=skipna) def min(self, axis=None, skipna: bool = True, *args, **kwargs): diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index 6cb825e9b79a2..b68337d9e0de9 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -1441,17 +1441,18 @@ def _maybe_arg_null_out( if axis is None or not getattr(result, "ndim", False): if skipna: if mask.all(): - return -1 + raise ValueError("Encountered all NA values") else: if mask.any(): - return -1 + raise ValueError("Encountered an NA value with skipna=False") else: - if skipna: - na_mask = mask.all(axis) - else: - na_mask = mask.any(axis) + na_mask = mask.all(axis) if na_mask.any(): - result[na_mask] = -1 + raise ValueError("Encountered all NA values") + elif not skipna: + na_mask = mask.any(axis) + if na_mask.any(): + raise ValueError("Encountered an NA value with skipna=False") return result diff --git a/pandas/core/series.py b/pandas/core/series.py index 08e56cb4925b3..4b0eb4e1f7358 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2333,8 +2333,8 @@ def idxmin(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab axis : {0 or 'index'} Unused. Parameter needed for compatibility with DataFrame. skipna : bool, default True - Exclude NA/null values. If the entire Series is NA, the result - will be NA. + Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` + and there is an NA value, this method will raise a ``ValueError``. *args, **kwargs Additional arguments and keywords have no effect but might be accepted for compatibility with NumPy. @@ -2376,32 +2376,10 @@ def idxmin(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab >>> s.idxmin() 'A' - - If `skipna` is False and there is an NA value in the data, - the function returns ``nan``. - - >>> s.idxmin(skipna=False) - nan """ axis = self._get_axis_number(axis) - with warnings.catch_warnings(): - # TODO(3.0): this catching/filtering can be removed - # ignore warning produced by argmin since we will issue a different - # warning for idxmin - warnings.simplefilter("ignore") - i = self.argmin(axis, skipna, *args, **kwargs) - - if i == -1: - # GH#43587 give correct NA value for Index. - warnings.warn( - f"The behavior of {type(self).__name__}.idxmin with all-NA " - "values, or any-NA and skipna=False, is deprecated. In a future " - "version this will raise ValueError", - FutureWarning, - stacklevel=find_stack_level(), - ) - return self.index._na_value - return self.index[i] + iloc = self.argmin(axis, skipna, *args, **kwargs) + return self.index[iloc] def idxmax(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashable: """ @@ -2415,8 +2393,8 @@ def idxmax(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab axis : {0 or 'index'} Unused. Parameter needed for compatibility with DataFrame. skipna : bool, default True - Exclude NA/null values. If the entire Series is NA, the result - will be NA. + Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` + and there is an NA value, this method will raise a ``ValueError``. *args, **kwargs Additional arguments and keywords have no effect but might be accepted for compatibility with NumPy. @@ -2459,32 +2437,10 @@ def idxmax(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab >>> s.idxmax() 'C' - - If `skipna` is False and there is an NA value in the data, - the function returns ``nan``. - - >>> s.idxmax(skipna=False) - nan """ axis = self._get_axis_number(axis) - with warnings.catch_warnings(): - # TODO(3.0): this catching/filtering can be removed - # ignore warning produced by argmax since we will issue a different - # warning for argmax - warnings.simplefilter("ignore") - i = self.argmax(axis, skipna, *args, **kwargs) - - if i == -1: - # GH#43587 give correct NA value for Index. - warnings.warn( - f"The behavior of {type(self).__name__}.idxmax with all-NA " - "values, or any-NA and skipna=False, is deprecated. In a future " - "version this will raise ValueError", - FutureWarning, - stacklevel=find_stack_level(), - ) - return self.index._na_value - return self.index[i] + iloc = self.argmax(axis, skipna, *args, **kwargs) + return self.index[iloc] def round(self, decimals: int = 0, *args, **kwargs) -> Series: """ diff --git a/pandas/core/shared_docs.py b/pandas/core/shared_docs.py index 15aa210a09d6d..a2b5439f9e12f 100644 --- a/pandas/core/shared_docs.py +++ b/pandas/core/shared_docs.py @@ -692,8 +692,8 @@ axis : {{0 or 'index', 1 or 'columns'}}, default 0 The axis to use. 0 or 'index' for row-wise, 1 or 'columns' for column-wise. skipna : bool, default True - Exclude NA/null values. If an entire row/column is NA, the result - will be NA. + Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` + and there is an NA value, this method will raise a ``ValueError``. numeric_only : bool, default {numeric_only_default} Include only `float`, `int` or `boolean` data. @@ -757,8 +757,8 @@ axis : {{0 or 'index', 1 or 'columns'}}, default 0 The axis to use. 0 or 'index' for row-wise, 1 or 'columns' for column-wise. skipna : bool, default True - Exclude NA/null values. If an entire row/column is NA, the result - will be NA. + Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` + and there is an NA value, this method will raise a ``ValueError``. numeric_only : bool, default {numeric_only_default} Include only `float`, `int` or `boolean` data. diff --git a/pandas/tests/extension/base/methods.py b/pandas/tests/extension/base/methods.py index c803a8113b4a4..26638c6160b7b 100644 --- a/pandas/tests/extension/base/methods.py +++ b/pandas/tests/extension/base/methods.py @@ -169,8 +169,8 @@ def test_argmin_argmax_all_na(self, method, data, na_value): ("idxmin", True, 2), ("argmax", True, 0), ("argmin", True, 2), - ("idxmax", False, np.nan), - ("idxmin", False, np.nan), + ("idxmax", False, -1), + ("idxmin", False, -1), ("argmax", False, -1), ("argmin", False, -1), ], @@ -179,17 +179,13 @@ def test_argreduce_series( self, data_missing_for_sorting, op_name, skipna, expected ): # data_missing_for_sorting -> [B, NA, A] with A < B and NA missing. - warn = None - msg = "The behavior of Series.argmax/argmin" - if op_name.startswith("arg") and expected == -1: - warn = FutureWarning - if op_name.startswith("idx") and np.isnan(expected): - warn = FutureWarning - msg = f"The behavior of Series.{op_name}" ser = pd.Series(data_missing_for_sorting) - with tm.assert_produces_warning(warn, match=msg): + if expected == -1: + with pytest.raises(ValueError, match="Encountered an NA value"): + getattr(ser, op_name)(skipna=skipna) + else: result = getattr(ser, op_name)(skipna=skipna) - tm.assert_almost_equal(result, expected) + tm.assert_almost_equal(result, expected) def test_argmax_argmin_no_skipna_notimplemented(self, data_missing_for_sorting): # GH#38733 diff --git a/pandas/tests/frame/test_reductions.py b/pandas/tests/frame/test_reductions.py index 63c15fab76562..408cb0ab6fc5c 100644 --- a/pandas/tests/frame/test_reductions.py +++ b/pandas/tests/frame/test_reductions.py @@ -1065,18 +1065,20 @@ def test_idxmin(self, float_frame, int_frame, skipna, axis): frame.iloc[5:10] = np.nan frame.iloc[15:20, -2:] = np.nan for df in [frame, int_frame]: - warn = None - if skipna is False or axis == 1: - warn = None if df is int_frame else FutureWarning - msg = "The behavior of DataFrame.idxmin with all-NA values" - with tm.assert_produces_warning(warn, match=msg): + if (not skipna or axis == 1) and df is not int_frame: + if axis == 1: + msg = "Encountered all NA values" + else: + msg = "Encountered an NA value" + with pytest.raises(ValueError, match=msg): + df.idxmin(axis=axis, skipna=skipna) + with pytest.raises(ValueError, match=msg): + df.idxmin(axis=axis, skipna=skipna) + else: result = df.idxmin(axis=axis, skipna=skipna) - - msg2 = "The behavior of Series.idxmin" - with tm.assert_produces_warning(warn, match=msg2): expected = df.apply(Series.idxmin, axis=axis, skipna=skipna) - expected = expected.astype(df.index.dtype) - tm.assert_series_equal(result, expected) + expected = expected.astype(df.index.dtype) + tm.assert_series_equal(result, expected) @pytest.mark.parametrize("axis", [0, 1]) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") @@ -1113,16 +1115,17 @@ def test_idxmax(self, float_frame, int_frame, skipna, axis): frame.iloc[5:10] = np.nan frame.iloc[15:20, -2:] = np.nan for df in [frame, int_frame]: - warn = None - if skipna is False or axis == 1: - warn = None if df is int_frame else FutureWarning - msg = "The behavior of DataFrame.idxmax with all-NA values" - with tm.assert_produces_warning(warn, match=msg): - result = df.idxmax(axis=axis, skipna=skipna) + if (skipna is False or axis == 1) and df is frame: + if axis == 1: + msg = "Encountered all NA values" + else: + msg = "Encountered an NA value" + with pytest.raises(ValueError, match=msg): + df.idxmax(axis=axis, skipna=skipna) + return - msg2 = "The behavior of Series.idxmax" - with tm.assert_produces_warning(warn, match=msg2): - expected = df.apply(Series.idxmax, axis=axis, skipna=skipna) + result = df.idxmax(axis=axis, skipna=skipna) + expected = df.apply(Series.idxmax, axis=axis, skipna=skipna) expected = expected.astype(df.index.dtype) tm.assert_series_equal(result, expected) @@ -2118,15 +2121,16 @@ def test_numeric_ea_axis_1(method, skipna, min_count, any_numeric_ea_dtype): if method in ("prod", "product", "sum"): kwargs["min_count"] = min_count - warn = None - msg = None if not skipna and method in ("idxmax", "idxmin"): - warn = FutureWarning + # GH#57745 - EAs use groupby for axis=1 which still needs a proper deprecation. msg = f"The behavior of DataFrame.{method} with all-NA values" - with tm.assert_produces_warning(warn, match=msg): - result = getattr(df, method)(axis=1, **kwargs) - with tm.assert_produces_warning(warn, match=msg): - expected = getattr(expected_df, method)(axis=1, **kwargs) + with tm.assert_produces_warning(FutureWarning, match=msg): + getattr(df, method)(axis=1, **kwargs) + with pytest.raises(ValueError, match="Encountered an NA value"): + getattr(expected_df, method)(axis=1, **kwargs) + return + result = getattr(df, method)(axis=1, **kwargs) + expected = getattr(expected_df, method)(axis=1, **kwargs) if method not in ("idxmax", "idxmin"): expected = expected.astype(expected_dtype) tm.assert_series_equal(result, expected) diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index 91ee13ecd87dd..b10319f5380e7 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -128,28 +128,14 @@ def test_nanargminmax(self, opname, index_or_series): obj = klass([NaT, datetime(2011, 11, 1)]) assert getattr(obj, arg_op)() == 1 - msg = ( - "The behavior of (DatetimeIndex|Series).argmax/argmin with " - "skipna=False and NAs" - ) - if klass is Series: - msg = "The behavior of Series.(idxmax|idxmin) with all-NA" - with tm.assert_produces_warning(FutureWarning, match=msg): - result = getattr(obj, arg_op)(skipna=False) - if klass is Series: - assert np.isnan(result) - else: - assert result == -1 + with pytest.raises(ValueError, match="Encountered an NA value"): + getattr(obj, arg_op)(skipna=False) obj = klass([NaT, datetime(2011, 11, 1), NaT]) # check DatetimeIndex non-monotonic path assert getattr(obj, arg_op)() == 1 - with tm.assert_produces_warning(FutureWarning, match=msg): - result = getattr(obj, arg_op)(skipna=False) - if klass is Series: - assert np.isnan(result) - else: - assert result == -1 + with pytest.raises(ValueError, match="Encountered an NA value"): + getattr(obj, arg_op)(skipna=False) @pytest.mark.parametrize("opname", ["max", "min"]) @pytest.mark.parametrize("dtype", ["M8[ns]", "datetime64[ns, UTC]"]) @@ -175,40 +161,38 @@ def test_argminmax(self): obj = Index([np.nan, 1, np.nan, 2]) assert obj.argmin() == 1 assert obj.argmax() == 3 - msg = "The behavior of Index.argmax/argmin with skipna=False and NAs" - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin(skipna=False) == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax(skipna=False) == -1 + with pytest.raises(ValueError, match="Encountered an NA value"): + obj.argmin(skipna=False) + with pytest.raises(ValueError, match="Encountered an NA value"): + obj.argmax(skipna=False) obj = Index([np.nan]) - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin() == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax() == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin(skipna=False) == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax(skipna=False) == -1 + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmin() + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmax() + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmin(skipna=False) + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmax(skipna=False) - msg = "The behavior of DatetimeIndex.argmax/argmin with skipna=False and NAs" obj = Index([NaT, datetime(2011, 11, 1), datetime(2011, 11, 2), NaT]) assert obj.argmin() == 1 assert obj.argmax() == 2 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin(skipna=False) == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax(skipna=False) == -1 + with pytest.raises(ValueError, match="Encountered an NA value"): + obj.argmin(skipna=False) + with pytest.raises(ValueError, match="Encountered an NA value"): + obj.argmax(skipna=False) obj = Index([NaT]) - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin() == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax() == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmin(skipna=False) == -1 - with tm.assert_produces_warning(FutureWarning, match=msg): - assert obj.argmax(skipna=False) == -1 + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmin() + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmax() + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmin(skipna=False) + with pytest.raises(ValueError, match="Encountered all NA values"): + obj.argmax(skipna=False) @pytest.mark.parametrize("op, expected_col", [["max", "a"], ["min", "b"]]) def test_same_tz_min_max_axis_1(self, op, expected_col): @@ -841,26 +825,16 @@ def test_idxmin_dt64index(self, unit): # GH#43587 should have NaT instead of NaN dti = DatetimeIndex(["NaT", "2015-02-08", "NaT"]).as_unit(unit) ser = Series([1.0, 2.0, np.nan], index=dti) - msg = "The behavior of Series.idxmin with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - res = ser.idxmin(skipna=False) - assert res is NaT - msg = "The behavior of Series.idxmax with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - res = ser.idxmax(skipna=False) - assert res is NaT + with pytest.raises(ValueError, match="Encountered an NA value"): + ser.idxmin(skipna=False) + with pytest.raises(ValueError, match="Encountered an NA value"): + ser.idxmax(skipna=False) df = ser.to_frame() - msg = "The behavior of DataFrame.idxmin with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - res = df.idxmin(skipna=False) - assert res.dtype == f"M8[{unit}]" - assert res.isna().all() - msg = "The behavior of DataFrame.idxmax with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - res = df.idxmax(skipna=False) - assert res.dtype == f"M8[{unit}]" - assert res.isna().all() + with pytest.raises(ValueError, match="Encountered an NA value"): + df.idxmin(skipna=False) + with pytest.raises(ValueError, match="Encountered an NA value"): + df.idxmax(skipna=False) def test_idxmin(self): # test idxmin @@ -872,9 +846,8 @@ def test_idxmin(self): # skipna or no assert string_series[string_series.idxmin()] == string_series.min() - msg = "The behavior of Series.idxmin" - with tm.assert_produces_warning(FutureWarning, match=msg): - assert isna(string_series.idxmin(skipna=False)) + with pytest.raises(ValueError, match="Encountered an NA value"): + string_series.idxmin(skipna=False) # no NaNs nona = string_series.dropna() @@ -883,8 +856,8 @@ def test_idxmin(self): # all NaNs allna = string_series * np.nan - with tm.assert_produces_warning(FutureWarning, match=msg): - assert isna(allna.idxmin()) + with pytest.raises(ValueError, match="Encountered all NA values"): + allna.idxmin() # datetime64[ns] s = Series(date_range("20130102", periods=6)) @@ -905,8 +878,7 @@ def test_idxmax(self): # skipna or no assert string_series[string_series.idxmax()] == string_series.max() - msg = "The behavior of Series.idxmax with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): + with pytest.raises(ValueError, match="Encountered an NA value"): assert isna(string_series.idxmax(skipna=False)) # no NaNs @@ -916,9 +888,8 @@ def test_idxmax(self): # all NaNs allna = string_series * np.nan - msg = "The behavior of Series.idxmax with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - assert isna(allna.idxmax()) + with pytest.raises(ValueError, match="Encountered all NA values"): + allna.idxmax() s = Series(date_range("20130102", periods=6)) result = s.idxmax() @@ -1175,12 +1146,12 @@ def test_idxminmax_object_dtype(self, using_infer_string): msg = "'>' not supported between instances of 'float' and 'str'" with pytest.raises(TypeError, match=msg): ser3.idxmax() - with pytest.raises(TypeError, match=msg): + with pytest.raises(ValueError, match="Encountered an NA value"): ser3.idxmax(skipna=False) msg = "'<' not supported between instances of 'float' and 'str'" with pytest.raises(TypeError, match=msg): ser3.idxmin() - with pytest.raises(TypeError, match=msg): + with pytest.raises(ValueError, match="Encountered an NA value"): ser3.idxmin(skipna=False) def test_idxminmax_object_frame(self): @@ -1228,14 +1199,12 @@ def test_idxminmax_with_inf(self): s = Series([0, -np.inf, np.inf, np.nan]) assert s.idxmin() == 1 - msg = "The behavior of Series.idxmin with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - assert np.isnan(s.idxmin(skipna=False)) + with pytest.raises(ValueError, match="Encountered an NA value"): + s.idxmin(skipna=False) assert s.idxmax() == 2 - msg = "The behavior of Series.idxmax with all-NA values" - with tm.assert_produces_warning(FutureWarning, match=msg): - assert np.isnan(s.idxmax(skipna=False)) + with pytest.raises(ValueError, match="Encountered an NA value"): + s.idxmax(skipna=False) def test_sum_uint64(self): # GH 53401 diff --git a/pandas/tests/test_nanops.py b/pandas/tests/test_nanops.py index ed125ece349a9..ce41f1e76de79 100644 --- a/pandas/tests/test_nanops.py +++ b/pandas/tests/test_nanops.py @@ -296,6 +296,7 @@ def check_fun_data( self, testfunc, targfunc, + testar, testarval, targarval, skipna, @@ -319,6 +320,13 @@ def check_fun_data( else: targ = bool(targ) + if testfunc.__name__ in ["nanargmax", "nanargmin"] and ( + testar.startswith("arr_nan") + or (testar.endswith("nan") and (not skipna or axis == 1)) + ): + with pytest.raises(ValueError, match="Encountered .* NA value"): + testfunc(testarval, axis=axis, skipna=skipna, **kwargs) + return res = testfunc(testarval, axis=axis, skipna=skipna, **kwargs) if ( @@ -350,6 +358,7 @@ def check_fun_data( self.check_fun_data( testfunc, targfunc, + testar, testarval2, targarval2, skipna=skipna, @@ -370,6 +379,7 @@ def check_fun( self.check_fun_data( testfunc, targfunc, + testar, testarval, targarval, skipna=skipna,