29

The pandas style option to add a background gradient is great for quickly inspecting my output table. However, it is applied either row-wise or columns-wise. Would it be possible to apply it to the whole dataframe at once?

EDIT: A minimum working example:

df = pd.DataFrame([[3,2,10,4],[20,1,3,2],[5,4,6,1]])
df.style.background_gradient()
JJJ
  • 1,009
  • 6
  • 19
  • 31
Peter9192
  • 2,899
  • 4
  • 15
  • 24
  • 4
    Did you try setting `axis=None`? – Nickil Maveli Aug 13 '16 at 10:17
  • I did now, but it throws an error.. Good idea though ;-) – Peter9192 Aug 13 '16 at 10:40
  • What kind of error? Could you share your code and a small snippet of the dataframe? – Nickil Maveli Aug 13 '16 at 10:41
  • I added an example. The error message upon adding `axis=None` is: `The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().` – Peter9192 Aug 13 '16 at 10:49
  • From what I know, you can't set the `background_gradient` for both the rows/columns simultaneously. But however you could create a custom function and apply colors to a particular threshold of the values you want to distinguish them by. – Nickil Maveli Aug 13 '16 at 11:51

4 Answers4

31

Currently you can't set the background_gradient for both the rows/columns simultaneously as pointed by Nickil Maveli. The trick is to customize the pandas function background_gradient:

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import colors

def background_gradient(s, m, M, cmap='PuBu', low=0, high=0):
    rng = M - m
    norm = colors.Normalize(m - (rng * low),
                            M + (rng * high))
    normed = norm(s.values)
    c = [colors.rgb2hex(x) for x in plt.cm.get_cmap(cmap)(normed)]
    return ['background-color: %s' % color for color in c]

df = pd.DataFrame([[3,2,10,4],[20,1,3,2],[5,4,6,1]])
df.style.apply(background_gradient,
               cmap='PuBu',
               m=df.min().min(),
               M=df.max().max(),
               low=0,
               high=0.2)
Community
  • 1
  • 1
  • Thanks! Can I use the style in a loop? I have a Dictionary in which the value is dataFrame. I would like to loop through the Dictionary and Show the DataFrame with style. @Guilherme Beltramini – Jason LiLy Feb 08 '19 at 14:45
  • @JasonLi, if your `dict` with `pandas.DataFrame` is like this `dict_dfs = {'df1': pd.DataFrame(...), 'df2': pd.DataFrame(...), ...}`, you can do use `from IPython.display import display` and put `display(df.style.apply(...))` inside a for-loop (e.g., `for df in dict_dfs.values()`) (obs.: since it's not possible to use block-formatting for code inside the comments, I had to simplify the examples, but I hope it's clear). – Guilherme Beltramini Feb 08 '19 at 19:49
  • It works after upgrading pandas from 0.19 to 0.24. Thanks @Guiherme Beltramini – Jason LiLy Feb 08 '19 at 20:59
15

You can use axis=None to get rid of the min and max computations in the call:

def background_gradient(s, m=None, M=None, cmap='PuBu', low=0, high=0):
    print(s.shape)
    if m is None:
        m = s.min().min()
    if M is None:
        M = s.max().max()
    rng = M - m
    norm = colors.Normalize(m - (rng * low),
                            M + (rng * high))
    normed = s.apply(norm)

    cm = plt.cm.get_cmap(cmap)
    c = normed.applymap(lambda x: colors.rgb2hex(cm(x)))
    ret = c.applymap(lambda x: 'background-color: %s' % x)
    return ret


df.style.apply(background_gradient, axis=None)

Edit: You may need to use normed = s.apply(lambda x: norm(x.values)) for this to work on matplotlib 2.2

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
Andreas Mueller
  • 27,470
  • 8
  • 62
  • 74
15

Setting axis=None is working for me in 1.0.5

abogaard
  • 339
  • 3
  • 8
5

You need to set axis to None. For me best solution using seaborn:

import seaborn as sns
import pandas as pd
cm = sns.color_palette("blend:white,green", as_cmap=True)
df.style.background_gradient(cmap = cm,axis=None)
Vojtech Stas
  • 631
  • 8
  • 22