7

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.

1
  • I think the background_gradient function is applying the colormap row by row, you need to apply it to the overall dataframe. This answer might help you: stackoverflow.com/a/42563850/4314229 Commented Jan 5, 2020 at 0:46

2 Answers 2

5
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

Sign up to request clarification or add additional context in comments.

Comments

0

The following is a bit easier to follow and uses matplotlib built in normalizer.

import pandas as pd
import numpy as np
import seaborn as sns
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)
divnorm = colors.TwoSlopeNorm(vmin=df.values.min(), vcenter=0, vmax=df.values.max())
df.style.background_gradient(cmap=cm, axis=None, vmin=0, vmax=1, gmap=df.apply(divnorm)).format(precision=2)

resulting table

The use of vmin=0, vmax=1 is to make sure the mapping uses those values as reference to lowest and highest color map values, instead of the actual data points, since we mapped all the original values of the data to this range. For reference, here are the mapped normalized values that are used to determine the corresponding color in the cmap:

df.apply(divnorm).style.background_gradient(cmap=cm, axis=None, gmap=df.apply(divnorm)).format(precision=2)

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.