Skip to content

Commit 2e5ceef

Browse files
feat(bokeh): implement scatter-categorical (#2691)
## Implementation: `scatter-categorical` - bokeh Implements the **bokeh** version of `scatter-categorical`. **File:** `plots/scatter-categorical/implementations/bokeh.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20594556498)* --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> 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 8db136d commit 2e5ceef

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
""" pyplots.ai
2+
scatter-categorical: Categorical Scatter Plot
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png, output_file, save
9+
from bokeh.models import ColumnDataSource, Legend
10+
from bokeh.plotting import figure
11+
12+
13+
# Data
14+
np.random.seed(42)
15+
16+
# Generate three distinct groups with different patterns
17+
n_per_group = 50
18+
19+
# Group A: Lower left cluster
20+
x_a = np.random.normal(25, 5, n_per_group)
21+
y_a = np.random.normal(30, 6, n_per_group)
22+
cat_a = ["Product A"] * n_per_group
23+
24+
# Group B: Upper middle cluster
25+
x_b = np.random.normal(50, 7, n_per_group)
26+
y_b = np.random.normal(70, 8, n_per_group)
27+
cat_b = ["Product B"] * n_per_group
28+
29+
# Group C: Right side, moderate y
30+
x_c = np.random.normal(75, 6, n_per_group)
31+
y_c = np.random.normal(50, 7, n_per_group)
32+
cat_c = ["Product C"] * n_per_group
33+
34+
# Combine data
35+
x = np.concatenate([x_a, x_b, x_c])
36+
y = np.concatenate([y_a, y_b, y_c])
37+
categories = cat_a + cat_b + cat_c
38+
39+
# Define colors for each category (Python Blue, Python Yellow, colorblind-safe red)
40+
color_map = {"Product A": "#306998", "Product B": "#FFD43B", "Product C": "#E74C3C"}
41+
42+
# Create figure with interactive tools
43+
p = figure(
44+
width=4800,
45+
height=2700,
46+
title="scatter-categorical · bokeh · pyplots.ai",
47+
x_axis_label="Marketing Spend ($K)",
48+
y_axis_label="Customer Engagement Score",
49+
tools="pan,wheel_zoom,box_zoom,reset,hover,save",
50+
tooltips=[("Category", "@cat"), ("X", "@x{0.1}"), ("Y", "@y{0.1}")],
51+
)
52+
53+
# Plot each category separately for legend
54+
legend_items = []
55+
56+
for cat, color in color_map.items():
57+
mask = [categories[i] == cat for i in range(len(categories))]
58+
cat_x = [x[i] for i in range(len(x)) if mask[i]]
59+
cat_y = [y[i] for i in range(len(y)) if mask[i]]
60+
cat_source = ColumnDataSource(data={"x": cat_x, "y": cat_y, "cat": [cat] * len(cat_x)})
61+
r = p.scatter(x="x", y="y", source=cat_source, size=25, color=color, alpha=0.7, line_color="white", line_width=1)
62+
legend_items.append((cat, [r]))
63+
64+
# Add legend
65+
legend = Legend(items=legend_items, location="top_right")
66+
legend.label_text_font_size = "28pt"
67+
legend.glyph_height = 40
68+
legend.glyph_width = 40
69+
legend.spacing = 12
70+
legend.padding = 15
71+
legend.background_fill_alpha = 0.8
72+
p.add_layout(legend)
73+
74+
# Title styling
75+
p.title.text_font_size = "48pt"
76+
p.title.text_font_style = "bold"
77+
78+
# Axis label styling
79+
p.xaxis.axis_label_text_font_size = "36pt"
80+
p.yaxis.axis_label_text_font_size = "36pt"
81+
82+
# Tick label styling
83+
p.xaxis.major_label_text_font_size = "28pt"
84+
p.yaxis.major_label_text_font_size = "28pt"
85+
86+
# Grid styling
87+
p.grid.grid_line_alpha = 0.3
88+
p.grid.grid_line_dash = "dashed"
89+
90+
# Background
91+
p.background_fill_color = "#fafafa"
92+
93+
# Axis styling
94+
p.axis.axis_line_width = 2
95+
p.axis.major_tick_line_width = 2
96+
97+
# Save PNG
98+
export_png(p, filename="plot.png")
99+
100+
# Save interactive HTML
101+
output_file("plot.html", title="scatter-categorical · bokeh · pyplots.ai")
102+
save(p)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
library: bokeh
2+
specification_id: scatter-categorical
3+
created: '2025-12-30T11:40:30Z'
4+
updated: '2025-12-30T11:42:42Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20594556498
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/scatter-categorical/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-categorical/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-categorical/bokeh/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent text sizing for the large canvas (48pt title, 36pt axis labels, 28pt
17+
tick labels)
18+
- Clean three-cluster visualization with distinct, colorblind-safe colors
19+
- Good use of Bokeh interactive tools (hover tooltips with formatted values)
20+
- Proper use of ColumnDataSource for data management
21+
- Realistic business context (marketing spend vs engagement)
22+
- Saves both PNG and interactive HTML output
23+
weaknesses:
24+
- Legend placement could be better integrated with the plot (currently appears slightly
25+
isolated in top-right)
26+
- Could demonstrate alpha transparency more effectively by having some overlapping
27+
points between categories
28+
- Library features usage is good but not exceptional (could use linked selections
29+
or custom hover styling)

0 commit comments

Comments
 (0)