Skip to content

Commit fe3046b

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

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
""" pyplots.ai
2+
volcano-basic: Volcano Plot for Statistical Significance
3+
Library: letsplot 4.8.2 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import *
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Data - Simulated differential expression results
15+
np.random.seed(42)
16+
n_genes = 500
17+
18+
# Generate log2 fold changes (mostly near zero with some extremes)
19+
log2_fc = np.concatenate(
20+
[
21+
np.random.normal(0, 0.4, n_genes - 100), # Unchanged genes
22+
np.random.normal(2.2, 0.6, 50), # Up-regulated
23+
np.random.normal(-2.2, 0.6, 50), # Down-regulated
24+
]
25+
)
26+
27+
# Generate p-values (strongly correlated with fold change magnitude)
28+
# Higher fold change = lower p-value (more significant)
29+
neg_log10_pval = np.zeros(n_genes)
30+
for i, fc in enumerate(log2_fc):
31+
if abs(fc) > 1.5: # Large fold changes get significant p-values
32+
neg_log10_pval[i] = np.random.uniform(1.5, 3.5)
33+
elif abs(fc) > 1.0: # Moderate fold changes get borderline p-values
34+
neg_log10_pval[i] = np.random.uniform(0.8, 2.0)
35+
else: # Small fold changes get non-significant p-values
36+
neg_log10_pval[i] = np.random.uniform(0.1, 1.5)
37+
38+
# Determine significance status
39+
p_threshold = 1.3 # -log10(0.05)
40+
fc_threshold = 1.0 # log2(2) = 1
41+
42+
significance = []
43+
for fc, nlp in zip(log2_fc, neg_log10_pval):
44+
if nlp > p_threshold and fc > fc_threshold:
45+
significance.append("Up-regulated")
46+
elif nlp > p_threshold and fc < -fc_threshold:
47+
significance.append("Down-regulated")
48+
else:
49+
significance.append("Not significant")
50+
51+
# Create DataFrame
52+
df = pd.DataFrame({"log2_fold_change": log2_fc, "neg_log10_pvalue": neg_log10_pval, "significance": significance})
53+
54+
# Create volcano plot
55+
plot = (
56+
ggplot(df, aes(x="log2_fold_change", y="neg_log10_pvalue", color="significance"))
57+
+ geom_point(aes(color="significance"), size=4, alpha=0.7)
58+
+ geom_hline(yintercept=p_threshold, linetype="dashed", color="#666666", size=1)
59+
+ geom_vline(xintercept=-fc_threshold, linetype="dashed", color="#666666", size=1)
60+
+ geom_vline(xintercept=fc_threshold, linetype="dashed", color="#666666", size=1)
61+
+ scale_color_manual(
62+
values=["#306998", "#888888", "#DC2626"], breaks=["Down-regulated", "Not significant", "Up-regulated"]
63+
)
64+
+ labs(x="Log2 Fold Change", y="-Log10(p-value)", title="volcano-basic · letsplot · pyplots.ai", color="Status")
65+
+ theme_minimal()
66+
+ theme(
67+
plot_title=element_text(size=24, face="bold"),
68+
axis_title=element_text(size=20),
69+
axis_text=element_text(size=16),
70+
legend_title=element_text(size=18),
71+
legend_text=element_text(size=16),
72+
legend_position="right",
73+
)
74+
+ ggsize(1600, 900)
75+
)
76+
77+
# Save as PNG (scale=3 gives 4800x2700)
78+
ggsave(plot, "plot.png", path=".", scale=3)
79+
80+
# Save as HTML for interactivity
81+
ggsave(plot, "plot.html", path=".")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
library: letsplot
2+
specification_id: volcano-basic
3+
created: '2025-12-31T05:34:59Z'
4+
updated: '2025-12-31T05:46:59Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612785486
7+
issue: 2924
8+
python_version: 3.13.11
9+
library_version: 4.8.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/letsplot/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/letsplot/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/letsplot/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Clear visual distinction between significance categories using blue/gray/red color
17+
coding
18+
- Proper threshold lines at -log10(0.05) and ±1 fold change as specified
19+
- Uses lets-plot grammar of graphics effectively with ggplot + geom_point + scale_color_manual
20+
- Good data generation with realistic volcano plot distribution - appropriate mix
21+
of significant and non-significant genes
22+
- Correct title format following spec-id · library · pyplots.ai convention
23+
weaknesses:
24+
- Legend title says Status instead of Significance for better clarity

0 commit comments

Comments
 (0)