First off, there's more than one way to do this.

  1. Pass an instance of DivergingNorm as the norm kwarg.
  2. Use the colors kwarg to contourf and manually specify the colors
  3. Use a discrete colormap constructed with matplotlib.colors.from_levels_and_colors.

The simplest way is the first option. It is also the only option that allows you to use a continuous colormap.

The reason to use the first or third options is that they will work for any type of matplotlib plot that uses a colormap (e.g. imshow, scatter, etc).

The third option constructs a discrete colormap and normalization object from specific colors. It's basically identical to the second option, but it will a) work with other types of plots than contour plots, and b) avoids having to manually specify the number of contours.

As an example of the first option (I'll use imshow here because it makes more sense than contourf for random data, but contourf would have identical usage other than the interpolation option.):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import DivergingNorm

data = np.random.random((10,10))
data = 10 * (data - 0.8)

fig, ax = plt.subplots()
im = ax.imshow(data, norm=DivergingNorm(0), cmap=plt.cm.seismic, interpolation='none')
fig.colorbar(im)
plt.show()

As an example of the third option (notice that this gives a discrete colormap instead of a continuous colormap):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors

data = np.random.random((10,10))
data = 10 * (data - 0.8)

num_levels = 20
vmin, vmax = data.min(), data.max()
midpoint = 0
levels = np.linspace(vmin, vmax, num_levels)
midp = np.mean(np.c_[levels[:-1], levels[1:]], axis=1)
vals = np.interp(midp, [vmin, midpoint, vmax], [0, 0.5, 1])
colors = plt.cm.seismic(vals)
cmap, norm = from_levels_and_colors(levels, colors)

fig, ax = plt.subplots()
im = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
fig.colorbar(im)
plt.show()

Answer from Joe Kington on Stack Overflow
Top answer
1 of 1
56

First off, there's more than one way to do this.

  1. Pass an instance of DivergingNorm as the norm kwarg.
  2. Use the colors kwarg to contourf and manually specify the colors
  3. Use a discrete colormap constructed with matplotlib.colors.from_levels_and_colors.

The simplest way is the first option. It is also the only option that allows you to use a continuous colormap.

The reason to use the first or third options is that they will work for any type of matplotlib plot that uses a colormap (e.g. imshow, scatter, etc).

The third option constructs a discrete colormap and normalization object from specific colors. It's basically identical to the second option, but it will a) work with other types of plots than contour plots, and b) avoids having to manually specify the number of contours.

As an example of the first option (I'll use imshow here because it makes more sense than contourf for random data, but contourf would have identical usage other than the interpolation option.):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import DivergingNorm

data = np.random.random((10,10))
data = 10 * (data - 0.8)

fig, ax = plt.subplots()
im = ax.imshow(data, norm=DivergingNorm(0), cmap=plt.cm.seismic, interpolation='none')
fig.colorbar(im)
plt.show()

As an example of the third option (notice that this gives a discrete colormap instead of a continuous colormap):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors

data = np.random.random((10,10))
data = 10 * (data - 0.8)

num_levels = 20
vmin, vmax = data.min(), data.max()
midpoint = 0
levels = np.linspace(vmin, vmax, num_levels)
midp = np.mean(np.c_[levels[:-1], levels[1:]], axis=1)
vals = np.interp(midp, [vmin, midpoint, vmax], [0, 0.5, 1])
colors = plt.cm.seismic(vals)
cmap, norm = from_levels_and_colors(levels, colors)

fig, ax = plt.subplots()
im = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
fig.colorbar(im)
plt.show()

🌐
GitHub
gist.github.com › phobson › 7916777
One way to shift (recenter) a colormap in matplotlib · GitHub
One way to shift (recenter) a colormap in matplotlib - ShiftedColorMap.ipynb
🌐
Matplotlib
discourse.matplotlib.org › community › matplotlib-users
How to shift colormap? - matplotlib-users - Matplotlib
November 6, 2011 - I knew that matplotlib can do GUI tricks but I didn’t felt skilled to go there. Seeing you code it seems easy now, but it’s always like that after you see the solution ... ''' returns a new colormap based on the one given but adjusted via power-law: newcmap = oldcmap**a ''' if a < 0.: return cmap cdict = copy.copy(cmap._segmentdata) fn = lambda x : (x[0]**a, x[1], x[2]) for key in ('red','green','blue'): cdict[key] = map(fn, cdict[key]) cdict[key].sort() assert (cdict[key][0]<0 or cdict[key][-1]>1), \ "Resulting indices extend out of the [0, 1] segment." return colors.LinearSegmentedColormap('colormap',cdict,1024)
🌐
Matplotlib
matplotlib.org › stable › gallery › images_contours_and_fields › colormap_interactive_adjustment.html
Interactive adjustment of colormap range — Matplotlib 3.11.2 documentation
import matplotlib.pyplot as plt import numpy as np t = np.linspace(0, 2 * np.pi, 1024) data2d = np.sin(t)[:, np.newaxis] * np.cos(t)[np.newaxis, :] fig, ax = plt.subplots() im = ax.imshow(data2d) ax.set_title('Pan on the colorbar to shift the color mapping\n' 'Zoom on the colorbar to scale ...
🌐
Matplotlib
matplotlib.org › stable › users › explain › colors › colormaps.html
Choosing Colormaps in Matplotlib — Matplotlib 3.11.2 documentation
Additionally, the \(L^*\) values vary widely throughout the colormap, making it a poor choice for representing data for viewers to see perceptually. See an extension on this idea at [mycarta-jet]. plot_color_gradients('Cyclic', ['twilight', 'twilight_shifted', 'hsv'])
🌐
Colormaps
pratiman-91.github.io › colormaps
Home - Colormaps
cmaps.ice.shift(0.5).discrete(10) Discrete levels then cut the colormap from left side · cmaps.ice.discrete(11).cut(0.25, 'left') Concatenate two or more colormaps · from colormaps.utils import concat concat1 = concat(["ice", "BkBlAqGrYeOrReViWh200"]) Concatenate two or more colormaps based on ratio · from colormaps.utils import concat concat2 = concat([cmaps.ice, cmaps.BkBlAqGrYeOrReViWh200], ratios=[0.25,0.75]) Matplotlib usage example ·
🌐
Readthedocs
splot.readthedocs.io › en › latest › generated › splot.mapping.shift_colormap.html
splot.mapping.shift_colormap — splot v1.1.5.post1+13.geffb183 Manual
splot.mapping.shift_colormap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap')[source]¶ · Function to offset the “center” of a colormap. Useful for data with a negative min and positive max and you want the middle of the colormap’s dynamic range to be at zero · Parameters ...
Find elsewhere
🌐
Matplotlib
matplotlib.org › stable › users › explain › colors › colormapnorms.html
Colormap normalization — Matplotlib 3.11.2 documentation
Matplotlib does this mapping in two steps, with a normalization from the input data to [0, 1] occurring first, and then mapping onto the indices in the colormap. Normalizations are classes defined in the matplotlib.colors() module.
🌐
Towards Data Science
towardsdatascience.com › home › latest › beautiful custom colormaps with matplotlib
Beautiful custom colormaps with Matplotlib | Towards Data Science
March 5, 2025 - We can also use this code to create a diverging colormap, which is useful if our data is to be displayed about some midpoint, like zero. If the data is not equally centred about the midpoint, for example if it ranges from -2 to 5, we need to shift the centre of the colormap to the midpoint of our data.
Top answer
1 of 11
101

I know this is late to the game, but I just went through this process and came up with a solution that perhaps less robust than subclassing normalize, but much simpler. I thought it'd be good to share it here for posterity.

The function

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid

def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
    '''
    Function to offset the "center" of a colormap. Useful for
    data with a negative min and positive max and you want the
    middle of the colormap's dynamic range to be at zero.

    Input
    -----
      cmap : The matplotlib colormap to be altered
      start : Offset from lowest point in the colormap's range.
          Defaults to 0.0 (no lower offset). Should be between
          0.0 and `midpoint`.
      midpoint : The new center of the colormap. Defaults to 
          0.5 (no shift). Should be between 0.0 and 1.0. In
          general, this should be  1 - vmax / (vmax + abs(vmin))
          For example if your data range from -15.0 to +5.0 and
          you want the center of the colormap at 0.0, `midpoint`
          should be set to  1 - 5/(5 + 15)) or 0.75
      stop : Offset from highest point in the colormap's range.
          Defaults to 1.0 (no upper offset). Should be between
          `midpoint` and 1.0.
    '''
    cdict = {
        'red': [],
        'green': [],
        'blue': [],
        'alpha': []
    }

    # regular index to compute the colors
    reg_index = np.linspace(start, stop, 257)

    # shifted index to match the data
    shift_index = np.hstack([
        np.linspace(0.0, midpoint, 128, endpoint=False), 
        np.linspace(midpoint, 1.0, 129, endpoint=True)
    ])

    for ri, si in zip(reg_index, shift_index):
        r, g, b, a = cmap(ri)

        cdict['red'].append((si, r, r))
        cdict['green'].append((si, g, g))
        cdict['blue'].append((si, b, b))
        cdict['alpha'].append((si, a, a))

    newcmap = matplotlib.colors.LinearSegmentedColormap(name, cdict)
    plt.register_cmap(cmap=newcmap)

    return newcmap

An example

biased_data = np.random.random_integers(low=-15, high=5, size=(37,37))

orig_cmap = matplotlib.cm.coolwarm
shifted_cmap = shiftedColorMap(orig_cmap, midpoint=0.75, name='shifted')
shrunk_cmap = shiftedColorMap(orig_cmap, start=0.15, midpoint=0.75, stop=0.85, name='shrunk')

fig = plt.figure(figsize=(6,6))
grid = AxesGrid(fig, 111, nrows_ncols=(2, 2), axes_pad=0.5,
                label_mode="1", share_all=True,
                cbar_location="right", cbar_mode="each",
                cbar_size="7%", cbar_pad="2%")

# normal cmap
im0 = grid[0].imshow(biased_data, interpolation="none", cmap=orig_cmap)
grid.cbar_axes[0].colorbar(im0)
grid[0].set_title('Default behavior (hard to see bias)', fontsize=8)

im1 = grid[1].imshow(biased_data, interpolation="none", cmap=orig_cmap, vmax=15, vmin=-15)
grid.cbar_axes[1].colorbar(im1)
grid[1].set_title('Centered zero manually,\nbut lost upper end of dynamic range', fontsize=8)

im2 = grid[2].imshow(biased_data, interpolation="none", cmap=shifted_cmap)
grid.cbar_axes[2].colorbar(im2)
grid[2].set_title('Recentered cmap with function', fontsize=8)

im3 = grid[3].imshow(biased_data, interpolation="none", cmap=shrunk_cmap)
grid.cbar_axes[3].colorbar(im3)
grid[3].set_title('Recentered cmap with function\nand shrunk range', fontsize=8)

for ax in grid:
    ax.set_yticks([])
    ax.set_xticks([])

Results of the example:

2 of 11
61

Note that in matplotlib version 3.2+ the TwoSlopeNorm class was added. I think it covers your use case. It can be used like this:

from matplotlib import colors
divnorm=colors.TwoSlopeNorm(vmin=-5., vcenter=0., vmax=10)
pcolormesh(your_data, cmap="coolwarm", norm=divnorm)

In matplotlib 3.1 the class was called DivergingNorm.

🌐
DataCamp
datacamp.com › tutorial › matplotlib-colormaps
Matplotlib Colormaps: Customizing Your Color Schemes | DataCamp
November 9, 2024 - An example of a sequential colormap is in heatmaps for temperature data, where lighter colors represent cooler temperatures and darker shades represent hotter temperatures. Assume you have a dataset with date and temperature columns. The following code will create a heatmap for the temperature. # Import required libraries import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib.colors as mcolors # Step 1: Extract month and day information from the date column calendar_data = temperature_data.copy() # Create a copy to work with calendar_data['Month'] = calendar_d
🌐
Tonysyu
tonysyu.github.io › mpltools › auto_examples › color › plot_cycle_cmap.html
Color cycle from a colormap — mpltools docs
import numpy as np import matplotlib.pyplot as plt from mpltools import layout from mpltools import color n_lines = 10 # Change default color cycle for all new axes color.cycle_cmap(n_lines) figsize = layout.figaspect(aspect_ratio=0.5) fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=figsize) # Change color cycle specifically for `ax2` color.cycle_cmap(n_lines, cmap='pink', ax=ax2) x = np.linspace(0, 10) for shift in np.linspace(0, np.pi, n_lines): ax1.plot(x, np.sin(x - shift), linewidth=2) ax2.plot(x, np.sin(x - shift), linewidth=2) plt.show()
🌐
GeeksforGeeks
geeksforgeeks.org › how-to-reverse-a-colormap-using-matplotlib-in-python
How to reverse a Colormap using Matplotlib in Python? - GeeksforGeeks
December 14, 2021 - In this first plot higher values are shown with yellow color whereas lower values are shown with dark blue color whereas in the second plot after reversing the colormap higher values are shown with dark blue color and lower values are shown with yellow color. There are many custom colormaps present in matplotlib library like inferno, cividis, magma, plasma, etc.
🌐
Readthedocs
proplot.readthedocs.io › en › latest › colormaps.html
Colormaps — ProPlot documentation - Read the Docs
Proplot lets you create modified versions of existing colormaps using the Colormap constructor function and the new ContinuousColormap and DiscreteColormap classes, which replace the native matplotlib colormap classes.
🌐
Matplotlib
matplotlib.org › stable › tutorials › colors › colormap-manipulation.html
Creating Colormaps in Matplotlib — Matplotlib 3.7.2 documentation
May 1, 2020 - First, getting a named colormap, most of which are listed in Choosing Colormaps in Matplotlib, may be done using matplotlib.colormaps, which returns a colormap object. The length of the list of colors used internally to define the colormap can be adjusted via Colormap.resampled.
🌐
Chris35wills
chris35wills.github.io › matplotlib_diverging_colorbar
Centre a diverging colorbar at a defined value with matplotlib
August 19, 2016 - import matplotlib import matplotlib.pyplot as plt import matplotlib.colors as colors import numpy as np # create an array of random vlues - you might read in a raster dataset x=25 y=25 ras=np.random.randint(-1000,3000,size=(x*y)).reshape(x,y) cmap=matplotlib.cm.RdBu_r # set the colormap to soemthing diverging plt.imshow(ras, cmap=cmap), plt.colorbar(), plt.show()
🌐
Matplotlib
matplotlib.org › stable › tutorials › colors › colormapnorms.html
Matplotlib
August 13, 2021 - Matplotlib has support for visualizing information with a wide array of colors and colormaps.