You can create a custom discrete colorbar quite easily by using a BoundaryNorm as normalizer for your scatter. The quirky bit (in my method) is making 0 showup as grey.

For images i often use the cmap.set_bad() and convert my data to a numpy masked array. That would be much easier to make 0 grey, but i couldnt get this to work with the scatter or the custom cmap.

As an alternative you can make your own cmap from scratch, or read-out an existing one and override just some specific entries.

import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt

fig, ax = plt.subplots(1, 1, figsize=(6, 6))  # setup the plot

x = np.random.rand(20)  # define the data
y = np.random.rand(20)  # define the data
tag = np.random.randint(0, 20, 20)
tag[10:12] = 0  # make sure there are some 0 values to show up as grey

cmap = plt.cm.jet  # define the colormap
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# force the first color entry to be grey
cmaplist[0] = (.5, .5, .5, 1.0)

# create the new map
cmap = mpl.colors.LinearSegmentedColormap.from_list(
    'Custom cmap', cmaplist, cmap.N)

# define the bins and normalize
bounds = np.linspace(0, 20, 21)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# make the scatter
scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20),
                  cmap=cmap, norm=norm)

# create a second axes for the colorbar
ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8])
cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm,
    spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i')

ax.set_title('Well defined discrete colors')
ax2.set_ylabel('Very custom cbar [-]', size=12)

I personally think that with 20 different colors its a bit hard to read the specific value, but thats up to you of course.

Answer from Rutger Kassies on Stack Overflow
Top answer
1 of 9
140

You can create a custom discrete colorbar quite easily by using a BoundaryNorm as normalizer for your scatter. The quirky bit (in my method) is making 0 showup as grey.

For images i often use the cmap.set_bad() and convert my data to a numpy masked array. That would be much easier to make 0 grey, but i couldnt get this to work with the scatter or the custom cmap.

As an alternative you can make your own cmap from scratch, or read-out an existing one and override just some specific entries.

import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt

fig, ax = plt.subplots(1, 1, figsize=(6, 6))  # setup the plot

x = np.random.rand(20)  # define the data
y = np.random.rand(20)  # define the data
tag = np.random.randint(0, 20, 20)
tag[10:12] = 0  # make sure there are some 0 values to show up as grey

cmap = plt.cm.jet  # define the colormap
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# force the first color entry to be grey
cmaplist[0] = (.5, .5, .5, 1.0)

# create the new map
cmap = mpl.colors.LinearSegmentedColormap.from_list(
    'Custom cmap', cmaplist, cmap.N)

# define the bins and normalize
bounds = np.linspace(0, 20, 21)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# make the scatter
scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20),
                  cmap=cmap, norm=norm)

# create a second axes for the colorbar
ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8])
cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm,
    spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i')

ax.set_title('Well defined discrete colors')
ax2.set_ylabel('Very custom cbar [-]', size=12)

I personally think that with 20 different colors its a bit hard to read the specific value, but thats up to you of course.

2 of 9
92

You could follow this example below or the newly added example in the documentation

#!/usr/bin/env python
"""
Use a pcolor or imshow with a custom colormap to make a contour plot.

Since this example was initially written, a proper contour routine was
added to matplotlib - see contour_demo.py and
http://matplotlib.sf.net/matplotlib.pylab.html#-contour.
"""

from pylab import *


delta = 0.01
x = arange(-3.0, 3.0, delta)
y = arange(-3.0, 3.0, delta)
X,Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1 # difference of Gaussians

cmap = cm.get_cmap('PiYG', 11)    # 11 discrete colors

im = imshow(Z, cmap=cmap, interpolation='bilinear',
            vmax=abs(Z).max(), vmin=-abs(Z).max())
axis('off')
colorbar()

show()

which produces the following image:

🌐
GitHub
gist.github.com › jakevdp › 91077b0cae40f8f8244a
Small utility to create a discrete matplotlib colormap · GitHub
def discrete_cmap(N, base_cmap=None): import matplotlib as pplt """Create an N-bin discrete colormap from the specified input map""" # Note that if base_cmap is a string or None, you can simply do # return plt.cm.get_cmap(base_cmap, N) # The following works for string, None, or a colormap instance: base = pplt.cm.get_cmap(base_cmap) color_list = base(np.linspace(0, 1, N, 0)) cmap_name = base.name + str(N) return pplt.cm.colors.ListedColormap(color_list, color_list, N) N = 10 cmap = discrete_cmap(N) x = np.random.randint(N, size = (2, 10))*-1 fig, ax = plt.subplots() h = ax.scatter(*x, c = x[0], cmap = cmap) fig.colorbar(h) fig.show()
🌐
Matplotlib
matplotlib.org › stable › users › explain › colors › colormaps.html
Choosing Colormaps in Matplotlib — Matplotlib 3.11.2 documentation
Matplotlib has a number of built-in colormaps accessible via matplotlib.colormaps. There are also external libraries that have many extra colormaps, which can be viewed in the Third-party colormaps section of the Matplotlib documentation. Here we briefly discuss how to choose between the many ...
🌐
Towards Data Science
towardsdatascience.com › home › data science › how to create custom color palettes in matplotlib - discrete vs. linear colormaps, explained
How to Create Custom Color Palettes in Matplotlib - Discrete vs. Linear Colormaps, Explained | Towards Data Science
August 29, 2024 - Since there are 5 office locations, and our discrete color palette has 5 colors, it's a perfect match for visualization. The only new parameter you need to know in plot() is colormap. You'll have to provide your palette variable.
🌐
Rip Tutorial
riptutorial.com › custom discrete colormap
matplotlib Tutorial => Custom discrete colormap
If you have predefined ranges and want to use specific colors for those ranges you can declare custom colormap. For example: import matplotlib.pyplot as plt import numpy as np import matplotlib.colors x = np.linspace(-2,2,500) y = np.linspace(-2,2,500) XX, YY = np.meshgrid(x, y) Z = np.sin(XX) * np.cos(YY) cmap = colors.ListedColormap(['red', '#000000','#444444', '#666666', '#ffffff', 'blue', 'orange']) boundaries = [-1, -0.9, -0.6, -0.3, 0, 0.3, 0.6, 1] norm = colors.BoundaryNorm(boundaries, cmap.N, clip=True) plt.pcolormesh(x,y,Z, cmap=cmap, norm=norm) plt.colorbar() plt.show()
🌐
Matplotlib
matplotlib.org › stable › users › explain › colors › colorbar_only.html
Customized Colorbars Tutorial — Matplotlib 3.11.2 documentation
The following example shows how to make a discrete colorbar based on a continuous cmap. We use matplotlib.colors.BoundaryNorm to describe the interval boundaries (which must be in increasing order), and further pass the extend argument to it to further display "over" and "under" colors (which are used for data outside of the norm range). fig, ax = plt.subplots(figsize=(6, 1), layout='constrained') cmap = mpl.colormaps["viridis"] bounds = [-1, 2, 5, 7, 12, 15] norm = mpl.colors.BoundaryNorm(bounds, cmap.N, extend='both') colorizer = mpl.colorizer.Colorizer(norm=norm, cmap='viridis') fig.colorbar(mpl.colorizer.ColorizingArtist(colorizer), cax=ax, orientation='horizontal', label="Discrete intervals with extend='both' keyword")
🌐
Moonbooks
en.moonbooks.org › Articles › How-to-create-a-discrete-colorbar-with-matplotlib-
How to create a discrete colorbar with matplotlib ? - Moonbooks
April 14, 2023 - cmap = cm.get_cmap('hot', 10) ...tplotlib.colors.rgb2hex(rgba)) ... We can create a discrete colormap using ListedColormap() by choosing the colors from a list of hexadecimal values and then plot the data:...
🌐
Matplotlib
matplotlib.org › stable › users › explain › colors › colormap-manipulation.html
Creating Colormaps in Matplotlib — Matplotlib 3.11.2 documentation
We can reduce the dynamic range of a colormap; here we choose the middle half of the colormap. Note, however, that because viridis is a listed colormap, we will end up with 128 discrete values instead of the 256 values that were in the original colormap.
Find elsewhere
🌐
Matplotlib
matplotlib.org › 3.5.0 › tutorials › colors › colorbar_only.html
Customized Colorbars Tutorial — Matplotlib 3.5.0 documentation
fig, ax = plt.subplots(figsize=(6, 1)) fig.subplots_adjust(bottom=0.5) cmap = mpl.cm.viridis bounds = [-1, 2, 5, 7, 12, 15] norm = mpl.colors.BoundaryNorm(bounds, cmap.N, extend='both') fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), cax=ax, orientation='horizontal', label="Discrete intervals with extend='both' keyword") ... The third example illustrates the use of a ListedColormap which generates a colormap from a set of listed colors, colors.BoundaryNorm which generates a colormap index based on discrete intervals and extended ends to show the "over" and "under" value colors.
🌐
Blogger
qingkaikong.blogspot.com › 2017 › 12 › create-discrete-colormap.html
Qingkai's Blog: Create discrete colormap
Discretizing the colormap: Use matplotlib.colors.BoundaryNorm to define color boundaries and matplotlib.pyplot.colorbar to create the colorbar.
🌐
Towards Data Science
towardsdatascience.com › home › latest › customize colormaps with matplotlib
Customize Colormaps with Matplotlib | Towards Data Science
March 5, 2025 - Based on the interpolation method, there are two types of colormaps in Matplotlib: A listed colormap is a list __ of colors. It’s a discrete colormap with a predefined set of colors, and it doesn’t interpolate between colors.
🌐
Matplotlib
matplotlib.org › stable › gallery › color › individual_colors_from_cmap.html
Selecting individual colors from a colormap — Matplotlib 3.11.2 documentation
import matplotlib.pyplot as plt import numpy as np import matplotlib as mpl n_lines = 21 cmap = mpl.colormaps['plasma'] # Take colors at regular intervals spanning the colormap.
🌐
GitHub
github.com › matplotlib › matplotlib › issues › 21786
Qualitative colormaps re-map discrete color values · Issue #21786 · matplotlib/matplotlib
November 28, 2021 - ... BoundaryNorm requires bins ... I thought NoNorm would/should do the trick but it didn't. ... Matplotlib has a very flexible continuous colormap model....
🌐
PYTHON CHARTS
python-charts.com › home › matplotlib colormaps
Matplotlib colormaps [Full List] | PYTHON CHARTS
July 29, 2026 - Matplotlib provides dozens of built-in colormaps: continuous or discrete color scales that you pass to the cmap argument whenever you map a numeric or categorical variable to color, for example in imshow, scatter, pcolormesh or a seaborn heatmap.
🌐
Matplotlib
matplotlib.org › stable › tutorials › colors › colorbar_only.html
Customized Colorbars Tutorial — Matplotlib 3.7.2 documentation
August 13, 2021 - fig, ax = plt.subplots(figsize=(6, 1)) fig.subplots_adjust(bottom=0.5) cmap = mpl.cm.viridis bounds = [-1, 2, 5, 7, 12, 15] norm = mpl.colors.BoundaryNorm(bounds, cmap.N, extend='both') fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), cax=ax, orientation='horizontal', label="Discrete intervals with extend='both' keyword") The third example illustrates the use of a ListedColormap which generates a colormap from a set of listed colors, colors.BoundaryNorm which generates a colormap index based on discrete intervals and extended ends to show the "over" and "under" value colors.
🌐
Medium
medium.datadriveninvestor.com › creating-a-discrete-colorbar-with-custom-bin-sizes-in-matplotlib-50b0daf8dd46
Creating a Discrete Colorbar with Custom Bin Sizes in Matplotlib | by Daniel Ellis Research | DataDrivenInvestor
February 5, 2021 - Creating a Discrete Colorbar with Custom Bin Sizes in Matplotlib How to create a fully customised colour mapping within matplotlib. Problem Our data is split into non-uniform categories, where a …
🌐
Stack Exchange
gis.stackexchange.com › questions › 239462 › custom-matplotlib-discrete-colormap-with-fixed-values-on-raster
color ramp - Custom matplotlib discrete colormap with fixed values on raster - Geographic Information Systems Stack Exchange
I've been using a color ramp with fixed values (0-255) corresponding to specific rgb color (discrete interpolation) for 8bit tifs in QGIS. I've used matplotlib to apply built in colormaps, but I'm ...