Skip to content

Axes Method - hist2d

The ax.hist2d() method creates 2D histograms with rectangular bins.

Official Documentation

Basic Usage

2D Histogram with Colorbar

import matplotlib.pyplot as plt
import numpy as np

def main():
    mean = [0, 0]
    cov = [[1, 1], [1, 2]]
    x = np.random.multivariate_normal(mean, cov, 10000)
    print(f"{x.shape = }")

    fig, ax = plt.subplots()
    _, _, _, a = ax.hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')  # type(a) QuadMesh
    plt.colorbar(a, label='counts in bin')
    plt.show()

if __name__ == "__main__":
    main()

Return Values

Understanding the Return Tuple

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, ax = plt.subplots()
h, xedges, yedges, image = ax.hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')

print(f"h (counts): {h.shape}")           # (30, 30) - bin counts
print(f"xedges: {xedges.shape}")          # (31,) - x bin edges
print(f"yedges: {yedges.shape}")          # (31,) - y bin edges
print(f"image type: {type(image)}")       # QuadMesh

plt.colorbar(image, label='counts')
plt.show()

bins Parameter

Number of Bins

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 4, figsize=(16, 4))
bin_counts = [10, 20, 30, 50]

for ax, bins in zip(axes, bin_counts):
    _, _, _, im = ax.hist2d(x[:, 0], x[:, 1], bins=bins, cmap='Blues')
    ax.set_title(f'bins={bins}')
    fig.colorbar(im, ax=ax, shrink=0.8)

plt.tight_layout()
plt.show()

Asymmetric Bins

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Symmetric bins
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('bins=30')
fig.colorbar(im1, ax=axes[0])

# Asymmetric bins
_, _, _, im2 = axes[1].hist2d(x[:, 0], x[:, 1], bins=[40, 20], cmap='Blues')
axes[1].set_title('bins=[40, 20]')
fig.colorbar(im2, ax=axes[1])

plt.tight_layout()
plt.show()

Custom Bin Edges

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

# Custom bin edges
xbins = np.linspace(-4, 4, 41)
ybins = np.linspace(-6, 6, 31)

fig, ax = plt.subplots(figsize=(8, 6))
_, _, _, im = ax.hist2d(x[:, 0], x[:, 1], bins=[xbins, ybins], cmap='Blues')
ax.set_title('Custom bin edges')
fig.colorbar(im, label='counts')
plt.show()

Range Parameter

Limiting Data Range

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Full range
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('Full range')
fig.colorbar(im1, ax=axes[0])

# Limited range
_, _, _, im2 = axes[1].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues',
                              range=[[-2, 2], [-3, 3]])
axes[1].set_title('range=[[-2, 2], [-3, 3]]')
fig.colorbar(im2, ax=axes[1])

plt.tight_layout()
plt.show()

Colormaps

Different Colormaps

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(2, 3, figsize=(15, 10))
cmaps = ['Blues', 'Reds', 'viridis', 'plasma', 'hot', 'YlOrRd']

for ax, cmap in zip(axes.flat, cmaps):
    _, _, _, im = ax.hist2d(x[:, 0], x[:, 1], bins=30, cmap=cmap)
    ax.set_title(f"cmap='{cmap}'")
    fig.colorbar(im, ax=ax, shrink=0.8)

plt.tight_layout()
plt.show()

Normalization

norm Parameter

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

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Linear (default)
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('Linear (default)')
fig.colorbar(im1, ax=axes[0])

# Log scale
_, _, _, im2 = axes[1].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues',
                              norm=colors.LogNorm())
axes[1].set_title('LogNorm')
fig.colorbar(im2, ax=axes[1])

# Power norm
_, _, _, im3 = axes[2].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues',
                              norm=colors.PowerNorm(gamma=0.5))
axes[2].set_title('PowerNorm(gamma=0.5)')
fig.colorbar(im3, ax=axes[2])

plt.tight_layout()
plt.show()

Value Limits

vmin and vmax

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Auto limits
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('Auto limits')
fig.colorbar(im1, ax=axes[0])

# Custom vmax
_, _, _, im2 = axes[1].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues', vmax=50)
axes[1].set_title('vmax=50')
fig.colorbar(im2, ax=axes[1])

# Custom vmin and vmax
_, _, _, im3 = axes[2].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues',
                              vmin=10, vmax=80)
axes[2].set_title('vmin=10, vmax=80')
fig.colorbar(im3, ax=axes[2])

plt.tight_layout()
plt.show()

Density Mode

density Parameter

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Counts (default)
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('Counts (default)')
fig.colorbar(im1, ax=axes[0], label='counts')

# Density
_, _, _, im2 = axes[1].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues', density=True)
axes[1].set_title('density=True')
fig.colorbar(im2, ax=axes[1], label='density')

plt.tight_layout()
plt.show()

Minimum Count

cmin Parameter

import matplotlib.pyplot as plt
import numpy as np

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
cmins = [None, 5, 20]

for ax, cmin in zip(axes, cmins):
    _, _, _, im = ax.hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues', cmin=cmin)
    ax.set_title(f'cmin={cmin}')
    fig.colorbar(im, ax=ax, shrink=0.8)

plt.tight_layout()
plt.show()

Comparison: hist2d vs hexbin vs KDE

Side by Side

import matplotlib.pyplot as plt
import numpy as np
from scipy import stats

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x = np.random.multivariate_normal(mean, cov, 10000)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# hist2d
_, _, _, im1 = axes[0].hist2d(x[:, 0], x[:, 1], bins=30, cmap='Blues')
axes[0].set_title('hist2d')
fig.colorbar(im1, ax=axes[0])

# hexbin
im2 = axes[1].hexbin(x[:, 0], x[:, 1], gridsize=30, cmap='Blues')
axes[1].set_title('hexbin')
fig.colorbar(im2, ax=axes[1])

# KDE with imshow
kde = stats.gaussian_kde(x.T)
x_ = np.linspace(-4, 4, 80)
y_ = np.linspace(-6, 6, 80)
X, Y = np.meshgrid(x_, y_)
Z = kde.evaluate(np.vstack([X.ravel(), Y.ravel()])).reshape(X.shape)
im3 = axes[2].imshow(Z, origin='lower', aspect='auto',
                     extent=[-4, 4, -6, 6], cmap='Blues')
axes[2].set_title('KDE (imshow)')
fig.colorbar(im3, ax=axes[2])

plt.tight_layout()
plt.show()

Practical Example

Distribution Analysis

import matplotlib.pyplot as plt
import numpy as np

# Generate bimodal data
np.random.seed(42)
n = 5000
x1 = np.random.multivariate_normal([-1, -1], [[0.5, 0.2], [0.2, 0.5]], n)
x2 = np.random.multivariate_normal([2, 2], [[0.8, -0.3], [-0.3, 0.8]], n)
x = np.vstack([x1, x2])

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

h, xedges, yedges, im = ax.hist2d(
    x[:, 0], x[:, 1], 
    bins=40, 
    cmap='YlOrRd',
    cmin=1
)

ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('Y', fontsize=12)
ax.set_title('2D Histogram: Bimodal Distribution', fontsize=14)

cbar = fig.colorbar(im, ax=ax, label='Counts per bin')

# Add statistics
total_points = len(x)
ax.text(0.02, 0.98, f'Total points: {total_points}',
        transform=ax.transAxes, fontsize=10,
        verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))

plt.tight_layout()
plt.show()