Skip to content

Heatmaps with pcolormesh

The ax.pcolormesh() method creates pseudocolor plots with quadrilateral cells, offering more flexibility than imshow for non-uniform grids and coordinate-based data.

Basic pcolormesh

Create a heatmap using pcolormesh.

1. Simple Usage

import matplotlib.pyplot as plt
import numpy as np

np.random.seed(42)
data = np.random.rand(10, 10)

fig, ax = plt.subplots()
pc = ax.pcolormesh(data)
plt.colorbar(pc)
plt.show()

2. With Coordinates

x = np.arange(11)  # 11 edges for 10 cells
y = np.arange(11)
X, Y = np.meshgrid(x, y)

fig, ax = plt.subplots()
pc = ax.pcolormesh(X, Y, data)
plt.colorbar(pc)
plt.show()

3. Cell Centers vs Edges

# pcolormesh uses edges: N+1 coordinates for N cells
# Data shape (10, 10) needs X, Y shape (11, 11)

imshow vs pcolormesh

Understanding when to use each method.

1. Key Differences

# imshow:
#   - Pixels are uniformly sized
#   - Origin at top-left by default
#   - Aspect ratio preserved
#   - Faster for large uniform grids

# pcolormesh:
#   - Cells can be non-uniform
#   - Origin at bottom-left by default
#   - Aspect ratio follows data coordinates
#   - Supports curvilinear grids

2. Origin Behavior

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))

ax1.imshow(data)
ax1.set_title('imshow (origin=upper)')

ax2.pcolormesh(data)
ax2.set_title('pcolormesh (origin=lower)')

plt.tight_layout()
plt.show()

3. Matching Origins

# Make imshow match pcolormesh
ax.imshow(data, origin='lower')

# Or flip data for pcolormesh to match imshow
ax.pcolormesh(data[::-1])

Non-Uniform Grids

pcolormesh excels with irregularly spaced data.

1. Logarithmic Spacing

x = np.logspace(0, 2, 11)  # 1 to 100, log-spaced
y = np.linspace(0, 10, 11)
X, Y = np.meshgrid(x, y)

data = np.random.rand(10, 10)

fig, ax = plt.subplots()
pc = ax.pcolormesh(X, Y, data)
ax.set_xscale('log')
plt.colorbar(pc)
plt.show()

2. Variable Cell Sizes

x = np.array([0, 1, 2, 4, 8, 16])  # Non-uniform spacing
y = np.array([0, 1, 3, 6, 10])
X, Y = np.meshgrid(x, y)

data = np.random.rand(4, 5)

fig, ax = plt.subplots()
pc = ax.pcolormesh(X, Y, data)
plt.colorbar(pc)
plt.show()

3. Time Series Data

import datetime

dates = [datetime.datetime(2024, 1, i) for i in range(1, 12)]
hours = np.arange(25)
data = np.random.rand(24, 10)

fig, ax = plt.subplots(figsize=(10, 5))
pc = ax.pcolormesh(dates, hours, data)
ax.set_ylabel('Hour')
plt.colorbar(pc)
plt.show()

Shading Options

The shading keyword controls cell rendering.

1. Flat Shading (Default)

ax.pcolormesh(X, Y, data, shading='flat')
# Requires X, Y one larger than data in each dimension

2. Auto Shading

# Automatically handles coordinate/data size mismatch
ax.pcolormesh(X, Y, data, shading='auto')

3. Gouraud Shading

# Interpolated colors at vertices
# Requires X, Y same shape as data
x = np.arange(10)
y = np.arange(10)
X, Y = np.meshgrid(x, y)

ax.pcolormesh(X, Y, data, shading='gouraud')

Edge Colors

Add grid lines between cells.

1. Black Edges

fig, ax = plt.subplots()
pc = ax.pcolormesh(data, edgecolors='black', linewidth=0.5)
plt.colorbar(pc)
plt.show()

2. White Edges

pc = ax.pcolormesh(data, edgecolors='white', linewidth=1)

3. Face Color Only

pc = ax.pcolormesh(data, edgecolors='face')  # Edges match cell color

Colormap and Range

Control color mapping identical to imshow.

1. Colormap Selection

pc = ax.pcolormesh(data, cmap='viridis')
pc = ax.pcolormesh(data, cmap='coolwarm')

2. Value Range

pc = ax.pcolormesh(data, vmin=0, vmax=1)

3. Centered Diverging

data_centered = np.random.randn(10, 10)
max_abs = np.abs(data_centered).max()
pc = ax.pcolormesh(data_centered, cmap='RdBu', vmin=-max_abs, vmax=max_abs)

Masked Data

Handle missing or invalid values.

1. Create Masked Array

data = np.random.rand(10, 10)
mask = data < 0.2
masked_data = np.ma.masked_array(data, mask)

fig, ax = plt.subplots()
pc = ax.pcolormesh(masked_data)
plt.colorbar(pc)
plt.show()

2. Set Bad Color

cmap = plt.cm.viridis.copy()
cmap.set_bad('gray')

pc = ax.pcolormesh(masked_data, cmap=cmap)

3. NaN Values

data_with_nan = data.copy()
data_with_nan[data < 0.2] = np.nan

pc = ax.pcolormesh(data_with_nan, cmap=cmap)

Practical Example

Create a complete heatmap with pcolormesh.

1. Generate Structured Data

x = np.linspace(-3, 3, 50)
y = np.linspace(-3, 3, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)

2. Create Visualization

fig, ax = plt.subplots(figsize=(8, 6))

pc = ax.pcolormesh(X, Y, Z, cmap='RdBu', shading='auto')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title('sin(x) × cos(y)')
ax.set_aspect('equal')

plt.colorbar(pc, label='Value')
plt.tight_layout()
plt.show()

3. Add Contour Overlay

fig, ax = plt.subplots(figsize=(8, 6))

pc = ax.pcolormesh(X, Y, Z, cmap='RdBu', shading='auto', alpha=0.8)
ax.contour(X, Y, Z, colors='black', linewidths=0.5, levels=10)

plt.colorbar(pc)
plt.show()