pythonpandasdataframecolormappandas-styles

Use center diverging colormap in a pandas dataframe heatmap display


I would like to use a diverging colormap to color the background of a pandas dataframe. The aspect that makes this trickier than one would think is the centering. In the example below, a red to blue colormap is used, but the middle of the colormap isn't used for values around zero. How to create a centered background color display where zero is white, all negatives are a red hue, and all positives are a blue hue?

import pandas as pd
import numpy as np
import seaborn as sns

np.random.seed(24)
df = pd.DataFrame()
df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4)*10, columns=list('ABCD'))],
               axis=1)
df.iloc[0, 2] = 0.0
cm = sns.diverging_palette(5, 250, as_cmap=True)
df.style.background_gradient(cmap=cm).set_precision(2)

enter image description here

The zero in the above display has a red hue and the closest to white background is used for a negative number.


Solution

  • import pandas as pd
    import numpy as np
    import seaborn as sns
    import matplotlib.pyplot as plt
    from matplotlib import colors
    
    np.random.seed(24)
    df = pd.DataFrame()
    df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4)*10, columns=list('ABCD'))],
                   axis=1)
    df.iloc[0, 2] = 0.0
    cm = sns.diverging_palette(5, 250, as_cmap=True)
    
    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]
    
    even_range = np.max([np.abs(df.values.min()), np.abs(df.values.max())])
    df.style.apply(background_gradient,
                   cmap=cm,
                   m=-even_range,
                   M=even_range).set_precision(2)
    

    centered diverging colormap dataframe background