From fa9c8d5989f320b3f372e1354c5c725fe8cfe1ed Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Fri, 5 Mar 2021 08:05:48 +0100 Subject: [PATCH 1/4] ENH: align args signature with other highlight methods. --- pandas/io/formats/style.py | 78 ++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index e50f5986098d3..49775d38bd1af 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -928,7 +928,7 @@ def apply( Examples -------- >>> def highlight_max(x, color): - ... return np.where(x == np.nanmax(x.values), f"color: {color};", None) + ... return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None) >>> df = pd.DataFrame(np.random.randn(5, 2)) >>> df.style.apply(highlight_max, color='red') >>> df.style.apply(highlight_max, color='blue', axis=1) @@ -1623,9 +1623,10 @@ def highlight_null( self, null_color: str = "red", subset: Optional[IndexLabel] = None, + props: Optional[str] = None, ) -> Styler: """ - Shade the background ``null_color`` for missing values. + Highlight missing values with a style. Parameters ---------- @@ -1635,79 +1636,124 @@ def highlight_null( .. versionadded:: 1.1.0 + props : str, default None + CSS properties to use for highlighting. If ``props`` is given, ``color`` + is not used. + + .. versionadded:: 1.3.0 + Returns ------- self : Styler + + See Also + -------- + Styler.highlight_max: Highlight the maximum with a style. + Styler.highlight_min: Highlight the minimum with a style. + Styler.highlight_quantile: Highlight values defined by a quantile with a style. + Styler.highlight_between: Highlight a defined range with a style. + + Notes + ----- + Uses ``pandas.isna()`` to detect the missing values. """ def f(data: DataFrame, props: str) -> np.ndarray: - return np.where(pd.isna(data).values, props, "") + return np.where(pd.isna(data).to_numpy(), props, "") - return self.apply( - f, axis=None, subset=subset, props=f"background-color: {null_color};" - ) + if props is None: + props = f"background-color: {null_color};" + return self.apply(f, axis=None, subset=subset, props=props) def highlight_max( self, subset: Optional[IndexLabel] = None, color: str = "yellow", axis: Optional[Axis] = 0, + props: Optional[str] = None, ) -> Styler: """ - Highlight the maximum by shading the background. + Highlight the maximum with a style. Parameters ---------- subset : IndexSlice, default None A valid slice for ``data`` to limit the style application to. color : str, default 'yellow' + Background color to use for highlighting. axis : {0 or 'index', 1 or 'columns', None}, default 0 Apply to each column (``axis=0`` or ``'index'``), to each row (``axis=1`` or ``'columns'``), or to the entire DataFrame at once with ``axis=None``. + props : str, default None + CSS properties to use for highlighting. If ``props`` is given, ``color`` + is not used. + + .. versionadded:: 1.3.0 Returns ------- self : Styler + + See Also + -------- + Styler.highlight_null: Highlight missing values with a style. + Styler.highlight_min: Highlight the minimum with a style. + Styler.highlight_quantile: Highlight values defined by a quantile with a style. + Styler.highlight_between: Highlight a defined range with a style. """ def f(data: FrameOrSeries, props: str) -> np.ndarray: - return np.where(data == np.nanmax(data.values), props, "") + return np.where(data == np.nanmax(data.to_numpy()), props, "") - return self.apply( - f, axis=axis, subset=subset, props=f"background-color: {color};" - ) + if props is None: + props = f"background-color: {color};" + return self.apply(f, axis=axis, subset=subset, props=props) def highlight_min( self, subset: Optional[IndexLabel] = None, color: str = "yellow", axis: Optional[Axis] = 0, + props: Optional[str] = None, ) -> Styler: """ - Highlight the minimum by shading the background. + Highlight the minimum with a style. Parameters ---------- subset : IndexSlice, default None A valid slice for ``data`` to limit the style application to. color : str, default 'yellow' + Background color to use for highlighting. axis : {0 or 'index', 1 or 'columns', None}, default 0 Apply to each column (``axis=0`` or ``'index'``), to each row (``axis=1`` or ``'columns'``), or to the entire DataFrame at once with ``axis=None``. + props : str, default None + CSS properties to use for highlighting. If ``props`` is given, ``color`` + is not used. + + .. versionadded:: 1.3.0 Returns ------- self : Styler + + See Also + -------- + Styler.highlight_null: Highlight missing values with a style. + Styler.highlight_max: Highlight the maximum with a style. + Styler.highlight_quantile: Highlight values defined by a quantile with a style. + Styler.highlight_between: Highlight a defined range with a style. """ def f(data: FrameOrSeries, props: str) -> np.ndarray: - return np.where(data == np.nanmin(data.values), props, "") + return np.where(data == np.nanmin(data.to_numpy()), props, "") - return self.apply( - f, axis=axis, subset=subset, props=f"background-color: {color};" - ) + if props is None: + props = f"background-color: {color};" + return self.apply(f, axis=axis, subset=subset, props=props) @classmethod def from_custom_template(cls, searchpath, name): From c4d799d3fad93106c403f7c7b183166172f3dc2a Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Fri, 5 Mar 2021 08:15:34 +0100 Subject: [PATCH 2/4] ENH: align args signature with other highlight methods. --- doc/source/reference/style.rst | 2 +- .../tests/io/formats/style/test_highlight.py | 73 +++++++++---------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/doc/source/reference/style.rst b/doc/source/reference/style.rst index 3a8d912fa6ffe..9dcdcb86c2ea4 100644 --- a/doc/source/reference/style.rst +++ b/doc/source/reference/style.rst @@ -53,9 +53,9 @@ Builtin styles .. autosummary:: :toctree: api/ + Styler.highlight_null Styler.highlight_max Styler.highlight_min - Styler.highlight_null Styler.background_gradient Styler.bar diff --git a/pandas/tests/io/formats/style/test_highlight.py b/pandas/tests/io/formats/style/test_highlight.py index e02e1f012c662..7cf958be11392 100644 --- a/pandas/tests/io/formats/style/test_highlight.py +++ b/pandas/tests/io/formats/style/test_highlight.py @@ -7,6 +7,11 @@ class TestStylerHighlight: + def setup_method(self, method): + np.random.seed(24) + self.s = DataFrame({"A": np.random.permutation(range(6))}) + self.df = DataFrame({"A": [0, 1], "B": np.random.randn(2)}) + def test_highlight_null(self): df = DataFrame({"A": [0, np.nan]}) result = df.style.highlight_null()._compute().ctx @@ -28,43 +33,33 @@ def test_highlight_null_subset(self): } assert result == expected - def test_highlight_max(self): - df = DataFrame([[1, 2], [3, 4]], columns=["A", "B"]) - css_seq = [("background-color", "yellow")] - # max(df) = min(-df) - for max_ in [True, False]: - if max_: - attr = "highlight_max" - else: - df = -df - attr = "highlight_min" - result = getattr(df.style, attr)()._compute().ctx - assert result[(1, 1)] == css_seq - - result = getattr(df.style, attr)(color="green")._compute().ctx - assert result[(1, 1)] == [("background-color", "green")] - - result = getattr(df.style, attr)(subset="A")._compute().ctx - assert result[(1, 0)] == css_seq - - result = getattr(df.style, attr)(axis=0)._compute().ctx - expected = { - (1, 0): css_seq, - (1, 1): css_seq, - } - assert result == expected - - result = getattr(df.style, attr)(axis=1)._compute().ctx - expected = { - (0, 1): css_seq, - (1, 1): css_seq, - } - assert result == expected - - # separate since we can't negate the strs - df["C"] = ["a", "b"] - result = df.style.highlight_max()._compute().ctx - expected = {(1, 1): css_seq} + @pytest.mark.parametrize("f", ["highlight_min", "highlight_max"]) + def test_highlight_minmax_basic(self, f): + expected = { + (0, 0): [("background-color", "red")], + (1, 0): [("background-color", "red")], + } + if f == "highlight_min": + df = -self.df + else: + df = self.df + result = getattr(df.style, f)(axis=1, color="red")._compute().ctx + assert result == expected - result = df.style.highlight_min()._compute().ctx - expected = {(0, 0): css_seq} + @pytest.mark.parametrize("f", ["highlight_min", "highlight_max"]) + @pytest.mark.parametrize( + "kwargs", + [ + {"axis": None, "color": "red"}, # test axis + {"axis": 0, "subset": ["A"], "color": "red"}, # test subset + {"axis": None, "props": "background-color: red"}, # test props + ], + ) + def test_highlight_minmax_ext(self, f, kwargs): + expected = {(1, 0): [("background-color", "red")]} + if f == "highlight_min": + df = -self.df + else: + df = self.df + result = getattr(df.style, f)(**kwargs)._compute().ctx + assert result == expected From cbb16d9a6439dfd43f146f5af7054ddfa8cd8358 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Fri, 5 Mar 2021 12:17:02 +0100 Subject: [PATCH 3/4] whats new --- doc/source/whatsnew/v1.3.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 5c61f259a4202..37e22eb90c0aa 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -135,6 +135,7 @@ Other enhancements - :meth:`.Styler.set_tooltips_class` and :meth:`.Styler.set_table_styles` amended to optionally allow certain css-string input arguments (:issue:`39564`) - :meth:`.Styler.apply` now more consistently accepts ndarray function returns, i.e. in all cases for ``axis`` is ``0, 1 or None`` (:issue:`39359`) - :meth:`.Styler.apply` and :meth:`.Styler.applymap` now raise errors if wrong format CSS is passed on render (:issue:`39660`) +- Builtin highlighting methods in :class:`Styler` have a more consistent signature and css customisability (:issue:`40242`) - :meth:`Series.loc.__getitem__` and :meth:`Series.loc.__setitem__` with :class:`MultiIndex` now raising helpful error message when indexer has too many dimensions (:issue:`35349`) - :meth:`pandas.read_stata` and :class:`StataReader` support reading data from compressed files. - Add support for parsing ``ISO 8601``-like timestamps with negative signs to :meth:`pandas.Timedelta` (:issue:`37172`) From e131b938d95e9e3ca678fcee40a4621fe98441a3 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Fri, 5 Mar 2021 18:19:17 +0100 Subject: [PATCH 4/4] remove unwanted docstring --- pandas/io/formats/style.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index ec71a8457d1df..1744a7c8970a6 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -1663,12 +1663,6 @@ def highlight_null( -------- Styler.highlight_max: Highlight the maximum with a style. Styler.highlight_min: Highlight the minimum with a style. - Styler.highlight_quantile: Highlight values defined by a quantile with a style. - Styler.highlight_between: Highlight a defined range with a style. - - Notes - ----- - Uses ``pandas.isna()`` to detect the missing values. """ def f(data: DataFrame, props: str) -> np.ndarray: @@ -1712,8 +1706,6 @@ def highlight_max( -------- Styler.highlight_null: Highlight missing values with a style. Styler.highlight_min: Highlight the minimum with a style. - Styler.highlight_quantile: Highlight values defined by a quantile with a style. - Styler.highlight_between: Highlight a defined range with a style. """ def f(data: FrameOrSeries, props: str) -> np.ndarray: @@ -1757,8 +1749,6 @@ def highlight_min( -------- Styler.highlight_null: Highlight missing values with a style. Styler.highlight_max: Highlight the maximum with a style. - Styler.highlight_quantile: Highlight values defined by a quantile with a style. - Styler.highlight_between: Highlight a defined range with a style. """ def f(data: FrameOrSeries, props: str) -> np.ndarray: