diff --git a/doc/source/whatsnew/v1.3.2.rst b/doc/source/whatsnew/v1.3.2.rst index 9b412d6d46c5e..c716460e997d0 100644 --- a/doc/source/whatsnew/v1.3.2.rst +++ b/doc/source/whatsnew/v1.3.2.rst @@ -22,7 +22,7 @@ Fixed regressions - Regression in :meth:`DataFrame.drop` does nothing if :class:`MultiIndex` has duplicates and indexer is a tuple or list of tuples (:issue:`42771`) - Fixed regression where :meth:`pandas.read_csv` raised a ``ValueError`` when parameters ``names`` and ``prefix`` were both set to None (:issue:`42387`) - Fixed regression in comparisons between :class:`Timestamp` object and ``datetime64`` objects outside the implementation bounds for nanosecond ``datetime64`` (:issue:`42794`) -- +- Fixed regression in :meth:`.Styler.highlight_min` and :meth:`.Styler.highlight_max` where ``pandas.NA`` was not successfully ignored (:issue:`42650`) .. --------------------------------------------------------------------------- diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 3d6705ed593d2..64b6de5722a56 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -2347,15 +2347,15 @@ def highlight_max( Styler.highlight_quantile: Highlight values defined by a quantile with a style. """ - def f(data: FrameOrSeries, props: str) -> np.ndarray: - return np.where(data == np.nanmax(data.to_numpy()), props, "") - if props is None: props = f"background-color: {color};" # error: Argument 1 to "apply" of "Styler" has incompatible type # "Callable[[FrameOrSeries, str], ndarray]"; expected "Callable[..., Styler]" return self.apply( - f, axis=axis, subset=subset, props=props # type: ignore[arg-type] + partial(_highlight_value, op="max"), # type: ignore[arg-type] + axis=axis, + subset=subset, + props=props, ) def highlight_min( @@ -2398,15 +2398,15 @@ def highlight_min( Styler.highlight_quantile: Highlight values defined by a quantile with a style. """ - def f(data: FrameOrSeries, props: str) -> np.ndarray: - return np.where(data == np.nanmin(data.to_numpy()), props, "") - if props is None: props = f"background-color: {color};" # error: Argument 1 to "apply" of "Styler" has incompatible type # "Callable[[FrameOrSeries, str], ndarray]"; expected "Callable[..., Styler]" return self.apply( - f, axis=axis, subset=subset, props=props # type: ignore[arg-type] + partial(_highlight_value, op="min"), # type: ignore[arg-type] + axis=axis, + subset=subset, + props=props, ) def highlight_between( @@ -2912,6 +2912,16 @@ def _highlight_between( return np.where(g_left & l_right, props, "") +def _highlight_value(data: FrameOrSeries, op: str, props: str) -> np.ndarray: + """ + Return an array of css strings based on the condition of values matching an op. + """ + value = getattr(data, op)(skipna=True) + if isinstance(data, DataFrame): # min/max must be done twice to return scalar + value = getattr(value, op)(skipna=True) + return np.where(data == value, props, "") + + def _bar( data: FrameOrSeries, align: str | float | int | Callable, diff --git a/pandas/tests/io/formats/style/test_highlight.py b/pandas/tests/io/formats/style/test_highlight.py index a681d7c65a190..1b579a43370a2 100644 --- a/pandas/tests/io/formats/style/test_highlight.py +++ b/pandas/tests/io/formats/style/test_highlight.py @@ -2,10 +2,10 @@ import pytest from pandas import ( + NA, DataFrame, IndexSlice, ) -import pandas._testing as tm pytest.importorskip("jinja2") @@ -55,9 +55,7 @@ def test_highlight_minmax_basic(df, f): } if f == "highlight_min": df = -df - with tm.assert_produces_warning(RuntimeWarning): - # All-NaN slice encountered - result = getattr(df.style, f)(axis=1, color="red")._compute().ctx + result = getattr(df.style, f)(axis=1, color="red")._compute().ctx assert result == expected @@ -78,6 +76,26 @@ def test_highlight_minmax_ext(df, f, kwargs): assert result == expected +@pytest.mark.parametrize("f", ["highlight_min", "highlight_max"]) +@pytest.mark.parametrize("axis", [None, 0, 1]) +def test_highlight_minmax_nulls(f, axis): + # GH 42750 + expected = { + (1, 0): [("background-color", "yellow")], + (1, 1): [("background-color", "yellow")], + } + if axis == 1: + expected.update({(2, 1): [("background-color", "yellow")]}) + + if f == "highlight_max": + df = DataFrame({"a": [NA, 1, None], "b": [np.nan, 1, -1]}) + else: + df = DataFrame({"a": [NA, -1, None], "b": [np.nan, -1, 1]}) + + result = getattr(df.style, f)(axis=axis)._compute().ctx + assert result == expected + + @pytest.mark.parametrize( "kwargs", [