Skip to content

Commit 722d472

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

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
""" pyplots.ai
2+
volcano-basic: Volcano Plot for Statistical Significance
3+
Library: seaborn 0.13.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
import pandas as pd
10+
import seaborn as sns
11+
12+
13+
# Data: Simulated differential expression results
14+
np.random.seed(42)
15+
n_genes = 500
16+
17+
# Generate log2 fold changes (centered around 0, some extreme values)
18+
log2_fold_change = np.concatenate(
19+
[
20+
np.random.normal(0, 0.5, n_genes - 60), # Most genes: no change
21+
np.random.normal(-2.5, 0.5, 30), # Down-regulated
22+
np.random.normal(2.5, 0.5, 30), # Up-regulated
23+
]
24+
)
25+
26+
# Generate p-values (correlated with fold change magnitude for realism)
27+
base_pvalues = 10 ** (-np.abs(log2_fold_change) * np.random.uniform(0.5, 2, n_genes))
28+
base_pvalues = np.clip(base_pvalues, 1e-10, 1.0)
29+
neg_log10_pvalue = -np.log10(base_pvalues)
30+
31+
# Define significance thresholds
32+
pval_threshold = 1.3 # -log10(0.05)
33+
fc_threshold = 1.0 # log2(2) = 1
34+
35+
# Categorize genes using numpy vectorized conditions
36+
categories = np.where(
37+
neg_log10_pvalue < pval_threshold,
38+
"Not Significant",
39+
np.where(
40+
log2_fold_change > fc_threshold,
41+
"Up-regulated",
42+
np.where(log2_fold_change < -fc_threshold, "Down-regulated", "Not Significant"),
43+
),
44+
)
45+
46+
# Create DataFrame
47+
df = pd.DataFrame({"log2_fold_change": log2_fold_change, "neg_log10_pvalue": neg_log10_pvalue, "category": categories})
48+
49+
# Sort so significant points are plotted on top
50+
category_order = {"Not Significant": 0, "Down-regulated": 1, "Up-regulated": 2}
51+
df["order"] = df["category"].map(category_order)
52+
df = df.sort_values("order")
53+
54+
# Color palette matching specification
55+
palette = {
56+
"Not Significant": "#888888",
57+
"Down-regulated": "#306998", # Python Blue
58+
"Up-regulated": "#E63946", # Red for up-regulated
59+
}
60+
61+
# Create figure
62+
fig, ax = plt.subplots(figsize=(16, 9))
63+
64+
# Scatter plot using seaborn
65+
sns.scatterplot(
66+
data=df,
67+
x="log2_fold_change",
68+
y="neg_log10_pvalue",
69+
hue="category",
70+
hue_order=["Not Significant", "Down-regulated", "Up-regulated"],
71+
palette=palette,
72+
s=100,
73+
alpha=0.7,
74+
edgecolor="none",
75+
ax=ax,
76+
legend=True,
77+
)
78+
79+
# Threshold lines
80+
ax.axhline(y=pval_threshold, color="#333333", linestyle="--", linewidth=2, alpha=0.7)
81+
ax.axvline(x=fc_threshold, color="#333333", linestyle="--", linewidth=2, alpha=0.7)
82+
ax.axvline(x=-fc_threshold, color="#333333", linestyle="--", linewidth=2, alpha=0.7)
83+
84+
# Add threshold annotations
85+
ax.text(ax.get_xlim()[1] - 0.3, pval_threshold + 0.3, "p = 0.05", fontsize=14, ha="right", color="#333333")
86+
ax.text(fc_threshold + 0.1, ax.get_ylim()[1] - 0.5, "FC = 2", fontsize=14, ha="left", color="#333333")
87+
ax.text(-fc_threshold - 0.1, ax.get_ylim()[1] - 0.5, "FC = 0.5", fontsize=14, ha="right", color="#333333")
88+
89+
# Labels and styling
90+
ax.set_xlabel("Log2 Fold Change", fontsize=20)
91+
ax.set_ylabel("-Log10(p-value)", fontsize=20)
92+
ax.set_title("volcano-basic · seaborn · pyplots.ai", fontsize=24)
93+
ax.tick_params(axis="both", labelsize=16)
94+
ax.grid(True, alpha=0.3, linestyle="--")
95+
96+
# Legend styling
97+
ax.legend(title="Significance", fontsize=14, title_fontsize=16, loc="upper right", framealpha=0.9)
98+
99+
plt.tight_layout()
100+
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
library: seaborn
2+
specification_id: volcano-basic
3+
created: '2025-12-31T05:31:24Z'
4+
updated: '2025-12-31T05:37:02Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612779597
7+
issue: 2924
8+
python_version: 3.13.11
9+
library_version: 0.13.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/seaborn/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/seaborn/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of the classic volcano plot pattern with clear visual
17+
separation of significance categories
18+
- Proper threshold annotations (p=0.05, FC=2, FC=0.5) provide context for the significance
19+
cutoffs
20+
- Clean data generation with realistic bimodal distribution for differentially expressed
21+
genes
22+
- Good use of seaborn hue parameter with custom palette matching the specification
23+
colors
24+
- Sorting mechanism ensures significant points are plotted on top and remain visible
25+
weaknesses:
26+
- Color palette could be more colorblind-friendly (consider using blue-orange instead
27+
of blue-red)
28+
- Missing optional gene labels for top significant features mentioned in the spec
29+
notes

0 commit comments

Comments
 (0)