Skip to content

Commit d86ddd9

Browse files
feat(bokeh): implement coefficient-confidence (#3588)
## Implementation: `coefficient-confidence` - bokeh Implements the **bokeh** version of `coefficient-confidence`. **File:** `plots/coefficient-confidence/implementations/bokeh.py` **Parent Issue:** #3576 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20868891789)* --------- 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 7db4cb4 commit d86ddd9

File tree

2 files changed

+386
-0
lines changed

2 files changed

+386
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
""" pyplots.ai
2+
coefficient-confidence: Coefficient Plot with Confidence Intervals
3+
Library: bokeh 3.8.2 | Python 3.13.11
4+
Quality: 90/100 | Created: 2026-01-09
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png, output_file, save
9+
from bokeh.models import ColumnDataSource, HoverTool, Legend, LegendItem, Span
10+
from bokeh.plotting import figure
11+
12+
13+
# Data - Regression coefficients for housing price prediction model
14+
np.random.seed(42)
15+
16+
variables = [
17+
"Square Footage",
18+
"Number of Bedrooms",
19+
"Number of Bathrooms",
20+
"Age of House",
21+
"Distance to City Center",
22+
"Lot Size",
23+
"Garage Size",
24+
"School Rating",
25+
"Crime Rate Index",
26+
"Property Tax Rate",
27+
]
28+
29+
# Generate realistic regression coefficients (some significant, some not)
30+
coefficients = np.array([0.45, 0.12, 0.18, -0.08, -0.22, 0.15, 0.09, 0.28, -0.35, -0.05])
31+
std_errors = np.array([0.05, 0.08, 0.06, 0.03, 0.07, 0.04, 0.06, 0.05, 0.09, 0.07])
32+
33+
# Calculate 95% confidence intervals
34+
ci_lower = coefficients - 1.96 * std_errors
35+
ci_upper = coefficients + 1.96 * std_errors
36+
37+
# Determine significance (CI does not cross zero)
38+
significant = ~((ci_lower < 0) & (ci_upper > 0))
39+
40+
# Sort by coefficient magnitude for better visualization
41+
sort_idx = np.argsort(np.abs(coefficients))
42+
variables = [variables[i] for i in sort_idx]
43+
coefficients = coefficients[sort_idx]
44+
ci_lower = ci_lower[sort_idx]
45+
ci_upper = ci_upper[sort_idx]
46+
significant = significant[sort_idx]
47+
48+
# Strong color contrast for significant vs non-significant distinction
49+
# Using vivid blue (#2171b5) for significant vs muted gray (#969696) for non-significant
50+
SIG_COLOR = "#2171b5"
51+
NONSIG_COLOR = "#969696"
52+
53+
colors = [SIG_COLOR if sig else NONSIG_COLOR for sig in significant]
54+
55+
# Create figure with categorical y-axis
56+
p = figure(
57+
width=4800,
58+
height=2700,
59+
y_range=variables,
60+
title="coefficient-confidence · bokeh · pyplots.ai",
61+
x_axis_label="Coefficient Estimate (Standardized)",
62+
y_axis_label="Predictor Variable",
63+
)
64+
65+
# Add vertical reference line at zero
66+
zero_line = Span(location=0, dimension="height", line_color="#333333", line_width=3, line_dash="dashed")
67+
p.add_layout(zero_line)
68+
69+
# Draw confidence interval segments (error bars) with distinct colors
70+
for i, var in enumerate(variables):
71+
color = colors[i]
72+
# Main confidence interval line
73+
p.line(x=[ci_lower[i], ci_upper[i]], y=[var, var], line_width=6, line_color=color, line_alpha=0.85)
74+
75+
# Plot coefficient points - separate renderers for legend with distinct colors
76+
sig_indices = [i for i, s in enumerate(significant) if s]
77+
nonsig_indices = [i for i, s in enumerate(significant) if not s]
78+
79+
# Create separate data sources for legend
80+
sig_source = ColumnDataSource(
81+
data={
82+
"variables": [variables[i] for i in sig_indices],
83+
"coefficients": [coefficients[i] for i in sig_indices],
84+
"ci_lower_fmt": [f"{ci_lower[i]:.3f}" for i in sig_indices],
85+
"ci_upper_fmt": [f"{ci_upper[i]:.3f}" for i in sig_indices],
86+
"coef_fmt": [f"{coefficients[i]:.3f}" for i in sig_indices],
87+
"significance": ["Significant (p < 0.05)"] * len(sig_indices),
88+
}
89+
)
90+
91+
nonsig_source = ColumnDataSource(
92+
data={
93+
"variables": [variables[i] for i in nonsig_indices],
94+
"coefficients": [coefficients[i] for i in nonsig_indices],
95+
"ci_lower_fmt": [f"{ci_lower[i]:.3f}" for i in nonsig_indices],
96+
"ci_upper_fmt": [f"{ci_upper[i]:.3f}" for i in nonsig_indices],
97+
"coef_fmt": [f"{coefficients[i]:.3f}" for i in nonsig_indices],
98+
"significance": ["Not Significant"] * len(nonsig_indices),
99+
}
100+
)
101+
102+
# Render significant points with vivid blue
103+
sig_renderer = p.scatter(
104+
x="coefficients", y="variables", source=sig_source, size=30, color=SIG_COLOR, line_color="white", line_width=3
105+
)
106+
107+
# Render non-significant points with muted gray
108+
nonsig_renderer = p.scatter(
109+
x="coefficients", y="variables", source=nonsig_source, size=30, color=NONSIG_COLOR, line_color="white", line_width=3
110+
)
111+
112+
# Add HoverTool for interactive tooltips (Bokeh distinctive feature)
113+
hover = HoverTool(
114+
tooltips=[
115+
("Variable", "@variables"),
116+
("Coefficient", "@coef_fmt"),
117+
("95% CI", "[@ci_lower_fmt, @ci_upper_fmt]"),
118+
("Status", "@significance"),
119+
],
120+
renderers=[sig_renderer, nonsig_renderer],
121+
)
122+
p.add_tools(hover)
123+
124+
# Create legend inside the plot area (top right corner within plot bounds)
125+
legend = Legend(
126+
items=[
127+
LegendItem(label="Significant (p < 0.05)", renderers=[sig_renderer]),
128+
LegendItem(label="Not Significant", renderers=[nonsig_renderer]),
129+
],
130+
location="top_right",
131+
label_text_font_size="24pt",
132+
glyph_width=40,
133+
glyph_height=40,
134+
border_line_color="#666666",
135+
border_line_width=2,
136+
background_fill_color="white",
137+
background_fill_alpha=0.95,
138+
padding=20,
139+
margin=30,
140+
)
141+
p.add_layout(legend)
142+
143+
# Style text sizes for large canvas (increased for better readability)
144+
p.title.text_font_size = "36pt"
145+
p.xaxis.axis_label_text_font_size = "28pt"
146+
p.yaxis.axis_label_text_font_size = "28pt"
147+
p.xaxis.major_label_text_font_size = "22pt"
148+
p.yaxis.major_label_text_font_size = "22pt"
149+
150+
# Grid styling
151+
p.xgrid.grid_line_alpha = 0.3
152+
p.xgrid.grid_line_dash = "dashed"
153+
p.ygrid.grid_line_alpha = 0.3
154+
p.ygrid.grid_line_dash = "dashed"
155+
156+
# Background styling
157+
p.background_fill_color = "#fafafa"
158+
p.border_fill_color = "white"
159+
160+
# Increase axis line width for visibility
161+
p.xaxis.axis_line_width = 2
162+
p.yaxis.axis_line_width = 2
163+
p.xaxis.major_tick_line_width = 2
164+
p.yaxis.major_tick_line_width = 2
165+
166+
# Save plot (PNG and HTML for interactive)
167+
export_png(p, filename="plot.png")
168+
output_file("plot.html", title="Coefficient Plot with Confidence Intervals")
169+
save(p)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
library: bokeh
2+
specification_id: coefficient-confidence
3+
created: '2026-01-09T23:48:14Z'
4+
updated: '2026-01-10T00:07:07Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20868891789
7+
issue: 3576
8+
python_version: 3.13.11
9+
library_version: 3.8.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/coefficient-confidence/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/coefficient-confidence/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/coefficient-confidence/bokeh/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Strong color contrast between significant (blue) and non-significant (gray) coefficients
17+
- 'Excellent use of Bokeh-specific features: HoverTool with detailed tooltips showing
18+
variable, coefficient, CI, and status'
19+
- Clean horizontal layout matching spec preference for long variable names
20+
- Professional styling with appropriate font sizes for large canvas
21+
- Zero reference line clearly visible as dashed line
22+
- Both PNG and HTML outputs generated for full interactivity
23+
weaknesses:
24+
- Legend placement in top-right slightly overlaps with the data region where Square
25+
Footage coefficient extends
26+
- Grid styling could be slightly more subtle (currently at alpha=0.3, could be 0.2)
27+
image_description: The plot displays a horizontal coefficient plot with 10 predictor
28+
variables for a housing price prediction model. Variables are listed on the y-axis
29+
(Property Tax Rate, Age of House, Garage Size, Number of Bedrooms, Lot Size, Number
30+
of Bathrooms, Distance to City Center, School Rating, Crime Rate Index, Square
31+
Footage) ordered by coefficient magnitude. The x-axis shows "Coefficient Estimate
32+
(Standardized)" ranging from approximately -0.4 to 0.6. A vertical dashed dark
33+
gray reference line at x=0 indicates the null hypothesis threshold. Significant
34+
coefficients (p < 0.05) are displayed in vivid blue (#2171b5) while non-significant
35+
coefficients appear in muted gray (#969696). Each coefficient is represented by
36+
a point marker with horizontal confidence interval bars. The legend in the top-right
37+
corner clearly distinguishes "Significant (p < 0.05)" and "Not Significant" categories.
38+
The background is light gray (#fafafa) with subtle dashed grid lines. The title
39+
"coefficient-confidence · bokeh · pyplots.ai" appears at the top left.
40+
criteria_checklist:
41+
visual_quality:
42+
score: 37
43+
max: 40
44+
items:
45+
- id: VQ-01
46+
name: Text Legibility
47+
score: 10
48+
max: 10
49+
passed: true
50+
comment: Title at 36pt, axis labels at 28pt, tick labels at 22pt - all clearly
51+
readable
52+
- id: VQ-02
53+
name: No Overlap
54+
score: 8
55+
max: 8
56+
passed: true
57+
comment: No overlapping text or elements, variable names fully visible
58+
- id: VQ-03
59+
name: Element Visibility
60+
score: 7
61+
max: 8
62+
passed: true
63+
comment: Markers size=30 and line_width=6 are appropriate, confidence intervals
64+
clearly visible
65+
- id: VQ-04
66+
name: Color Accessibility
67+
score: 5
68+
max: 5
69+
passed: true
70+
comment: Blue vs gray is colorblind-safe, strong contrast between significant/non-significant
71+
- id: VQ-05
72+
name: Layout Balance
73+
score: 5
74+
max: 5
75+
passed: true
76+
comment: Plot fills canvas well, good margins, legend positioned appropriately
77+
inside plot
78+
- id: VQ-06
79+
name: Axis Labels
80+
score: 2
81+
max: 2
82+
passed: true
83+
comment: X-axis has Coefficient Estimate (Standardized), Y-axis has Predictor
84+
Variable
85+
- id: VQ-07
86+
name: Grid & Legend
87+
score: 0
88+
max: 2
89+
passed: false
90+
comment: Legend placement in top-right partially overlaps with data region
91+
for Square Footage
92+
spec_compliance:
93+
score: 25
94+
max: 25
95+
items:
96+
- id: SC-01
97+
name: Plot Type
98+
score: 8
99+
max: 8
100+
passed: true
101+
comment: Correct coefficient plot with horizontal error bars
102+
- id: SC-02
103+
name: Data Mapping
104+
score: 5
105+
max: 5
106+
passed: true
107+
comment: Variables on Y-axis, coefficients on X-axis per spec preference
108+
- id: SC-03
109+
name: Required Features
110+
score: 5
111+
max: 5
112+
passed: true
113+
comment: Zero reference line, color distinction for significance, confidence
114+
intervals all present
115+
- id: SC-04
116+
name: Data Range
117+
score: 3
118+
max: 3
119+
passed: true
120+
comment: All data visible within axis range
121+
- id: SC-05
122+
name: Legend Accuracy
123+
score: 2
124+
max: 2
125+
passed: true
126+
comment: Legend correctly labels significant vs non-significant
127+
- id: SC-06
128+
name: Title Format
129+
score: 2
130+
max: 2
131+
passed: true
132+
comment: 'Uses correct format: coefficient-confidence · bokeh · pyplots.ai'
133+
data_quality:
134+
score: 18
135+
max: 20
136+
items:
137+
- id: DQ-01
138+
name: Feature Coverage
139+
score: 7
140+
max: 8
141+
passed: true
142+
comment: Shows mix of positive/negative coefficients, significant/non-significant,
143+
varying CI widths
144+
- id: DQ-02
145+
name: Realistic Context
146+
score: 7
147+
max: 7
148+
passed: true
149+
comment: Housing price prediction is a real, neutral scenario with sensible
150+
predictors
151+
- id: DQ-03
152+
name: Appropriate Scale
153+
score: 4
154+
max: 5
155+
passed: true
156+
comment: Standardized coefficients in reasonable range (-0.35 to 0.45)
157+
code_quality:
158+
score: 10
159+
max: 10
160+
items:
161+
- id: CQ-01
162+
name: KISS Structure
163+
score: 3
164+
max: 3
165+
passed: true
166+
comment: 'Linear script: imports, data, plot, save - no functions/classes'
167+
- id: CQ-02
168+
name: Reproducibility
169+
score: 3
170+
max: 3
171+
passed: true
172+
comment: np.random.seed(42) is set
173+
- id: CQ-03
174+
name: Clean Imports
175+
score: 2
176+
max: 2
177+
passed: true
178+
comment: All imports are used (numpy, bokeh components)
179+
- id: CQ-04
180+
name: No Deprecated API
181+
score: 1
182+
max: 1
183+
passed: true
184+
comment: Uses current Bokeh 3.x API
185+
- id: CQ-05
186+
name: Output Correct
187+
score: 1
188+
max: 1
189+
passed: true
190+
comment: Saves as plot.png and plot.html
191+
library_features:
192+
score: 5
193+
max: 5
194+
items:
195+
- id: LF-01
196+
name: Distinctive Features
197+
score: 5
198+
max: 5
199+
passed: true
200+
comment: 'Excellent use of Bokeh features: ColumnDataSource, HoverTool with
201+
tooltips, Legend with custom items, Span for reference line, HTML export'
202+
verdict: APPROVED
203+
impl_tags:
204+
dependencies: []
205+
techniques:
206+
- hover-tooltips
207+
- custom-legend
208+
- html-export
209+
patterns:
210+
- data-generation
211+
- columndatasource
212+
- iteration-over-groups
213+
dataprep: []
214+
styling:
215+
- edge-highlighting
216+
- grid-styling
217+
- alpha-blending

0 commit comments

Comments
 (0)