Skip to content

Commit 8b38540

Browse files
feat(bokeh): implement facet-grid (#2764)
## Implementation: `facet-grid` - bokeh Implements the **bokeh** version of `facet-grid`. **File:** `plots/facet-grid/implementations/bokeh.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20601058721)* --------- 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 05c1da0 commit 8b38540

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
""" pyplots.ai
2+
facet-grid: Faceted Grid Plot
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 90/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from bokeh.io import export_png
10+
from bokeh.layouts import column, gridplot
11+
from bokeh.models import ColumnDataSource, Div
12+
from bokeh.plotting import figure
13+
14+
15+
# Data - Product performance across regions and seasons
16+
np.random.seed(42)
17+
18+
regions = ["North", "South", "East"]
19+
seasons = ["Spring", "Summer", "Fall", "Winter"]
20+
21+
data = []
22+
for region in regions:
23+
for season in seasons:
24+
n_points = 25
25+
# Base values vary by region
26+
base_x = {"North": 20, "South": 30, "East": 25}[region]
27+
base_y = {"North": 50, "South": 70, "East": 60}[region]
28+
29+
# Seasonal adjustments
30+
season_adj = {"Spring": 1.0, "Summer": 1.3, "Fall": 0.9, "Winter": 0.7}[season]
31+
32+
x = np.random.normal(base_x, 5, n_points)
33+
y = base_y * season_adj + x * 0.8 + np.random.normal(0, 8, n_points)
34+
35+
for xi, yi in zip(x, y, strict=True):
36+
data.append({"marketing_spend": xi, "sales": yi, "region": region, "season": season})
37+
38+
df = pd.DataFrame(data)
39+
40+
# Colors for each region - darker orange instead of yellow for better visibility
41+
colors = {"North": "#306998", "South": "#E69F00", "East": "#4ECDC4"}
42+
43+
# Create grid of plots (rows=seasons, cols=regions)
44+
# Target: 4800x2700 total. With 3 cols, 4 rows + title/legend:
45+
# - Subplot width: 4800 / 3 = 1600
46+
# - Subplot height: (2700 - 120 title - 100 legend) / 4 = 620
47+
plots = []
48+
49+
for season in seasons:
50+
row_plots = []
51+
for region in regions:
52+
subset = df[(df["region"] == region) & (df["season"] == season)]
53+
source = ColumnDataSource(data={"x": subset["marketing_spend"], "y": subset["sales"]})
54+
55+
# Create figure for each cell
56+
p = figure(width=1600, height=620, x_range=(5, 50), y_range=(20, 130), tools="")
57+
58+
# Add scatter points
59+
p.scatter(
60+
x="x", y="y", source=source, size=18, color=colors[region], alpha=0.7, line_color="#333333", line_width=1
61+
)
62+
63+
# Add facet label in top-left corner
64+
p.text(
65+
x=[8],
66+
y=[122],
67+
text=[f"{region} · {season}"],
68+
text_font_size="18pt",
69+
text_color="#333333",
70+
text_font_style="bold",
71+
)
72+
73+
# Style axes
74+
p.xaxis.axis_label = "Marketing Spend ($K)" if season == seasons[-1] else ""
75+
p.yaxis.axis_label = "Sales ($K)" if region == regions[0] else ""
76+
p.xaxis.axis_label_text_font_size = "20pt"
77+
p.yaxis.axis_label_text_font_size = "20pt"
78+
p.xaxis.major_label_text_font_size = "16pt"
79+
p.yaxis.major_label_text_font_size = "16pt"
80+
81+
# Grid styling
82+
p.xgrid.grid_line_alpha = 0.3
83+
p.ygrid.grid_line_alpha = 0.3
84+
p.xgrid.grid_line_dash = "dashed"
85+
p.ygrid.grid_line_dash = "dashed"
86+
87+
# Background
88+
p.background_fill_color = "#fafafa"
89+
90+
row_plots.append(p)
91+
plots.append(row_plots)
92+
93+
# Create gridplot layout
94+
grid = gridplot(plots, toolbar_location=None, merge_tools=False)
95+
96+
# Add overall title using Div for cleaner implementation
97+
title_div = Div(
98+
text="<h1 style='text-align: center; color: #333333; font-size: 32pt; margin: 20px 0;'>"
99+
"facet-grid · bokeh · pyplots.ai</h1>",
100+
width=4800,
101+
height=120,
102+
)
103+
104+
# Create legend using Div for region-color mapping
105+
legend_html = (
106+
"<div style='text-align: center; font-size: 18pt; padding: 20px 0;'>"
107+
"<span style='font-weight: bold; margin-right: 30px;'>Region:</span>"
108+
)
109+
for region, color in colors.items():
110+
legend_html += (
111+
f"<span style='margin-right: 40px;'>"
112+
f"<span style='display: inline-block; width: 20px; height: 20px; "
113+
f"background-color: {color}; border: 1px solid #333; border-radius: 50%; "
114+
f"vertical-align: middle; margin-right: 8px;'></span>"
115+
f"{region}</span>"
116+
)
117+
legend_html += "</div>"
118+
119+
legend_div = Div(text=legend_html, width=4800, height=80)
120+
121+
# Combine title, grid, and legend using column layout
122+
final_layout = column(title_div, grid, legend_div)
123+
124+
# Save
125+
export_png(final_layout, filename="plot.png")
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: bokeh
2+
specification_id: facet-grid
3+
created: '2025-12-30T16:31:18Z'
4+
updated: '2025-12-30T16:49:58Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20601058721
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/facet-grid/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/facet-grid/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/facet-grid/bokeh/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Well-organized 4×3 grid layout that clearly shows patterns across regions and
17+
seasons
18+
- Excellent use of colorblind-safe color palette (blue/orange/teal)
19+
- Good text sizing throughout - all labels are clearly readable
20+
- Realistic business scenario with meaningful variation in the data
21+
- Clean, reproducible code structure following KISS principles
22+
weaknesses:
23+
- HTML-based legend (Div element) does not render in PNG export - only small colored
24+
artifacts appear in subplot corners
25+
- No use of Bokeh distinctive interactive features (hover tooltips, linked brushing,
26+
pan/zoom tools)

0 commit comments

Comments
 (0)