Matplotlib Custom Colormaps and Normalizations

This article provides a practical guide on configuring custom colormaps and data normalizations using Python's matplotlib.colors module. You will learn how to create discrete and continuous colormaps using ListedColormap and LinearSegmentedColormap, as well as how to control the mapping of numerical values to these colors using classes such as Normalize, LogNorm, TwoSlopeNorm, and BoundaryNorm.


Creating Custom Colormaps

Matplotlib maps scalar data to colors using colormaps. The matplotlib.colors module offers two primary classes for building custom palettes: ListedColormap for discrete steps and LinearSegmentedColormap for smooth gradients.

1. Discrete Palettes with ListedColormap

ListedColormap defines a colormap from an explicit list of color names, hex codes, or RGBA tuples.

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

# Define a discrete list of colors
colors = ["#264653", "#2a9d8f", "#e9c46a", "#f4a261", "#e76f51"]
custom_listed = ListedColormap(colors, name="custom_desert")

2. Continuous Gradients with LinearSegmentedColormap

LinearSegmentedColormap calculates smooth transitions between specified color stops.

From a List of Colors

The simplest approach is from_list(), which evenly spaces the colors across the range:

from matplotlib.colors import LinearSegmentedColormap

# Smooth gradient from dark blue to white to dark red
custom_gradient = LinearSegmentedColormap.from_list(
    "blue_white_red", ["#00008b", "#ffffff", "#8b0000"]
)

From a Channel Dictionary

For exact control over where transitions occur along the \([0, 1]\) interval, specify anchor points for red, green, and blue channels:

cdict = {
    "red":   [(0.0, 0.0, 0.0),
              (0.5, 1.0, 1.0),
              (1.0, 1.0, 1.0)],
    "green": [(0.0, 0.0, 0.0),
              (1.0, 0.0, 0.0)],
    "blue":  [(0.0, 1.0, 1.0),
              (0.5, 1.0, 1.0),
              (1.0, 0.0, 0.0)]
}

custom_segmented = LinearSegmentedColormap("custom_diverging", cdict)

Configuring Color Normalizations

Normalization is the process of mapping arbitrary data values onto the interval \([0.0, 1.0]\) before passing them to a colormap.

1. Linear Normalization (Normalize)

Normalize scales data linearly between a specified minimum (vmin) and maximum (vmax):

from matplotlib.colors import Normalize

norm = Normalize(vmin=0, vmax=100)

2. Logarithmic Normalization (LogNorm)

LogNorm is suitable for data spanning multiple orders of magnitude. vmin must be strictly positive:

from matplotlib.colors import LogNorm

norm = LogNorm(vmin=1e-2, vmax=1e4)

3. Diverging Data with a Fixed Center (TwoSlopeNorm)

TwoSlopeNorm (formerly DivergingNorm) scales values on either side of a chosen midpoint (vcenter) with different linear slopes. This is ideal for data centered around zero or a baseline average:

from matplotlib.colors import TwoSlopeNorm

# Negative values map from -50 to 0; positive values map from 0 to 200
norm = TwoSlopeNorm(vmin=-50, vcenter=0, vmax=200)

4. Binning Values (BoundaryNorm)

BoundaryNorm groups continuous data into discrete bins, mapping each bin directly to a color index in a ListedColormap:

from matplotlib.colors import BoundaryNorm

bounds = [0, 10, 25, 50, 100]
norm = BoundaryNorm(boundaries=bounds, ncolors=custom_listed.N)

Applied Example

To apply both a custom colormap and normalization, pass them via the cmap and norm arguments in plotting functions such as imshow, scatter, or pcolormesh:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, TwoSlopeNorm

# Generate sample diverging data
data = np.random.randn(20, 20) * 10

# Configure colormap and normalization
cmap = LinearSegmentedColormap.from_list("cool_warm", ["#1f77b4", "#f7f7f7", "#d62728"])
norm = TwoSlopeNorm(vmin=-20, vcenter=0, vmax=30)

# Plot
fig, ax = plt.subplots()
cax = ax.imshow(data, cmap=cmap, norm=norm)
fig.colorbar(cax, ax=ax)
plt.show()