4

I have a dataframe df with values from 0 to x (x is integer and no fixed value), in my example is x=10

I want to map the heatmap with cmap 'Reds', however where value 0 is should not be white but green '#009933'

import seaborn as sns # matplotlib inline 
import random
data = []
for i in range(10):
    data.append([random.randrange(0, 11, 1) for _ in range(10)])
df = pd.DataFrame(data)

fig, ax = plt.subplots(figsize = (12, 10)) 
# cmap = [????]
ax = sns.heatmap(df, cmap='Reds', linewidths = 0.005, annot = True, cbar=True) 
                            
plt.show()

enter image description here How can I do that?

actnmk
  • 156
  • 12
  • Recent: [seaborn don't give color in heatmap if cell value is higher than 0.05](https://stackoverflow.com/questions/65596029/seaborn-dont-give-color-in-heatmap-if-cell-value-is-higher-than-0-05) – JohanC Jan 08 '21 at 23:27

2 Answers2

4

As an alternative to the accepted answer you could also set vmin to slightly above 0 and define the color for out-of-range values with set_under:

import copy
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

cmap = copy.copy(plt.get_cmap("Reds"))
cmap.set_under('#009933')
sns.heatmap(np.random.randint(0,10,(10,10)), cmap=cmap, lw=0.005, annot=True, vmin=1e-5)

enter image description here

The under-color is not shown by default in the colorbar. To alter the colorbar, use for example sns.heatmap(..., cbar_kws={'extend':'min', 'extendrect':True}). The explanation of these parameters can be found in the colorbar docs.

JohanC
  • 71,591
  • 8
  • 33
  • 66
Stef
  • 28,728
  • 2
  • 24
  • 52
  • 1
    You can use `cbar_kw={'extend':'min', 'extendrect':'True}` to show the under color. See e.g.[seaborn don't give color in heatmap if cell value is higher than 0.05](https://stackoverflow.com/questions/65596029/seaborn-dont-give-color-in-heatmap-if-cell-value-is-higher-than-0-05) – JohanC Jan 08 '21 at 23:25
  • @Stef SO doesn't let me editing comments after 5 minutes. Anyway, SO prefers answers to be more self-containing, so I updated your answer. – JohanC Jan 09 '21 at 17:34
  • @JohanC Ah, I see. I just wanted to give the due credit to you. Thank you so much for the editing! – Stef Jan 09 '21 at 17:35
2

You can use a LinearSegmentedColormap from matplotlib.colors. You first have to find the largest value, in this case 10, then use that to create a colors variable that starts with green, then goes to the standard 'Reds' colorset. Also, set the colorbar to False when making the heatmap with seaborn, and separately make one with matplotlib.

This code was adapted from here:

import matplotlib.pyplot as plt
import matplotlib.colors as cl
import seaborn as sns
import pandas as pd
import numpy as np
import random

data = []
for i in range(10):
    data.append([random.randrange(0, 11, 1) for _ in range(10)])
df = pd.DataFrame(data)

fig, ax = plt.subplots(figsize = (12, 10)) 
cmap_reds = plt.get_cmap('Reds')
num_colors = 11
colors = ['#009933'] + [cmap_reds(i / num_colors) for i in range(1, num_colors)]
cmap = cl.LinearSegmentedColormap.from_list('', colors, num_colors)
ax = sns.heatmap(df, cmap=cmap, vmin=0, vmax=num_colors, square=True, cbar=False, annot = True)
cbar = plt.colorbar(ax.collections[0], ticks=range(num_colors + 1))
cbar.set_ticks(np.linspace(0, num_colors, 2*num_colors+1)[1::2])
cbar.ax.set_yticklabels(range(num_colors))
plt.show()

Here is the output: enter image description here

DapperDuck
  • 2,728
  • 1
  • 9
  • 21