Tutorial 10 - Matplotlib NxM Subplots and Spacing

This tutorial shows how to build NxM subplot grids with the matplotlib backend and control the distance between plots using wspace=... and hspace=....

It includes both:

  • line plots

  • color plots (add_imshow)

and prints measured subplot gaps so you can verify spacing changes numerically.

[1]:
import numpy as np
import matplotlib.pyplot as plt

from maxplotlib import Canvas
[2]:
def measure_gaps(axes):
    """Measure one representative horizontal and vertical subplot gap."""
    horizontal_gap = axes[0, 1].get_position().x0 - axes[0, 0].get_position().x1
    vertical_gap = axes[0, 0].get_position().y0 - axes[1, 0].get_position().y1
    return horizontal_gap, vertical_gap

1 · NxM line plots with spacing control

[3]:
x = np.linspace(0, 2 * np.pi, 200)

tight_canvas, tight_axes = Canvas.subplots(
    nrows=2,
    ncols=3,
    width="14cm",
    ratio=0.65,
    wspace=0.05,
    hspace=0.08,
)
for i, row in enumerate(tight_axes):
    for j, ax in enumerate(row):
        tight_canvas.plot(
            x, np.sin((i + 1) * (j + 1) * x), label=f"sin({(i + 1) * (j + 1)}x)"
        )
        tight_canvas.set_title(f"line {i},{j}")

tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib")
tight_fig.suptitle("Line plots - tight spacing")
tight_h, tight_v = measure_gaps(tight_m_axes)
print(f"tight line gaps: h={tight_h:.4f}, v={tight_v:.4f}")

loose_canvas, loose_axes = Canvas.subplots(
    nrows=2,
    ncols=3,
    width="14cm",
    ratio=0.65,
    wspace=0.45,
    hspace=0.45,
)
for i, row in enumerate(loose_axes):
    for j, ax in enumerate(row):
        loose_canvas.plot(
            x, np.sin((i + 1) * (j + 1) * x), label=f"sin({(i + 1) * (j + 1)}x)"
        )
        loose_canvas.set_title(f"line {i},{j}")

loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib")
loose_fig.suptitle("Line plots - loose spacing")
loose_h, loose_v = measure_gaps(loose_m_axes)
print(f"loose line gaps: h={loose_h:.4f}, v={loose_v:.4f}")

assert loose_h > tight_h
assert loose_v > tight_v
tight line gaps: h=0.0125, v=0.0296
loose line gaps: h=0.0894, v=0.1414
../_images/tutorials_tutorial_10_matplotlib_nxm_spacing_4_1.png
../_images/tutorials_tutorial_10_matplotlib_nxm_spacing_4_2.png

2 · NxM color plots (imshow) with spacing control

[4]:
base = np.arange(100).reshape(10, 10)

tight_canvas, tight_axes = Canvas.subplots(
    nrows=2,
    ncols=3,
    width="14cm",
    ratio=0.75,
    wspace=0.05,
    hspace=0.08,
)
idx = 0
for row in tight_axes:
    for ax in row:
        tight_canvas.imshow(base + idx, cmap="viridis")
        tight_canvas.set_title(f"heatmap {idx}")
        idx += 1

tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib")
tight_fig.suptitle("Color plots - tight spacing")
tight_h, tight_v = measure_gaps(tight_m_axes)
print(f"tight color gaps: h={tight_h:.4f}, v={tight_v:.4f}")

loose_canvas, loose_axes = Canvas.subplots(
    nrows=2,
    ncols=3,
    width="14cm",
    ratio=0.75,
    wspace=0.45,
    hspace=0.45,
)
idx = 0
for row in loose_axes:
    for ax in row:
        loose_canvas.imshow(base + idx, cmap="viridis")
        loose_canvas.set_title(f"heatmap {idx}")
        idx += 1

loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib")
loose_fig.suptitle("Color plots - loose spacing")
loose_h, loose_v = measure_gaps(loose_m_axes)
print(f"loose color gaps: h={loose_h:.4f}, v={loose_v:.4f}")

assert loose_h > tight_h
assert loose_v > tight_v

plt.show()
tight color gaps: h=0.0125, v=0.0480
loose color gaps: h=0.0894, v=0.1661
../_images/tutorials_tutorial_10_matplotlib_nxm_spacing_6_1.png
../_images/tutorials_tutorial_10_matplotlib_nxm_spacing_6_2.png

Plotly preview

The spacing measurements above are Matplotlib-specific. You can still preview the same canvas with the Plotly backend (layout/spacing will differ):

[5]:
# Reuse the last canvas created above
loose_canvas.show(backend="plotly")

Data type cannot be displayed: application/vnd.plotly.v1+json