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

ENH: Allow elementwise coloring in background_gradient with axis=None #15204

Closed
dhirschfeld opened this issue Jan 24, 2017 · 8 comments · Fixed by #21259
Closed

ENH: Allow elementwise coloring in background_gradient with axis=None #15204

dhirschfeld opened this issue Jan 24, 2017 · 8 comments · Fixed by #21259
Labels
Enhancement Output-Formatting __repr__ of pandas objects, to_string
Milestone

Comments

@dhirschfeld
Copy link
Contributor

At present the axis argument to background_gradient only accepts 0 or 1 so that the resulting "heatmap" can only be applied columnwise or rowwise rather than to the whole table.

Signature: df.style.background_gradient(cmap='PuBu', low=0, high=0, axis=0, subset=None)

Parameters
----------
cmap: str or colormap
    matplotlib colormap
low, high: float
    compress the range by these values.
axis: int or str
    1 or 'columns' for colunwise, 0 or 'index' for rowwise
subset: IndexSlice
    a valid slice for ``data`` to limit the style application to

I propose that axis=None be allowed to mean applying a color gradient elementwise.

@dhirschfeld
Copy link
Contributor Author

df = pd.DataFrame(np.sqrt((np.mgrid[0:11, 0:11]**2).sum(axis=0)))

Example of axis=0 coloring whereas I would like the color gradient to be radially increasing like the numbers
image

@dhirschfeld
Copy link
Contributor Author

Note: This would be consistent with the axis argument to apply:
image

@dhirschfeld
Copy link
Contributor Author

dhirschfeld commented Jan 24, 2017

My workaround:

def background_gradient(self, cvals=None, cmin=None, cmax=None, cmap='viridis', **css):
    """For use with `DataFrame.style.apply` this function will apply a heatmap
    color gradient *elementwise* to the calling DataFrame

    Parameters
    ----------
    self : pd.DataFrame
        The calling DataFrame. This argument is automatically passed in by the
        `DataFrame.style.apply` method

    cvals : pd.DataFrame
        If specified this DataFrame is used to determine the color gradient

    cmin : float
        If specified, any values below this will be clipped to the bottom of
        the cmap

    cmax : float
        If specified, any values above this will be clipped to the top of
        the cmap

    cmap : colormap or str
        The colormap to use

    css : dict
        Any extra inline css key/value pars to pass to the styler

    Returns
    -------
    pd.DataFrame
        The css styles to apply

    """
    from matplotlib import cm
    from matplotlib.colors import rgb2hex
    if cvals is None:
        cvals = self.values.ravel().copy()
    else:
        assert cvals.shape == self.shape
        cvals = cvals.values.ravel().copy()
    cvals -= cmin or cvals.min()
    cvals /= cmax or cvals.max()
    cvals = cvals.clip(0, 1)
    styles = []
    for rgb in cm.viridis_r(cvals):
        style = [
            "{}: {}".format(key.replace('_', '-'), value)
            for key, value in css.items()
        ]
        style.append("background-color: {}".format(rgb2hex(rgb)))
        styles.append('; '.join(style))
    styles = np.asarray(styles).reshape(self.shape)
    return pd.DataFrame(styles, index=self.index, columns=self.columns)

image

@jorisvandenbossche jorisvandenbossche added Enhancement Output-Formatting __repr__ of pandas objects, to_string labels Jan 24, 2017
@jorisvandenbossche
Copy link
Member

Sounds logical, PR certainly welcome I think!
cc @TomAugspurger

@TomAugspurger
Copy link
Contributor

Yep, that's be great if you could submit a PR.

@jreback
Copy link
Contributor

jreback commented Jan 24, 2017

this should be fairly generalized i think, iow where we accept axis

@jorisvandenbossche
Copy link
Member

@dhirschfeld Interested to do a PR for this? (I also stumbled into a case where I would need this)

BTW, your ability to specify a vmin/vmax is also a nice feature I think.

@dhirschfeld
Copy link
Contributor Author

@jorisvandenbossche - I'd like to finish it up by submitting a PR but it's unlikely I'll have time in at least the next couple of weeks so I'd be happy for anyone else interested to pick it up. In the interim, my implementation posted above is available for anyone who finds it...

I had a need to explicitly specify the cmin/cmax values so I implemented that directly rather than the high/low compression. I also thought the **css made for a very usable interactive api but concede that a case could be made that it was too magic

@jschendel jschendel added this to the 0.24.0 milestone May 30, 2018
soxofaan added a commit to soxofaan/pandas that referenced this issue Jun 19, 2018
soxofaan added a commit to soxofaan/pandas that referenced this issue Jun 20, 2018
soxofaan added a commit to soxofaan/pandas that referenced this issue Jul 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement Output-Formatting __repr__ of pandas objects, to_string
Projects
None yet
5 participants