Skip to content

Commit 6529163

Browse files
feat(plotnine): implement subplot-grid (#2824)
## Implementation: `subplot-grid` - plotnine Implements the **plotnine** version of `subplot-grid`. **File:** `plots/subplot-grid/implementations/plotnine.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20602450179)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent bbc686b commit 6529163

2 files changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
""" pyplots.ai
2+
subplot-grid: Subplot Grid Layout
3+
Library: plotnine 0.15.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from plotnine import (
10+
aes,
11+
element_line,
12+
element_rect,
13+
element_text,
14+
geom_bar,
15+
geom_histogram,
16+
geom_line,
17+
geom_point,
18+
ggplot,
19+
labs,
20+
scale_color_manual,
21+
scale_fill_manual,
22+
scale_x_continuous,
23+
stat_smooth,
24+
theme,
25+
theme_minimal,
26+
)
27+
28+
29+
# Data - Product performance dashboard
30+
np.random.seed(42)
31+
32+
# Daily product metrics
33+
n_days = 40
34+
days = pd.date_range("2024-01-01", periods=n_days, freq="D")
35+
products = ["A", "B"]
36+
37+
# Generate time series data
38+
data_list = []
39+
for product in products:
40+
base = 100 if product == "A" else 85
41+
trend = 0.5 if product == "A" else 0.8
42+
sales = base + np.arange(n_days) * trend + np.random.randn(n_days) * 10
43+
data_list.append(pd.DataFrame({"date": days, "sales": sales, "product": product}))
44+
45+
df_timeseries = pd.concat(data_list, ignore_index=True)
46+
df_timeseries["day_num"] = (df_timeseries["date"] - df_timeseries["date"].min()).dt.days
47+
48+
# Category breakdown data
49+
categories = ["Q1", "Q2", "Q3", "Q4"]
50+
revenues = [45, 32, 28, 18]
51+
df_category = pd.DataFrame({"category": categories, "revenue": revenues})
52+
df_category["category"] = pd.Categorical(df_category["category"], categories=categories, ordered=True)
53+
54+
# Product distribution data
55+
df_prod_a = df_timeseries[df_timeseries["product"] == "A"]["sales"]
56+
57+
# Scatter data - relationship between units sold and profit margin
58+
units = np.random.uniform(100, 500, 60)
59+
margin = 20 + 0.03 * units + np.random.randn(60) * 5
60+
df_scatter = pd.DataFrame({"units": units, "margin": margin})
61+
62+
# Define colors
63+
colors = ["#306998", "#FFD43B"]
64+
65+
# Shared theme for all plots - sized for 4800x2700 canvas
66+
base_theme = theme_minimal() + theme(
67+
plot_title=element_text(size=22, face="bold", ha="center", margin={"b": 15}),
68+
axis_title=element_text(size=18),
69+
axis_text=element_text(size=14),
70+
legend_text=element_text(size=14),
71+
legend_title=element_text(size=16, face="bold"),
72+
panel_grid_major=element_line(color="#cccccc", alpha=0.3),
73+
panel_grid_minor=element_line(color="#eeeeee", alpha=0.2),
74+
panel_background=element_rect(fill="white"),
75+
plot_margin=0.02,
76+
)
77+
78+
# Plot 1: Sales trend over time (Line chart)
79+
p1 = (
80+
ggplot(df_timeseries, aes(x="day_num", y="sales", color="product"))
81+
+ geom_line(size=1.5)
82+
+ geom_point(size=3, alpha=0.7)
83+
+ stat_smooth(method="lm", se=False, linetype="dashed", size=1.0)
84+
+ scale_color_manual(values=colors)
85+
+ labs(title="Sales Trend", x="Day", y="Sales (Units)", color="Product")
86+
+ base_theme
87+
+ theme(legend_position="right")
88+
)
89+
90+
# Plot 2: Revenue by category (Bar chart)
91+
p2 = (
92+
ggplot(df_category, aes(x="category", y="revenue", fill="category"))
93+
+ geom_bar(stat="identity", width=0.7, show_legend=False)
94+
+ scale_fill_manual(values=["#306998", "#4A8BBF", "#6BA3D6", "#FFD43B"])
95+
+ labs(title="Quarterly Revenue", x="Quarter", y="Revenue (k$)")
96+
+ base_theme
97+
)
98+
99+
# Plot 3: Sales distribution histogram for Product A
100+
df_hist = pd.DataFrame({"sales": df_prod_a.values})
101+
p3 = (
102+
ggplot(df_hist, aes(x="sales"))
103+
+ geom_histogram(bins=10, fill="#306998", color="white", alpha=0.8)
104+
+ scale_x_continuous(breaks=[90, 105, 120])
105+
+ labs(title="Sales Distribution (Product A)", x="Sales (Units)", y="Frequency")
106+
+ base_theme
107+
)
108+
109+
# Plot 4: Units vs Margin scatter plot
110+
p4 = (
111+
ggplot(df_scatter, aes(x="units", y="margin"))
112+
+ geom_point(size=4, color="#306998", alpha=0.7)
113+
+ stat_smooth(method="lm", color="#FFD43B", se=True, fill="#FFD43B", alpha=0.2)
114+
+ labs(title="Units vs Margin", x="Units Sold", y="Profit Margin (%)")
115+
+ base_theme
116+
)
117+
118+
# Compose into 2x2 grid using plotnine's composition operators
119+
# | = beside (columns), / = stack (rows)
120+
top_row = p1 | p2
121+
bottom_row = p3 | p4
122+
grid = top_row / bottom_row
123+
124+
# Draw the grid and add a main figure title using matplotlib's suptitle
125+
fig = grid.draw()
126+
# Resize figure with extra height for the main title (16:9 base + title space)
127+
fig.set_size_inches(16, 11)
128+
# Compress subplots significantly to make room for the main title at the top
129+
fig.subplots_adjust(top=0.76, bottom=0.08, hspace=0.40, wspace=0.25)
130+
# Add main title using suptitle positioned well above all subplot titles
131+
fig.suptitle("subplot-grid · plotnine · pyplots.ai", fontsize=28, fontweight="bold", y=0.90)
132+
fig.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: plotnine
2+
specification_id: subplot-grid
3+
created: '2025-12-30T17:50:18Z'
4+
updated: '2025-12-30T18:22:27Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20602450179
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 0.15.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-grid/plotnine/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-grid/plotnine/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent use of plotnine native patchwork-style composition operators (| and
17+
/) for grid layout
18+
- Clean implementation of 4 distinct plot types demonstrating subplot-grid versatility
19+
- Good color scheme with consistent blue-yellow palette across all subplots
20+
- Proper use of theme customization with shared base_theme for consistency
21+
- Realistic business dashboard scenario with coherent metrics
22+
weaknesses:
23+
- Main title positioning overlaps slightly with Sales Trend subplot title - could
24+
benefit from more vertical spacing
25+
- The legend in the sales trend plot consumes plot area; consider positioning outside

0 commit comments

Comments
 (0)