Skip to content

ENH: Add Styler.pipe() method (#23229) #23384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2482,6 +2482,7 @@ Style Application
Styler.set_properties
Styler.set_uuid
Styler.clear
Styler.pipe

Builtin Styles
~~~~~~~~~~~~~~
Expand Down
26 changes: 26 additions & 0 deletions doc/source/whatsnew/v0.24.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@ array, but rather an ``ExtensionArray``:
This is the same behavior as ``Series.values`` for categorical data. See
:ref:`whatsnew_0240.api_breaking.interval_values` for more.


.. _whatsnew_0240.enhancements.styler_pipe:

New ``Styler.pipe()`` method
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :class:`~pandas.io.formats.style.Styler` class has gained a
:meth:`~pandas.io.formats.style.Styler.pipe` method (:issue:`23229`). This provides a
convenient way to apply users' predefined styling functions, and can help reduce
"boilerplate" when using DataFrame styling functionality repeatedly within a notebook.

.. ipython:: python

df = pandas.DataFrame({'N': [1250, 1500, 1750], 'X': [0.25, 0.35, 0.50]})

def format_and_align(styler):
return (styler.format({'N': '{:,}', 'X': '{:.1%}'})
.set_properties(**{'text-align': 'right'}))

df.style.pipe(format_and_align).set_caption('Summary of results.')

Similar methods already exist for other classes in pandas, including :meth:`DataFrame.pipe`,
:meth:`Groupby.pipe`, and :meth:`Resampler.pipe`.


.. _whatsnew_0240.enhancements.join_with_two_multiindexes:

Joining with two multi-indexes
Expand Down Expand Up @@ -225,6 +249,7 @@ For earlier versions this can be done using the following.
pd.merge(left.reset_index(), right.reset_index(),
on=['key'], how='inner').set_index(['key', 'X', 'Y'])


.. _whatsnew_0240.enhancements.rename_axis:

Renaming names in a MultiIndex
Expand All @@ -248,6 +273,7 @@ Example:

See the :ref:`advanced docs on renaming<advanced.index_names>` for more details.


.. _whatsnew_0240.enhancements.other:

Other Enhancements
Expand Down
69 changes: 69 additions & 0 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,75 @@ class MyStyler(cls):

return MyStyler

def pipe(self, func, *args, **kwargs):
"""
Apply ``func(self, *args, **kwargs)``, and return the result.

.. versionadded:: 0.24.0

Parameters
----------
func : function
Function to apply to the Styler. Alternatively, a
``(callable, keyword)`` tuple where ``keyword`` is a string
indicating the keyword of ``callable`` that expects the Styler.
*args, **kwargs :
Arguments passed to `func`.

Returns
-------
object :
The value returned by ``func``.

See Also
--------
DataFrame.pipe : Analogous method for DataFrame.
Styler.apply : Apply a function row-wise, column-wise, or table-wise to
modify the dataframe's styling.

Notes
-----
Like :meth:`DataFrame.pipe`, this method can simplify the
application of several user-defined functions to a styler. Instead
of writing:

.. code-block:: python

f(g(df.style.set_precision(3), arg1=a), arg2=b, arg3=c)

users can write:

.. code-block:: python

(df.style.set_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.

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."))
"""
return com._pipe(self, func, *args, **kwargs)


def _is_visible(idx_row, idx_col, lengths):
"""
Expand Down
16 changes: 16 additions & 0 deletions pandas/tests/io/formats/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,22 @@ def test_hide_columns_mult_levels(self):
assert ctx['body'][1][2]['is_visible']
assert ctx['body'][1][2]['display_value'] == 3

def test_pipe(self):
def set_caption_from_template(styler, a, b):
return styler.set_caption(
'Dataframe with a = {a} and b = {b}'.format(a=a, b=b))

styler = self.df.style.pipe(set_caption_from_template, 'A', b='B')
assert 'Dataframe with a = A and b = B' in styler.render()

# Test with an argument that is a (callable, keyword_name) pair.
def f(a, b, styler):
return (a, b, styler)

styler = self.df.style
result = styler.pipe((f, 'styler'), a=1, b=2)
assert result == (1, 2, styler)


@td.skip_if_no_mpl
class TestStylerMatplotlibDep(object):
Expand Down