Use Listed colors or Linear Segmented Colormap from Matplotlib, eg. like in this question (Discrete legend in seaborn heatmap plot):
myColors = ((0.8, 0.0, 0.0, 1.0), (0.0, 0.8, 0.0, 1.0), (0.0, 0.0, 0.8, 1.0))
cmap = LinearSegmentedColormap.from_list('Custom', myColors, len(myColors))
You can specify your colours using RGB or also hex format. However, seaborn provides many predefined palettes, see https://seaborn.pydata.org/tutorial/color_palettes.html.
Answer from My Work on Stack OverflowAs 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)

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.
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:

Consider the following:
import matplotlib as mpl
import seaborn as sns
import numpy as np
A = np.random.randint(0,100, size=(20,20))
mask_array = np.zeros((20, 20), dtype=bool)
mask_array[:, :5] = True
cmap = mpl.colormaps["viridis"]
newcolors = cmap(np.linspace(0, 1, 100))
newcolors[:10] = np.array([1,1,1,1])
newcolors[90:] = np.array([0,0,0,1])
newcolors[20] = mpl.colors.to_rgb('tab:orange') + (1,)
newcmap = mpl.colors.ListedColormap(newcolors)
g = sns.heatmap(A, cmap=newcmap, mask=mask_array)
# Set color of masked region
g.set_facecolor('lightgrey')
Sample result:

The following (appended to the above script) will make it so that the colorbar has a visible outline.
cbar_ax = g.figure.axes[-1]
for spine in cbar_ax.spines.values():
spine.set(visible=True)
Sample result with outline:

In order to mask the colors of the heatmap, but not show an updated colorbar, set cbar=False, and then attach a custom colorbar, as shown in Standalone colorbar.
g = sns.heatmap(A, cmap=newcmap, mask=mask_array, cbar=False)
# add a new axes of the desired shape
cb = g.figure.add_axes([0.93, 0.11, 0.025, 0.77])
# attach a new colorbar to the axes
mpl.colorbar.ColorbarBase(cb, cmap='viridis', norm=mpl.colors.Normalize(10, 90), # vmax and vmin
label=None, ticks=range(10, 91, 10))

- The difficulty arises from the requirement to use more than a single mask.
mask_array, and a mask forA == 20if the array is onlyint, or20 <= A < 21if the array isfloat.
matplotlib.colors.Colormapoffers only 3 methods for setting colors.set_underset_overset_bad- Set the color for masked values, which is already used formask_array.
with_extremes- Does the three combined.cmap = mpl.colormaps['viridis'].with_extremes(bad='orange', under='w', over='k')
- Using these methods doesn't effect the look of the colorbar.
Imports and Sample Data
import numpy as np
import seaborn as sns
import matplotlib as mpl
# set for repeatable sample
np.random.seed(2023)
# random 20 x 20 array
# A = np.random.randint(0, 100, size=(20, 20)) # random integers
A = np.random.random(size=(20, 20)) * 100 # random floats
1. Don't show the sliced columns
- This is the easiest option, because it frees up
mask
# remove the unneeded columns
A = A[:, 5:]
# create the value mask
mask = np.logical_and(A >= 20, A < 21)
# create the colormap with extremes
cmap = mpl.colormaps["viridis"].with_extremes(bad='orange', under='w', over='k')
# plot
g = sns.heatmap(A, vmin=10, vmax=90, cmap=cmap, mask=mask)
# reset the xticklabels to show the correct column labels
_ = g.set_xticks(ticks=g.get_xticks(), labels=range(5, 20))

2. Show all the columns
- This is the more cumbersome option because, like the answer provided by Ben, this requires manually adding a color to
cmap, and adding a custom colorbar.- Using
mask, or anynp.nanvalues in the array, are colored byset_bad.
- Using
- This reuses the colorbar creation method I previously added to Ben's answer, which came from Standalone colorbar.
- As demonstrated in Creating Colormaps in Matplotlib, a new color can be added into a slice of colors from a resampled colormap.
- This does not get the correct value if
vminandvmaxare used, because those options change the range of the colorbar.
- This does not get the correct value if
# create the column mask
mask = np.zeros((20, 20), dtype=bool)
mask[:, :5] = True # A[:, :5] = np.nan has the same effect
# slice the colors into the range of values in the array
colors = mpl.colormaps["viridis"].resampled(100).colors
# map a specific value range to a color; use a range for floats, and / or set a hight number to resampled
colors[19:21] = mpl.colors.to_rgba('tab:orange')
# create the new colormap with extremes
cmap = mpl.colors.ListedColormap(colors).with_extremes(bad='lightgray', under='w', over='k')
# draw the heatmap
g = sns.heatmap(A, cmap=cmap, mask=mask, cbar=False)
# add a new axes of the desired shape
cb_ax = g.figure.add_axes([0.93, 0.11, 0.025, 0.77])
# attach a new colorbar to the axes without an outline
cb = mpl.colorbar.ColorbarBase(cb_ax, cmap='viridis', norm=mpl.colors.Normalize(10, 90), # vmax and vmin
label=None, ticks=range(10, 91, 10)).outline.set_visible(False)

3. Let the columns and bad values share the color
# create the value mask
mask = np.logical_and(A >= 20, A < 21)
# add the unwanted column to the mask as np.nan
mask[:, :5] = np.nan
# create the colormap with extremes
cmap = mpl.colormaps["viridis"].with_extremes(bad='orchid', under='w', over='k')
# plot
g = sns.heatmap(A, vmin=10, vmax=90, cmap=cmap, mask=mask)


