Create custom colormap and set ticks to your integers
from matplotlib import colors
cmap = colors.ListedColormap(['green','red','black','yellow'])
bounds=[-0.5, 0.5, 1.5, 2.5, 3.5]
norm = colors.BoundaryNorm(bounds, cmap.N)
heatmap = plt.pcolor(np.array(data), cmap=cmap, norm=norm)
plt.colorbar(heatmap, ticks=[0, 1, 2, 3])
Is this what you want?
Notice, that your data are displayed "upside down".

How do I reverse the color scale in a heatmap?
How can I add numeric values inside a heatmap?
How do I add a custom colorbar with specific ticks?
Create custom colormap and set ticks to your integers
from matplotlib import colors
cmap = colors.ListedColormap(['green','red','black','yellow'])
bounds=[-0.5, 0.5, 1.5, 2.5, 3.5]
norm = colors.BoundaryNorm(bounds, cmap.N)
heatmap = plt.pcolor(np.array(data), cmap=cmap, norm=norm)
plt.colorbar(heatmap, ticks=[0, 1, 2, 3])
Is this what you want?
Notice, that your data are displayed "upside down".

I modified this code to show 3 red / yellow / green states of 9 nodes
import matplotlib.pyplot as plt
from matplotlib.colors
import LinearSegmentedColormap
colors = [(1, 0, 0), (1, 1, 0), (0, 1, 0)] # Red, yellow, green
n_bins = [3] # Discretizes the interpolation into bins
cmap_name = 'my_list'
cm = LinearSegmentedColormap.from_list(cmap_name, colors, N=3)
threshold = 3 # max value
data = [[1, 1, 2], [1, 1, 3], [1, 1, 2]]
img = plt.imshow(data, interpolation='nearest', vmax=threshold, cmap=cm)
plt.show()
This will solve your first two problems -
fig = plt.figure()
fig, ax = plt.subplots(1,1, figsize=(12,12))
heatplot = ax.imshow(data_matrix,cmap = 'Greens')
cbar = fig.colorbar(heatplot, ticks=[data_raw.overlap.min(), data_raw.overlap.max()])
tick_spacing = 1
ax.set_title("Overlap")
I would use the seaborn heatmap function instead. The colormap Greens should do the trick with regards to your desired color scheme. If you'd like you can check out other options in the matplotlib docs.
Just hihglight and ctrl + c the dataset in your question and run the snippet below:
# Imports
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
data_raw = pd.read_clipboard(sep='\\s+')
data_matrix = data_raw.pivot("curation1", "curation2", "overlap")
data_matrix = data_matrix.fillna(0)
# A heatmap function that builds on the seaborn heatmap function
def HeatMap_function(df, title, transpose = True, colors = 'Greens', dropDuplicates = True):
if transpose:
df = df.T
if dropDuplicates:
mask = np.zeros_like(df, dtype=np.bool)
mask = np.invert(mask)
mask[np.triu_indices_from(mask)] = False
# Set background color / chart style
sns.set_style(style = 'white')
# Set up matplotlib figure
f, ax = plt.subplots(figsize=(11, 9))
ax.set_title(title)
# Add diverging colormap from red to blue
# cmap = sns.diverging_palette(250, 10, as_cmap=True)
cmap=plt.get_cmap(colors)
# Draw correlation plot with or without duplicates
if dropDuplicates:
sns.heatmap(df, mask=mask, cmap=cmap,
square=True,
linewidth=.5, cbar_kws={"shrink": .5}, ax=ax)
else:
sns.heatmap(df, cmap=cmap,
square=True,
linewidth=.5, cbar_kws={"shrink": .5}, ax=ax)
ax.xaxis.set_ticks_position('top')
ax.yaxis.set_ticks_position('right')
# A testrun
HeatMap_function(df = data_matrix, title = 'Overlap', transpose = False,
colors = 'Greens', dropDuplicates = True)
And you'll get this:

Now you can also change the layout of your plot by using different combinations of transpose, colors and dropDuplicates.
Using matplotlib.colors.LinearSegmentedColormap's from_list method seems more intuitive than some of the other answers here.
from matplotlib.colors import LinearSegmentedColormap
cmap=LinearSegmentedColormap.from_list('rg',["r", "w", "g"], N=256)

Or for more sophisticated tuning:
from matplotlib.colors import LinearSegmentedColormap
c = ["darkred","red","lightcoral","white", "palegreen","green","darkgreen"]
v = [0,.15,.4,.5,0.6,.9,1.]
l = list(zip(v,c))
cmap=LinearSegmentedColormap.from_list('rg',l, N=256)

you can create your own using a LinearSegmentedColormap. I like to set the red and green channels to something less than 1.0 at the upper and lower limits so the colours aren't too bright (here I used 0.8). Adjust that to suit your taste.
See the custom_cmap example on the matplotlib website for further details.
Here's an working example:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
# This dictionary defines the colormap
cdict = {'red': ((0.0, 0.0, 0.0), # no red at 0
(0.5, 1.0, 1.0), # all channels set to 1.0 at 0.5 to create white
(1.0, 0.8, 0.8)), # set to 0.8 so its not too bright at 1
'green': ((0.0, 0.8, 0.8), # set to 0.8 so its not too bright at 0
(0.5, 1.0, 1.0), # all channels set to 1.0 at 0.5 to create white
(1.0, 0.0, 0.0)), # no green at 1
'blue': ((0.0, 0.0, 0.0), # no blue at 0
(0.5, 1.0, 1.0), # all channels set to 1.0 at 0.5 to create white
(1.0, 0.0, 0.0)) # no blue at 1
}
# Create the colormap using the dictionary
GnRd = colors.LinearSegmentedColormap('GnRd', cdict)
# Make a figure and axes
fig,ax = plt.subplots(1)
# Some fake data in the range -3 to 3
dummydata = np.random.rand(5,5)*6.-3.
# Plot the fake data
p=ax.pcolormesh(dummydata,cmap=GnRd,vmin=-3,vmax=3)
# Make a colorbar
fig.colorbar(p,ax=ax)
plt.show()

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)

