Skip to content

Commit b9edc9a

Browse files
authored
REGR: styler.highlight_min/max did not ignore pd.NA and caused error (#42861)
1 parent 9ed9a65 commit b9edc9a

File tree

3 files changed

+41
-13
lines changed

3 files changed

+41
-13
lines changed

doc/source/whatsnew/v1.3.2.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Fixed regressions
2222
- Regression in :meth:`DataFrame.drop` does nothing if :class:`MultiIndex` has duplicates and indexer is a tuple or list of tuples (:issue:`42771`)
2323
- Fixed regression where :meth:`pandas.read_csv` raised a ``ValueError`` when parameters ``names`` and ``prefix`` were both set to None (:issue:`42387`)
2424
- Fixed regression in comparisons between :class:`Timestamp` object and ``datetime64`` objects outside the implementation bounds for nanosecond ``datetime64`` (:issue:`42794`)
25-
-
25+
- Fixed regression in :meth:`.Styler.highlight_min` and :meth:`.Styler.highlight_max` where ``pandas.NA`` was not successfully ignored (:issue:`42650`)
2626

2727
.. ---------------------------------------------------------------------------
2828

pandas/io/formats/style.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -2347,15 +2347,15 @@ def highlight_max(
23472347
Styler.highlight_quantile: Highlight values defined by a quantile with a style.
23482348
"""
23492349

2350-
def f(data: FrameOrSeries, props: str) -> np.ndarray:
2351-
return np.where(data == np.nanmax(data.to_numpy()), props, "")
2352-
23532350
if props is None:
23542351
props = f"background-color: {color};"
23552352
# error: Argument 1 to "apply" of "Styler" has incompatible type
23562353
# "Callable[[FrameOrSeries, str], ndarray]"; expected "Callable[..., Styler]"
23572354
return self.apply(
2358-
f, axis=axis, subset=subset, props=props # type: ignore[arg-type]
2355+
partial(_highlight_value, op="max"), # type: ignore[arg-type]
2356+
axis=axis,
2357+
subset=subset,
2358+
props=props,
23592359
)
23602360

23612361
def highlight_min(
@@ -2398,15 +2398,15 @@ def highlight_min(
23982398
Styler.highlight_quantile: Highlight values defined by a quantile with a style.
23992399
"""
24002400

2401-
def f(data: FrameOrSeries, props: str) -> np.ndarray:
2402-
return np.where(data == np.nanmin(data.to_numpy()), props, "")
2403-
24042401
if props is None:
24052402
props = f"background-color: {color};"
24062403
# error: Argument 1 to "apply" of "Styler" has incompatible type
24072404
# "Callable[[FrameOrSeries, str], ndarray]"; expected "Callable[..., Styler]"
24082405
return self.apply(
2409-
f, axis=axis, subset=subset, props=props # type: ignore[arg-type]
2406+
partial(_highlight_value, op="min"), # type: ignore[arg-type]
2407+
axis=axis,
2408+
subset=subset,
2409+
props=props,
24102410
)
24112411

24122412
def highlight_between(
@@ -2912,6 +2912,16 @@ def _highlight_between(
29122912
return np.where(g_left & l_right, props, "")
29132913

29142914

2915+
def _highlight_value(data: FrameOrSeries, op: str, props: str) -> np.ndarray:
2916+
"""
2917+
Return an array of css strings based on the condition of values matching an op.
2918+
"""
2919+
value = getattr(data, op)(skipna=True)
2920+
if isinstance(data, DataFrame): # min/max must be done twice to return scalar
2921+
value = getattr(value, op)(skipna=True)
2922+
return np.where(data == value, props, "")
2923+
2924+
29152925
def _bar(
29162926
data: FrameOrSeries,
29172927
align: str | float | int | Callable,

pandas/tests/io/formats/style/test_highlight.py

+22-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import pytest
33

44
from pandas import (
5+
NA,
56
DataFrame,
67
IndexSlice,
78
)
8-
import pandas._testing as tm
99

1010
pytest.importorskip("jinja2")
1111

@@ -55,9 +55,7 @@ def test_highlight_minmax_basic(df, f):
5555
}
5656
if f == "highlight_min":
5757
df = -df
58-
with tm.assert_produces_warning(RuntimeWarning):
59-
# All-NaN slice encountered
60-
result = getattr(df.style, f)(axis=1, color="red")._compute().ctx
58+
result = getattr(df.style, f)(axis=1, color="red")._compute().ctx
6159
assert result == expected
6260

6361

@@ -78,6 +76,26 @@ def test_highlight_minmax_ext(df, f, kwargs):
7876
assert result == expected
7977

8078

79+
@pytest.mark.parametrize("f", ["highlight_min", "highlight_max"])
80+
@pytest.mark.parametrize("axis", [None, 0, 1])
81+
def test_highlight_minmax_nulls(f, axis):
82+
# GH 42750
83+
expected = {
84+
(1, 0): [("background-color", "yellow")],
85+
(1, 1): [("background-color", "yellow")],
86+
}
87+
if axis == 1:
88+
expected.update({(2, 1): [("background-color", "yellow")]})
89+
90+
if f == "highlight_max":
91+
df = DataFrame({"a": [NA, 1, None], "b": [np.nan, 1, -1]})
92+
else:
93+
df = DataFrame({"a": [NA, -1, None], "b": [np.nan, -1, 1]})
94+
95+
result = getattr(df.style, f)(axis=axis)._compute().ctx
96+
assert result == expected
97+
98+
8199
@pytest.mark.parametrize(
82100
"kwargs",
83101
[

0 commit comments

Comments
 (0)