A dashboard mixing group shapes and deleted axes

The realistic case GroupLayout is actually for: a dashboard whose panels are genuinely different shapes, not a tidy repeated pattern. Six groups share one GroupLayout(2, 3) outer grid – three different plain rectangular shapes, plus one with a mask describing a checkerboard of present/deleted cells – and subplots_from_groups() works out the one shared grid (a least-common-multiple resolution across every group’s own row/column count) that fits all of them as ordinary, non-overlapping spans, with no per-group math to do by hand.

The checkerboard group (“Diagnostics”) is the strange combination: an uneven-shaped neighbor (the tall single-column “History” group) still shares one grid with it, just at whatever resolution both need – the checkerboard mask rides on top of that same shared grid rather than needing its own separate one.

Keep every group’s row count sharing a small common multiple (and likewise for columns) – 2, 4, and 8 rather than 2, 3, and 5. The whole figure resolves onto one grid sized to the least common multiple of every group’s own shape, so shapes with incompatible prime factors (a literal 5-wide group here would force a much finer shared grid than a 4-wide one) can blow that resolution up far past what a dense grid can actually afford – see GroupLayout’s own docstring for what happens past that point.

Dismissing “Alerts” below uses remove_group() – see Finding a group or axes again: title, id, or position for every way to find a group or axes again, gathered in one place.

plot 15 dashboard mixed shapes and masks

This figure has 22 panels — too many to usefully embed at a fixed size.

Open the full interactive example in a new page to pick a tool, then zoom, pan, point-pick or annotate any panel.

View this figure’s Vega export ↗ — the raw JSON spec, rendered live by a real Vega engine.

View this figure’s Vega-Lite export ↗ — the raw JSON spec(s), rendered live by a real Vega-Lite engine.

groups remaining: ['Engine', 'Status lights', 'History', 'Diagnostics', 'Fuel / battery']

import numpy as np
import plotpress

rng = np.random.default_rng(11)
t = np.linspace(0, 4 * np.pi, 200)

layout = plotpress.GroupLayout(2, 3)
layout.add_group(0, 0, 2, 2, title="Engine", color="#d62728")
layout.add_group(0, 1, 1, 4, title="Status lights", color="#ff7f0e")
layout.add_group(0, 2, 4, 1, title="History", color="#2ca02c")
layout.add_group(1, 0, 4, 4, title="Diagnostics", color="#1f77b4",
                mask=[[1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1]])
layout.add_group(1, 1, 2, 1, title="Fuel / battery", color="#9467bd")
layout.add_group(1, 2, 1, 1, title="Alerts", color="#8c564b")

fig, axes = plotpress.subplots_from_groups(layout, figsize=(13, 8))

# Engine: 2x2 -- a few related trend lines.
for ax in axes[0, 0].ravel():
    ax.plot(t, np.sin(t + rng.uniform(0, 6)) * (1 + 0.2 * rng.standard_normal()),
           color="#d62728")
    ax.set_xticks([]); ax.set_yticks([])

# Status lights: 1x5 -- simple on/off indicators as bars.
for i, ax in enumerate(axes[0, 1]):
    ax.bar([0], [1 if rng.random() > 0.3 else 0.15], color="#ff7f0e", width=0.6)
    ax.set_ylim(0, 1)
    ax.set_xticks([]); ax.set_yticks([])

# History: 3x1 -- a tall stack of trend panels.
for ax in axes[0, 2]:
    ax.plot(t, np.cumsum(rng.standard_normal(t.size)) * 0.1, color="#2ca02c")
    ax.set_xticks([]); ax.set_yticks([])

# Diagnostics: 3x3 checkerboard -- only the mask's True cells exist.
for ax in axes[1, 0].ravel():
    if ax is None:
        continue
    ax.plot(t, np.sin(2 * t + rng.uniform(0, 6)), color="#1f77b4", linewidth=1.0)
    ax.set_xticks([]); ax.set_yticks([])

# Fuel / battery: 2x1.
for ax in axes[1, 1]:
    ax.barh([0], [rng.uniform(0.3, 1.0)], color="#9467bd")
    ax.set_xlim(0, 1)
    ax.set_xticks([]); ax.set_yticks([])

# Alerts: a single panel.
axes[1, 2].pie([3, 1], colors=["#8c564b", "#ecd9d0"])

fig.group_spacing(wspace=18.0, hspace=22.0)
fig.suptitle("Instrument dashboard: mixed group shapes and deleted axes")
fig.tight_layout()

# An acknowledged alert dismisses its whole panel -- fig.remove_group()
# removes the group's axes (via the now id/group-aware Axes.remove()) and
# its own box/title registration together, leaving a blank rectangle
# rather than reflowing the rest of the dashboard to fill it (the same
# thing Axes.remove() already does for one axes).
fig.remove_group(title="Alerts")
print("groups remaining:", [g.title for g in fig.get_groups()])

Total running time of the script: (0 minutes 0.369 seconds)

Gallery generated by Sphinx-Gallery