Skip to content

Commit 3d12d5a

Browse files
feat(bokeh): implement subplot-grid-custom (#2877)
## Implementation: `subplot-grid-custom` - bokeh Implements the **bokeh** version of `subplot-grid-custom`. **File:** `plots/subplot-grid-custom/implementations/bokeh.py` **Parent Issue:** #2856 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20608471124)* --------- 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 1273bc4 commit 3d12d5a

2 files changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
""" pyplots.ai
2+
subplot-grid-custom: Custom Subplot Grid Layout
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png
9+
from bokeh.layouts import column, row
10+
from bokeh.models import ColumnDataSource, Title
11+
from bokeh.plotting import figure
12+
13+
14+
# Data
15+
np.random.seed(42)
16+
17+
# Main time series data (simulating daily stock prices)
18+
n_days = 100
19+
dates = np.arange(n_days)
20+
prices = 100 + np.cumsum(np.random.randn(n_days) * 2)
21+
volumes = np.random.uniform(1, 5, n_days) * 1e6
22+
returns = np.diff(prices) / prices[:-1] * 100
23+
24+
# Additional data for supporting plots
25+
categories = ["Product A", "Product B", "Product C", "Product D"]
26+
sales = [45, 72, 38, 65]
27+
28+
scatter_x = np.random.randn(80)
29+
scatter_y = scatter_x * 0.8 + np.random.randn(80) * 0.5
30+
31+
# Column data sources
32+
price_source = ColumnDataSource(data={"x": dates, "y": prices})
33+
bar_source = ColumnDataSource(data={"x": categories, "y": sales})
34+
scatter_source = ColumnDataSource(data={"x": scatter_x, "y": scatter_y})
35+
36+
# Color palette
37+
python_blue = "#306998"
38+
python_yellow = "#FFD43B"
39+
accent_green = "#2E8B57"
40+
accent_red = "#CD5C5C"
41+
42+
# Plot 1: Main time series (large - spans conceptually 2 columns)
43+
p1 = figure(
44+
width=3000,
45+
height=1400,
46+
title="Price Trend Over Time",
47+
x_axis_label="Day",
48+
y_axis_label="Price ($)",
49+
toolbar_location=None,
50+
)
51+
p1.line("x", "y", source=price_source, line_width=4, color=python_blue, alpha=0.9)
52+
p1.scatter("x", "y", source=price_source, size=12, color=python_blue, alpha=0.6)
53+
p1.title.text_font_size = "32pt"
54+
p1.xaxis.axis_label_text_font_size = "24pt"
55+
p1.yaxis.axis_label_text_font_size = "24pt"
56+
p1.xaxis.major_label_text_font_size = "20pt"
57+
p1.yaxis.major_label_text_font_size = "20pt"
58+
p1.grid.grid_line_alpha = 0.3
59+
p1.outline_line_color = "#cccccc"
60+
p1.outline_line_width = 2
61+
62+
# Add main title above p1
63+
main_title = Title(text="subplot-grid-custom · bokeh · pyplots.ai", text_font_size="36pt", align="center")
64+
p1.add_layout(main_title, "above")
65+
66+
# Plot 2: Volume bar chart (right side panel)
67+
p2 = figure(
68+
width=1600,
69+
height=1400,
70+
title="Daily Trading Volume",
71+
x_axis_label="Day",
72+
y_axis_label="Volume (millions)",
73+
toolbar_location=None,
74+
)
75+
p2.vbar(x=dates, top=volumes / 1e6, width=0.7, color=python_yellow, alpha=0.85)
76+
p2.title.text_font_size = "32pt"
77+
p2.xaxis.axis_label_text_font_size = "24pt"
78+
p2.yaxis.axis_label_text_font_size = "24pt"
79+
p2.xaxis.major_label_text_font_size = "20pt"
80+
p2.yaxis.major_label_text_font_size = "20pt"
81+
p2.grid.grid_line_alpha = 0.3
82+
p2.outline_line_color = "#cccccc"
83+
p2.outline_line_width = 2
84+
85+
# Plot 3: Returns histogram (bottom left)
86+
hist, edges = np.histogram(returns, bins=20)
87+
hist_source = ColumnDataSource(data={"top": hist, "left": edges[:-1], "right": edges[1:]})
88+
p3 = figure(
89+
width=1500,
90+
height=1100,
91+
title="Returns Distribution",
92+
x_axis_label="Daily Return (%)",
93+
y_axis_label="Frequency",
94+
toolbar_location=None,
95+
)
96+
p3.quad(
97+
top="top",
98+
bottom=0,
99+
left="left",
100+
right="right",
101+
source=hist_source,
102+
fill_color=accent_green,
103+
line_color="white",
104+
line_width=2,
105+
alpha=0.85,
106+
)
107+
p3.title.text_font_size = "28pt"
108+
p3.xaxis.axis_label_text_font_size = "22pt"
109+
p3.yaxis.axis_label_text_font_size = "22pt"
110+
p3.xaxis.major_label_text_font_size = "18pt"
111+
p3.yaxis.major_label_text_font_size = "18pt"
112+
p3.grid.grid_line_alpha = 0.3
113+
p3.outline_line_color = "#cccccc"
114+
p3.outline_line_width = 2
115+
116+
# Plot 4: Categorical bar chart (bottom center)
117+
p4 = figure(
118+
width=1500,
119+
height=1100,
120+
x_range=categories,
121+
title="Sales by Product Category",
122+
x_axis_label="Product",
123+
y_axis_label="Units Sold",
124+
toolbar_location=None,
125+
)
126+
p4.vbar(x="x", top="y", source=bar_source, width=0.6, color=python_blue, alpha=0.85)
127+
p4.title.text_font_size = "28pt"
128+
p4.xaxis.axis_label_text_font_size = "22pt"
129+
p4.yaxis.axis_label_text_font_size = "22pt"
130+
p4.xaxis.major_label_text_font_size = "18pt"
131+
p4.yaxis.major_label_text_font_size = "18pt"
132+
p4.xaxis.major_label_orientation = 0.3
133+
p4.grid.grid_line_alpha = 0.3
134+
p4.outline_line_color = "#cccccc"
135+
p4.outline_line_width = 2
136+
137+
# Plot 5: Scatter plot with correlation (bottom right)
138+
p5 = figure(
139+
width=1600,
140+
height=1100,
141+
title="Variable Correlation Analysis",
142+
x_axis_label="X Variable",
143+
y_axis_label="Y Variable",
144+
toolbar_location=None,
145+
)
146+
p5.scatter("x", "y", source=scatter_source, size=18, color=accent_red, alpha=0.7)
147+
p5.title.text_font_size = "28pt"
148+
p5.xaxis.axis_label_text_font_size = "22pt"
149+
p5.yaxis.axis_label_text_font_size = "22pt"
150+
p5.xaxis.major_label_text_font_size = "18pt"
151+
p5.yaxis.major_label_text_font_size = "18pt"
152+
p5.grid.grid_line_alpha = 0.3
153+
p5.outline_line_color = "#cccccc"
154+
p5.outline_line_width = 2
155+
156+
# Custom grid layout demonstrating non-uniform cell sizes
157+
# Row 1: Main time series (3000px) + Volume sidebar (1600px) = 4600px
158+
# Row 2: Histogram (1500px) + Bar chart (1500px) + Scatter (1600px) = 4600px
159+
# This creates a dashboard with varied cell sizes (colspan/rowspan concept)
160+
161+
top_row = row(p1, p2)
162+
bottom_row = row(p3, p4, p5)
163+
final_layout = column(top_row, bottom_row)
164+
165+
# Save
166+
export_png(final_layout, filename="plot.png")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
library: bokeh
2+
specification_id: subplot-grid-custom
3+
created: '2025-12-30T23:54:36Z'
4+
updated: '2025-12-30T23:59:23Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20608471124
7+
issue: 2856
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/bokeh/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent dashboard-style layout demonstrating non-uniform subplot sizes with
17+
larger main chart and smaller detail panels
18+
- Consistent visual styling across all subplots with matching font sizes, grid alpha,
19+
and outline styling
20+
- Realistic financial dashboard scenario with coherent data story (price, volume,
21+
returns analysis)
22+
- Clean KISS code structure with proper use of ColumnDataSource for all data
23+
- Good color palette using Python blue/yellow theme with accent colors for variety
24+
weaknesses:
25+
- Bottom row panels have slight width inconsistency (1500+1500+1600 vs top row)
26+
creating minor visual imbalance
27+
- Does not demonstrate rowspan (tall sidebar plot) which is mentioned in the spec
28+
notes
29+
- Could leverage more Bokeh-specific interactive features like linked brushing or
30+
hover tooltips

0 commit comments

Comments
 (0)