Skip to content

ENH: Styler column and row styles #35607

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 23 commits into from
Nov 24, 2020

Conversation

attack68
Copy link
Contributor

@attack68 attack68 commented Aug 7, 2020

@attack68 attack68 changed the title Styler column style enh ENH: Styler column styles Aug 7, 2020
Copy link
Contributor

@jreback jreback left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am not sure we want to be adding api like this, rather can you use a keyword arg to .set_table_styles?

@jreback jreback added the Styler conditional formatting using DataFrame.style label Aug 7, 2020
@attack68
Copy link
Contributor Author

attack68 commented Aug 7, 2020

i am not sure we want to be adding api like this, rather can you use a keyword arg to .set_table_styles?

One of the problems with set_table_styles is that it is a one-time function. Any repeated activity will simply overwrite previous styling, so the user is required to custom build up all style functions and then add at the end. No column argument will avoid this complexity.

One of the functions in this commit is literally just an extension, extend_table_styles which doesn't overwrite. I think this will be necessary then even if an argument solution to the problem is sought or not.

I could modify the code to accept an argument column into extend_table_styles, which repeats the code from the secondary function:

def extend_table_styles(table_styles, column=None):
    if column is not None:
        _styles = []
        for s in table_styles:
            c = str(self.data.columns.get_loc(col))
            _styles.append(
                    {"selector": s["selector"] + ".col" + c, "props": s["props"]}
            )
    else:
        _styles = table_styles
    if self.table_styles is None:
            return self.set_table_styles(table_styles)
    self.table_styles.extend(table_styles)
    return self

This would work for a single column and could be integrated into a user loop over different columns.

##Discussion

But since the purpose of Styler is to provide display capability, and it currently does not offer the ability to directly attach named CSS classes to cells, or rows or columns, I do not understand why you would shy away from providing the functionality to directly style a column in the most efficient manner. This can be extended to rows later.

It seems to be quite a basic functionality that has been requested in other instances that is easily crafted with current tools, and the direct purpose of styling columns is quite an easy user functionality to grasp???

@attack68
Copy link
Contributor Author

attack68 commented Aug 7, 2020

I had a think on your comment @jreback. The addition of multiple arguments to the set_table_styles method allowed all of this functionality to be rolled up into the same function, which I think is what you are after, and in hindsight I think it is better also.

It also had the advantage of being able to address row styling at the same time as column styling.

Tests and examples have been amended.

@attack68 attack68 force-pushed the styler_column_style_enh branch from f67f6bb to ed50068 Compare August 14, 2020 07:04
@attack68
Copy link
Contributor Author

conversations/changes resolved or comments added, all green, @jreback .. ping.

@TomAugspurger
Copy link
Contributor

TomAugspurger commented Aug 17, 2020 via email

@attack68 attack68 requested a review from jreback August 18, 2020 09:38
@attack68 attack68 force-pushed the styler_column_style_enh branch 2 times, most recently from d78f198 to 25a1d7d Compare August 26, 2020 06:05
@attack68 attack68 force-pushed the styler_column_style_enh branch from 25a1d7d to 08fc864 Compare August 28, 2020 07:49
@attack68
Copy link
Contributor Author

@jreback reviving this after a couple of weeks, all green, and changes requested implemented.

@attack68 attack68 changed the title ENH: Styler column styles ENH: Styler column and row styles Sep 2, 2020
…le_enh

# Conflicts:
#	doc/source/whatsnew/v1.2.0.rst
Copy link
Contributor

@TomAugspurger TomAugspurger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restarted the failed build.

@attack68
Copy link
Contributor Author

@jreback keeping this one alive...

@jreback jreback added this to the 1.2 milestone Sep 13, 2020
@jreback
Copy link
Contributor

jreback commented Sep 13, 2020

can you merge master. I understand what overwrite means here, but we don't actually use this anywhere else in pandas.

I think it might be better to use inplace (though not a fan of this either), and prefer chaining to append.

cc @TomAugspurger

@attack68
Copy link
Contributor Author

This is a strange one.

The problem with inplace is that Pandas typically have object methods which return new objects, and the underlying object is unaffected. If inplace is True then the underlying object is permanently affected and the return is None.

In Styler, this is not really how it was designed or works. I would say there are 6 possibilities for how this method might work. For set_table_styles(..):

i) The underlying Styler is unaffected and a new Styler is returned with only the argument attributes set.
ii) The underlying Styler is affected with only the argument attributes set and a self Styler returned .
iii) The underlying Styler is affected with only the argument attributes set and no return value.
iv) The underlying Styler is unaffected and a new Styler is returned with the new attributes extended to the underlying.
v) The underlying Styler is affected with new attributes extended and a self Styler is returned.
vi) The underlying Styler is affected with new attributes extended to the underlying and no return value.

Currently, Styler has no methods that ever do i) or iv). All methods always seem to update itself inplace.

Also Styler never returns None, so this would seem to rule out iii) and vi).

set_table_styles actually does ii) which is a kind of inplace=True with a return, which seems quite unconventional for Pandas. That is why I don't think the inplace argument really works here. And to get the additional behaviour of v) which is what we want left the only other options of either: adding the overwrite argument (see here.. https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.combine.html), or creating another method: extend_table_styles or add_table_styles would be better??

FYI part of the design structure I think was to maintain a self contained object as a property: df.style which then developed the ability to be manipulated but always inplace by design.

@TomAugspurger
Copy link
Contributor

Fixed the merge conflict in the whatsnew. I think this was OK last I looked.

@github-actions
Copy link
Contributor

This pull request is stale because it has been open for thirty days with no activity. Please update or respond to this comment if you're still interested in working on this.

@github-actions github-actions bot added the Stale label Oct 30, 2020
…le_enh

# Conflicts:
#	doc/source/user_guide/style.ipynb
#	doc/source/whatsnew/v1.2.0.rst
@attack68
Copy link
Contributor Author

attack68 commented Nov 5, 2020

I merged master into this again.

Only outstanding issue is acceptance, or not, of the override keyword.

I stand by this, rather than inplace, which I think is misplaced here, as explained above.

Copy link
Contributor

@jreback jreback left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok small comment, pls add a whatsnew note in 1.2, merge master and ping on green.

@@ -987,34 +987,95 @@ def set_caption(self, caption: str) -> "Styler":
self.caption = caption
return self

def set_table_styles(self, table_styles) -> "Styler":
def set_table_styles(self, table_styles, axis=0, overwrite=True) -> "Styler":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you type table_styles

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried adding the following to func def, but it got errors, I don't know my way around mypy and this seems slightly more complicated than normal with the multiple acceptable input types and programming logic:

table_styles: Union[
        List[Dict[str, Union[str, List[Tuple[str, str]]]]],
        Dict[
            Union[str, Tuple[Any, ...]],
            List[Dict[str, Union[str, List[Tuple[str, str]]]]],
        ],
    ] = []

So I reverted the attempt.

However, all green, pinging @jreback

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok if you can follow and make this much more intuitive (e.g. define aliases here)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it is not-trivial to actually construct this union

Copy link
Contributor

@jreback jreback left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok this is fine. pls followup with a typing PR as the table_styles is very confusing w/o it

@@ -987,34 +987,95 @@ def set_caption(self, caption: str) -> "Styler":
self.caption = caption
return self

def set_table_styles(self, table_styles) -> "Styler":
def set_table_styles(self, table_styles, axis=0, overwrite=True) -> "Styler":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok if you can follow and make this much more intuitive (e.g. define aliases here)

@@ -987,34 +987,95 @@ def set_caption(self, caption: str) -> "Styler":
self.caption = caption
return self

def set_table_styles(self, table_styles) -> "Styler":
def set_table_styles(self, table_styles, axis=0, overwrite=True) -> "Styler":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it is not-trivial to actually construct this union

@jreback jreback merged commit de919ff into pandas-dev:master Nov 24, 2020
@jreback
Copy link
Contributor

jreback commented Nov 24, 2020

thanks @attack68

@attack68 attack68 deleted the styler_column_style_enh branch January 26, 2021 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Stale Styler conditional formatting using DataFrame.style
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ENH: Styler for specific column styling
3 participants