diff --git a/doc/source/_static/style/df_pipe_applydata.png b/doc/source/_static/style/df_pipe_applydata.png new file mode 100644 index 0000000000000..a2d5aa514e311 Binary files /dev/null and b/doc/source/_static/style/df_pipe_applydata.png differ diff --git a/doc/source/_static/style/df_pipe_applymap.png b/doc/source/_static/style/df_pipe_applymap.png new file mode 100644 index 0000000000000..cd493c78452ef Binary files /dev/null and b/doc/source/_static/style/df_pipe_applymap.png differ diff --git a/doc/source/_static/style/df_pipe_hl.png b/doc/source/_static/style/df_pipe_hl.png new file mode 100644 index 0000000000000..2238a55ab1ce3 Binary files /dev/null and b/doc/source/_static/style/df_pipe_hl.png differ diff --git a/doc/source/_static/style/df_pipe_hl2.png b/doc/source/_static/style/df_pipe_hl2.png new file mode 100644 index 0000000000000..7025a7c373d92 Binary files /dev/null and b/doc/source/_static/style/df_pipe_hl2.png differ diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 8e3e35a510c58..9d0b213e44671 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -3509,41 +3509,85 @@ def pipe(self, func: Callable, *args, **kwargs): .. code-block:: python - f(g(df.style.set_precision(3), arg1=a), arg2=b, arg3=c) + f(g(df.style.format(precision=3), arg1=a), arg2=b, arg3=c) users can write: .. code-block:: python - (df.style.set_precision(3) + (df.style.format(precision=3) .pipe(g, arg1=a) .pipe(f, arg2=b, arg3=c)) In particular, this allows users to define functions that take a styler object, along with other parameters, and return the styler after making styling changes (such as calling :meth:`Styler.apply` or - :meth:`Styler.set_properties`). Using ``.pipe``, these user-defined - style "transformations" can be interleaved with calls to the built-in - Styler interface. + :meth:`Styler.set_properties`). Examples -------- - >>> def format_conversion(styler): - ... return (styler.set_properties(**{'text-align': 'right'}) - ... .format({'conversion': '{:.1%}'})) - - The user-defined ``format_conversion`` function above can be called - within a sequence of other style modifications: - - >>> df = pd.DataFrame({'trial': list(range(5)), - ... 'conversion': [0.75, 0.85, np.nan, 0.7, 0.72]}) - >>> (df.style - ... .highlight_min(subset=['conversion'], color='yellow') - ... .pipe(format_conversion) - ... .set_caption("Results with minimum conversion highlighted.")) - ... # doctest: +SKIP - .. figure:: ../../_static/style/df_pipe.png + **Common Use** + + A common usage pattern is to pre-define styling operations which + can be easily applied to a generic styler in a single ``pipe`` call. + + >>> def some_highlights(styler, min_color="red", max_color="blue"): + ... styler.highlight_min(color=min_color, axis=None) + ... styler.highlight_max(color=max_color, axis=None) + ... styler.highlight_null() + ... return styler + >>> df = pd.DataFrame([[1, 2, 3, pd.NA], [pd.NA, 4, 5, 6]], dtype="Int64") + >>> df.style.pipe(some_highlights, min_color="green") # doctest: +SKIP + + .. figure:: ../../_static/style/df_pipe_hl.png + + Since the method returns a ``Styler`` object it can be chained with other + methods as if applying the underlying highlighters directly. + + >>> df.style.format("{:.1f}") + ... .pipe(some_highlights, min_color="green") + ... .highlight_between(left=2, right=5) # doctest: +SKIP + + .. figure:: ../../_static/style/df_pipe_hl2.png + + **Advanced Use** + + Sometimes it may be necessary to pre-define styling functions, but in the case + where those functions rely on the styler, data or context. Since + ``Styler.use`` and ``Styler.export`` are designed to be non-data dependent, + they cannot be used for this purpose. Additionally the ``Styler.apply`` + and ``Styler.format`` type methods are not context aware, so a solution + is to use ``pipe`` to dynamically wrap this functionality. + + Suppose we want to code a generic styling function that highlights the final + level of a MultiIndex. The number of levels in the Index is dynamic so we + need the ``Styler`` context to define the level. + + >>> def highlight_last_level(styler): + ... return styler.apply_index( + ... lambda v: "background-color: pink; color: yellow", axis="columns", + ... level=styler.columns.nlevels-1 + ... ) # doctest: +SKIP + >>> df.columns = pd.MultiIndex.from_product([["A", "B"], ["X", "Y"]]) + >>> df.style.pipe(highlight_last_level) # doctest: +SKIP + + .. figure:: ../../_static/style/df_pipe_applymap.png + + Additionally suppose we want to highlight a column header if there is any + missing data in that column. + In this case we need the data object itself to determine the effect on the + column headers. + + >>> def highlight_header_missing(styler, level): + ... def dynamic_highlight(s): + ... return np.where( + ... styler.data.isna().any(), "background-color: red;", "" + ... ) + ... return styler.apply_index(dynamic_highlight, axis=1, level=level) + >>> df.style.pipe(highlight_header_missing, level=1) # doctest: +SKIP + + .. figure:: ../../_static/style/df_pipe_applydata.png """ return com.pipe(self, func, *args, **kwargs)