diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 3b60085e9fa66..9d0047f734c92 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -883,6 +883,7 @@ Other - Bug in :meth:`DataFrame.__dir__` caused a segfault when using unicode surrogates in a column name (:issue:`25509`) - Bug in :meth:`DataFrame.plot.scatter` caused an error when plotting variable marker sizes (:issue:`32904`) - :class:`IntegerArray` now implements the ``sum`` operation (:issue:`33172`) +- More informative error message with ``np.min`` or ``np.max`` on unordered :class:`Categorical` (:issue:`33115`) - Bug in :class:`Tick` comparisons raising ``TypeError`` when comparing against timedelta-like objects (:issue:`34088`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 2a01ab3802e62..80fe1ac7ce619 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -9,6 +9,7 @@ from pandas._libs import NaT, algos as libalgos, hashtable as htable from pandas._typing import ArrayLike, Dtype, Ordered, Scalar +from pandas.compat.numpy import function as nv from pandas.util._decorators import cache_readonly, deprecate_kwarg, doc from pandas.util._validators import validate_bool_kwarg, validate_fillna_kwargs @@ -2077,7 +2078,7 @@ def _reduce(self, name, axis=0, **kwargs): return func(**kwargs) @deprecate_kwarg(old_arg_name="numeric_only", new_arg_name="skipna") - def min(self, skipna=True): + def min(self, skipna=True, **kwargs): """ The minimum value of the object. @@ -2096,6 +2097,7 @@ def min(self, skipna=True): ------- min : the minimum of this `Categorical` """ + nv.validate_min((), kwargs) self.check_for_ordered("min") if not len(self._codes): @@ -2112,7 +2114,7 @@ def min(self, skipna=True): return self.categories[pointer] @deprecate_kwarg(old_arg_name="numeric_only", new_arg_name="skipna") - def max(self, skipna=True): + def max(self, skipna=True, **kwargs): """ The maximum value of the object. @@ -2131,6 +2133,7 @@ def max(self, skipna=True): ------- max : the maximum of this `Categorical` """ + nv.validate_max((), kwargs) self.check_for_ordered("max") if not len(self._codes): diff --git a/pandas/tests/arrays/categorical/test_analytics.py b/pandas/tests/arrays/categorical/test_analytics.py index 83ecebafbea07..4bf9b4b40d0b6 100644 --- a/pandas/tests/arrays/categorical/test_analytics.py +++ b/pandas/tests/arrays/categorical/test_analytics.py @@ -1,3 +1,4 @@ +import re import sys import numpy as np @@ -93,6 +94,37 @@ def test_deprecate_numeric_only_min_max(self, method): with tm.assert_produces_warning(expected_warning=FutureWarning): getattr(cat, method)(numeric_only=True) + @pytest.mark.parametrize("method", ["min", "max"]) + def test_numpy_min_max_raises(self, method): + cat = Categorical(["a", "b", "c", "b"], ordered=False) + msg = ( + f"Categorical is not ordered for operation {method}\n" + "you can use .as_ordered() to change the Categorical to an ordered one" + ) + method = getattr(np, method) + with pytest.raises(TypeError, match=re.escape(msg)): + method(cat) + + @pytest.mark.parametrize("kwarg", ["axis", "out", "keepdims"]) + @pytest.mark.parametrize("method", ["min", "max"]) + def test_numpy_min_max_unsupported_kwargs_raises(self, method, kwarg): + cat = Categorical(["a", "b", "c", "b"], ordered=True) + msg = ( + f"the '{kwarg}' parameter is not supported in the pandas implementation " + f"of {method}" + ) + kwargs = {kwarg: 42} + method = getattr(np, method) + with pytest.raises(ValueError, match=msg): + method(cat, **kwargs) + + @pytest.mark.parametrize("method, expected", [("min", "a"), ("max", "c")]) + def test_numpy_min_max_axis_equals_none(self, method, expected): + cat = Categorical(["a", "b", "c", "b"], ordered=True) + method = getattr(np, method) + result = method(cat, axis=None) + assert result == expected + @pytest.mark.parametrize( "values,categories,exp_mode", [