Skip to content
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

Styler class should have a pipe() method, akin to DataFrame.pipe() #23229

Closed
nmusolino opened this issue Oct 18, 2018 · 3 comments · Fixed by #23384
Closed

Styler class should have a pipe() method, akin to DataFrame.pipe() #23229

nmusolino opened this issue Oct 18, 2018 · 3 comments · Fixed by #23384
Labels
Code Style Code style, linting, code_checks Enhancement

Comments

@nmusolino
Copy link
Contributor

nmusolino commented Oct 18, 2018

Problem description

Users may want to create functions that apply customized styling to a dataframe or styler object.

In order to make these functions composable, a convenient signature is f(styler) -> Styler, so that a user can chain multiple such functions together, in a "mix-and-match" fashion. (N.B. Such functions can obtain the underlying dataframe using the styler.data attribute.)

A method Styler.pipe() would provide an ergonomic way to apply these functions sequentially, after a series of Dataframe-related manipulations.

Usage example:

def right_align(styler):
    return styler.set_properties(**{'text-align': 'right'})

def highlight_rows_gt(styler, colname, threshold):
    return ...        # implementation omitted

>>> (df.groupby('A')[['B', 'C']].sum()
...        .assign(D=lambda x: x['B'] / x['C'])
...        .style
...        .format('{:.2f}')
...        .pipe(highlight_rows_gt, colname='D', threshold=0.95)
...        .pipe(right_align))
@WillAyd WillAyd added Enhancement Code Style Code style, linting, code_checks labels Oct 24, 2018
@gfyoung
Copy link
Member

gfyoung commented Oct 27, 2018

I think this proposition seems reasonable, unless there is some workaround already.

cc @jreback @TomAugspurger

@nmusolino
Copy link
Contributor Author

Thanks very much, @gfyoung, for the feedback and the review on the corresponding pull request (#23384).

I can elaborate on the two major use cases I had in mind for this:
I. An explicit goal for the style functionality is extensibility. As noted above, users (or library developers) can write functions that perform certain style transformations, possibly application-specific, using the core Styler functions in pandas. The pipe() method would provide an ergonomic way to compose or "mix-and-match" these user-defined transformations.

II. As a variant of (I), users may find themselves attempting to style many different views of data in a consistent way within a notebook. For example, users might want to present two different kinds of results (e.g. summary results and detailed results) while grouping by three different columns in their data frame, for a total of six tables being created/styled. In that case, the Styler.pipe() method can help reduce the "style boilerplate": a user can place all the common styling operations in a function within the notebook (or elsewhere), and perform specific styling operations (such as captions, or highlighting specific data problems) in each individual cell.

Here's an example (untested code). Note that in general, there may be a series of operations performed on a data frame to get the final result frame, before any styling is applied.

def format_summary(styler, grouping_text):
    return (styler.set_properties(**{'text-align': 'right'})
                  .format({'N': '{:,d}', 'ResponseRate': '{:.1%}', 'Date': '{:%Y-%m-%d}')
                  .set_caption("Summary of results, grouped by {}.".format(grouping_text)))   

# Notebook cell
(df.groupby('A').apply(calculate_summary)
   .style.pipe(format_summary, grouping_text="attribute A"))

# Notebook cell
(df.groupby('B').apply(calculate_summary)
   .style.pipe(format_summary, grouping_text="attribute B"))

# Notebook cell.  This was a special case, so apply extra styling to explain details to readers.
(df.groupby('C').apply(calculate_summary)
   .style.pipe(format_summary, grouping_text="C"))
   .set_caption("Summary of results, grouped by 'C'.  Note that the group with C equal to 4 had insufficient number of samples to estimate results.")
   .apply(lambda row: ['background-color: yellow'] * len(row), subset=pandas.IndexSlice[4, :], axis=0))

@gfyoung
Copy link
Member

gfyoung commented Oct 28, 2018

@nmusolino : Thanks for the explanation! Now if you could take some of this and put it in the whatsnew, that would be great. 👍

TomAugspurger pushed a commit that referenced this issue Nov 28, 2018
* Add Styler.pipe() method, akin to DataFrame.pipe()
Pingviinituutti pushed a commit to Pingviinituutti/pandas that referenced this issue Feb 28, 2019
* Add Styler.pipe() method, akin to DataFrame.pipe()
Pingviinituutti pushed a commit to Pingviinituutti/pandas that referenced this issue Feb 28, 2019
* Add Styler.pipe() method, akin to DataFrame.pipe()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Code Style Code style, linting, code_checks Enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants