Skip to content

Commit 54f9beb

Browse files
feat(matplotlib): implement subplot-mosaic (#3028)
## Implementation: `subplot-mosaic` - matplotlib Implements the **matplotlib** version of `subplot-mosaic`. **File:** `plots/subplot-mosaic/implementations/matplotlib.py` **Parent Issue:** #3002 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20617508043)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent d0bfd78 commit 54f9beb

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
""" pyplots.ai
2+
subplot-mosaic: Mosaic Subplot Layout with Varying Sizes
3+
Library: matplotlib 3.10.8 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-31
5+
"""
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
10+
11+
# Data
12+
np.random.seed(42)
13+
14+
# Time series data for main overview chart
15+
dates = np.arange(100)
16+
sales = np.cumsum(np.random.randn(100) * 5 + 2) + 500
17+
18+
# Category data for bar chart
19+
categories = ["Product A", "Product B", "Product C", "Product D"]
20+
values = [85, 120, 95, 110]
21+
22+
# Scatter data for correlation plot
23+
x_scatter = np.random.normal(50, 15, 80)
24+
y_scatter = x_scatter * 0.8 + np.random.normal(0, 10, 80)
25+
26+
# Histogram data
27+
measurements = np.random.normal(100, 20, 200)
28+
29+
# Metric values for small panels
30+
metrics = {"Growth": 12.5, "Conversion": 3.2, "Retention": 87.4}
31+
32+
# Create mosaic layout: "AAB;AAB;CDE" pattern
33+
# A = large overview (spans 2 rows, 2 cols), B = bar chart (right side, 2 rows)
34+
# C, D, E = three small panels at bottom
35+
mosaic = """
36+
AAB
37+
AAB
38+
CDE
39+
"""
40+
41+
fig, axes = plt.subplot_mosaic(mosaic, figsize=(16, 9))
42+
43+
# A: Main time series overview
44+
axes["A"].plot(dates, sales, linewidth=3, color="#306998")
45+
axes["A"].fill_between(dates, sales.min(), sales, alpha=0.3, color="#306998")
46+
axes["A"].set_xlabel("Day", fontsize=18)
47+
axes["A"].set_ylabel("Cumulative Sales ($)", fontsize=18)
48+
axes["A"].set_title("Sales Overview", fontsize=22)
49+
axes["A"].tick_params(axis="both", labelsize=14)
50+
axes["A"].grid(True, alpha=0.3, linestyle="--")
51+
52+
# B: Bar chart for categories
53+
bars = axes["B"].barh(categories, values, color="#FFD43B", edgecolor="#306998", linewidth=2)
54+
axes["B"].set_xlabel("Units Sold", fontsize=18)
55+
axes["B"].set_title("Product Performance", fontsize=22)
56+
axes["B"].tick_params(axis="both", labelsize=14)
57+
axes["B"].grid(True, alpha=0.3, linestyle="--", axis="x")
58+
# Add value labels
59+
for bar, val in zip(bars, values, strict=True):
60+
axes["B"].text(val + 2, bar.get_y() + bar.get_height() / 2, str(val), va="center", fontsize=14)
61+
62+
# C: Scatter plot
63+
axes["C"].scatter(x_scatter, y_scatter, s=100, alpha=0.7, color="#306998", edgecolor="white", linewidth=1)
64+
axes["C"].set_xlabel("Feature X", fontsize=16)
65+
axes["C"].set_ylabel("Feature Y", fontsize=16)
66+
axes["C"].set_title("Correlation", fontsize=20)
67+
axes["C"].tick_params(axis="both", labelsize=12)
68+
axes["C"].grid(True, alpha=0.3, linestyle="--")
69+
70+
# D: Histogram
71+
axes["D"].hist(measurements, bins=20, color="#306998", edgecolor="white", linewidth=1, alpha=0.8)
72+
axes["D"].set_xlabel("Value", fontsize=16)
73+
axes["D"].set_ylabel("Frequency", fontsize=16)
74+
axes["D"].set_title("Distribution", fontsize=20)
75+
axes["D"].tick_params(axis="both", labelsize=12)
76+
axes["D"].grid(True, alpha=0.3, linestyle="--", axis="y")
77+
78+
# E: Metrics display
79+
axes["E"].set_xlim(0, 1)
80+
axes["E"].set_ylim(0, 1)
81+
axes["E"].axis("off")
82+
axes["E"].set_title("Key Metrics", fontsize=20)
83+
y_positions = [0.75, 0.45, 0.15]
84+
for (name, value), y_pos in zip(metrics.items(), y_positions, strict=True):
85+
axes["E"].text(0.5, y_pos, name, ha="center", va="center", fontsize=16, fontweight="bold")
86+
if name == "Growth":
87+
axes["E"].text(0.5, y_pos - 0.12, f"+{value}%", ha="center", va="center", fontsize=24, color="#306998")
88+
elif name == "Conversion":
89+
axes["E"].text(0.5, y_pos - 0.12, f"{value}%", ha="center", va="center", fontsize=24, color="#306998")
90+
else:
91+
axes["E"].text(0.5, y_pos - 0.12, f"{value}%", ha="center", va="center", fontsize=24, color="#306998")
92+
# Add box around metrics
93+
axes["E"].add_patch(plt.Rectangle((0.05, 0.02), 0.9, 0.96, fill=False, edgecolor="#306998", linewidth=2))
94+
95+
# Main title
96+
fig.suptitle("subplot-mosaic · matplotlib · pyplots.ai", fontsize=26, fontweight="bold", y=0.98)
97+
98+
plt.tight_layout(rect=[0, 0, 1, 0.95])
99+
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: matplotlib
2+
specification_id: subplot-mosaic
3+
created: '2025-12-31T10:56:15Z'
4+
updated: '2025-12-31T11:07:18Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20617508043
7+
issue: 3002
8+
python_version: 3.13.11
9+
library_version: 3.10.8
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-mosaic/matplotlib/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-mosaic/matplotlib/plot_thumb.png
12+
preview_html: null
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent demonstration of plt.subplot_mosaic() with the AAB;AAB;CDE pattern -
17+
this is exactly what the spec asked for
18+
- Great variety of plot types (line with fill_between, horizontal bar, scatter,
19+
histogram, text metrics)
20+
- Clean, professional color scheme with accessible blue/yellow palette
21+
- Well-structured dashboard layout with clear visual hierarchy (large overview,
22+
medium bar chart, small detail panels)
23+
- Realistic sales dashboard scenario with appropriate data scales
24+
weaknesses:
25+
- Scatter plot markers could be larger (s=100 is adequate but s=150 would be more
26+
visible for 80 points)
27+
- Missing demonstration of empty cells with . placeholder mentioned in spec notes
28+
- Metrics panel (E) layout could be tighter - the box has significant internal whitespace

0 commit comments

Comments
 (0)