Skip to content

Commit f8483fd

Browse files
feat(bokeh): implement volcano-basic (#2940)
## Implementation: `volcano-basic` - bokeh Implements the **bokeh** version of `volcano-basic`. **File:** `plots/volcano-basic/implementations/bokeh.py` **Parent Issue:** #2924 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20612781583)* --------- 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 3bef521 commit f8483fd

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
""" pyplots.ai
2+
volcano-basic: Volcano Plot for Statistical Significance
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png
9+
from bokeh.models import ColumnDataSource, Span
10+
from bokeh.plotting import figure
11+
12+
13+
# Data - Simulated differential expression results
14+
np.random.seed(42)
15+
n_genes = 2000
16+
17+
# Generate log2 fold changes (effect sizes)
18+
log2_fc = np.random.normal(0, 1.5, n_genes)
19+
20+
# Generate p-values: most genes non-significant, some highly significant
21+
# Use inverse relationship with fold change magnitude for realism
22+
base_pvals = np.random.uniform(0.001, 1, n_genes)
23+
# Genes with larger fold changes tend to have lower p-values
24+
fold_effect = np.abs(log2_fc) / np.max(np.abs(log2_fc))
25+
pvals = base_pvals * (1 - 0.7 * fold_effect) + 0.01 * np.random.random(n_genes)
26+
pvals = np.clip(pvals, 1e-50, 1)
27+
28+
neg_log10_pval = -np.log10(pvals)
29+
30+
# Significance thresholds
31+
pval_threshold = -np.log10(0.05) # ~1.3
32+
fc_threshold = 1.0 # log2(2) = 1
33+
34+
# Classify points by significance
35+
significant_up = (neg_log10_pval > pval_threshold) & (log2_fc > fc_threshold)
36+
significant_down = (neg_log10_pval > pval_threshold) & (log2_fc < -fc_threshold)
37+
38+
# Create separate data sources for each category (enables proper legend)
39+
source_up = ColumnDataSource(data={"x": log2_fc[significant_up], "y": neg_log10_pval[significant_up]})
40+
41+
source_down = ColumnDataSource(data={"x": log2_fc[significant_down], "y": neg_log10_pval[significant_down]})
42+
43+
source_ns = ColumnDataSource(
44+
data={"x": log2_fc[~(significant_up | significant_down)], "y": neg_log10_pval[~(significant_up | significant_down)]}
45+
)
46+
47+
# Create figure
48+
p = figure(
49+
width=4800,
50+
height=2700,
51+
title="volcano-basic · bokeh · pyplots.ai",
52+
x_axis_label="Log₂ Fold Change",
53+
y_axis_label="-Log₁₀ (P-value)",
54+
)
55+
56+
# Plot points by category (non-significant first, then significant on top)
57+
p.scatter(x="x", y="y", source=source_ns, color="#AAAAAA", size=18, alpha=0.5, legend_label="Not significant")
58+
59+
p.scatter(x="x", y="y", source=source_down, color="#306998", size=25, alpha=0.7, legend_label="Down-regulated")
60+
61+
p.scatter(x="x", y="y", source=source_up, color="#D62728", size=25, alpha=0.7, legend_label="Up-regulated")
62+
63+
# Add threshold lines
64+
# Horizontal line for p-value threshold
65+
hline = Span(
66+
location=pval_threshold, dimension="width", line_color="#333333", line_dash="dashed", line_width=3, line_alpha=0.7
67+
)
68+
p.add_layout(hline)
69+
70+
# Vertical lines for fold change thresholds
71+
vline_pos = Span(
72+
location=fc_threshold, dimension="height", line_color="#333333", line_dash="dashed", line_width=3, line_alpha=0.7
73+
)
74+
p.add_layout(vline_pos)
75+
76+
vline_neg = Span(
77+
location=-fc_threshold, dimension="height", line_color="#333333", line_dash="dashed", line_width=3, line_alpha=0.7
78+
)
79+
p.add_layout(vline_neg)
80+
81+
# Styling - scaled for 4800x2700 canvas
82+
p.title.text_font_size = "36pt"
83+
p.title.align = "center"
84+
p.xaxis.axis_label_text_font_size = "28pt"
85+
p.yaxis.axis_label_text_font_size = "28pt"
86+
p.xaxis.major_label_text_font_size = "22pt"
87+
p.yaxis.major_label_text_font_size = "22pt"
88+
p.xaxis.axis_line_width = 2
89+
p.yaxis.axis_line_width = 2
90+
p.xaxis.major_tick_line_width = 2
91+
p.yaxis.major_tick_line_width = 2
92+
93+
# Grid styling
94+
p.grid.grid_line_alpha = 0.3
95+
p.grid.grid_line_dash = "dotted"
96+
97+
# Background
98+
p.background_fill_color = "#FAFAFA"
99+
p.border_fill_color = "#FFFFFF"
100+
101+
# Legend styling
102+
p.legend.location = "top_right"
103+
p.legend.label_text_font_size = "22pt"
104+
p.legend.glyph_height = 40
105+
p.legend.glyph_width = 40
106+
p.legend.spacing = 15
107+
p.legend.padding = 20
108+
p.legend.background_fill_alpha = 0.8
109+
p.legend.border_line_width = 2
110+
p.legend.border_line_color = "#CCCCCC"
111+
112+
# Save
113+
export_png(p, filename="plot.png")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
library: bokeh
2+
specification_id: volcano-basic
3+
created: '2025-12-31T05:31:44Z'
4+
updated: '2025-12-31T05:38:22Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612781583
7+
issue: 2924
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/bokeh/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of volcano plot with all required threshold lines and
17+
color coding
18+
- Well-scaled for 4800x2700 canvas with appropriately sized text and markers
19+
- Clean separation of data into three ColumnDataSources enables proper legend handling
20+
- Realistic simulated gene expression data with correlation between fold change
21+
magnitude and significance
22+
- Proper use of alpha transparency for dense data visualization
23+
- Clean, readable code following KISS principles
24+
weaknesses:
25+
- Red/blue color scheme not optimal for colorblind accessibility - consider using
26+
colorblind-safe palette
27+
- 'Missing optional feature: labeling of top significant genes by name as suggested
28+
in spec notes'
29+
- No hover tooltips to identify individual points - a key Bokeh interactive feature

0 commit comments

Comments
 (0)