From 3bf86498fe47ebd145f1225413d73e8d83c9874d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:30:58 +0000 Subject: [PATCH 1/8] feat(pygal): implement volcano-basic --- plots/volcano-basic/implementations/pygal.py | 106 +++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 plots/volcano-basic/implementations/pygal.py diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py new file mode 100644 index 0000000000..b4ad82123d --- /dev/null +++ b/plots/volcano-basic/implementations/pygal.py @@ -0,0 +1,106 @@ +"""pyplots.ai +volcano-basic: Volcano Plot for Statistical Significance +Library: pygal | Python 3.13 +Quality: pending | Created: 2025-12-31 +""" + +import numpy as np +import pygal +from pygal.style import Style + + +# Data - Simulated differential gene expression results +np.random.seed(42) +n_genes = 500 + +# Generate fold changes (mostly near zero, some with larger effects) +log2_fc = np.concatenate( + [ + np.random.normal(0, 0.5, 400), # Non-significant genes + np.random.normal(2.5, 0.5, 50), # Up-regulated + np.random.normal(-2.5, 0.5, 50), # Down-regulated + ] +) + +# Generate p-values (correlated with effect size) +base_pval = np.random.uniform(0.001, 0.9, n_genes) +# Significant genes have lower p-values +base_pval[400:] = np.random.uniform(0.0001, 0.01, 100) +neg_log10_pval = -np.log10(base_pval) + +# Classification thresholds +fc_threshold = 1.0 # log2 fold change threshold (2-fold) +pval_threshold = 1.3 # -log10(0.05) ≈ 1.3 + +# Classify genes +up_regulated = (log2_fc > fc_threshold) & (neg_log10_pval > pval_threshold) +down_regulated = (log2_fc < -fc_threshold) & (neg_log10_pval > pval_threshold) +not_significant = ~(up_regulated | down_regulated) + +# Custom style for large canvas +custom_style = Style( + background="white", + plot_background="white", + foreground="#333333", + foreground_strong="#333333", + foreground_subtle="#666666", + colors=("#888888", "#c0392b", "#2980b9"), # Gray, Red, Blue + title_font_size=48, + label_font_size=32, + major_label_font_size=28, + legend_font_size=28, + value_font_size=20, + stroke_width=2, + opacity=0.7, + opacity_hover=1.0, + font_family="DejaVu Sans", +) + +# Create XY chart (scatter plot) +chart = pygal.XY( + width=4800, + height=2700, + style=custom_style, + title="volcano-basic · pygal · pyplots.ai", + x_title="Log₂ Fold Change", + y_title="-Log₁₀(p-value)", + show_legend=True, + legend_at_bottom=False, + legend_box_size=24, + dots_size=8, + stroke=False, + show_x_guides=True, + show_y_guides=True, + x_label_rotation=0, + range=(-5, 5), + y_labels_major_count=6, + x_labels_major_count=11, + truncate_legend=-1, +) + +# Prepare data points for each category +not_sig_points = [ + {"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if not_significant[i] +] +up_points = [{"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if up_regulated[i]] +down_points = [{"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if down_regulated[i]] + +# Add data series +chart.add("Not Significant", not_sig_points) +chart.add("Up-regulated", up_points) +chart.add("Down-regulated", down_points) + +# Add threshold lines as separate series +# Horizontal line at p-value threshold (y = 1.3) +h_line = [{"value": (-5, pval_threshold)}, {"value": (5, pval_threshold)}] +chart.add("p = 0.05", h_line, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) + +# Vertical lines at fold change thresholds (x = ±1) +v_line_pos = [{"value": (fc_threshold, 0)}, {"value": (fc_threshold, 8)}] +v_line_neg = [{"value": (-fc_threshold, 0)}, {"value": (-fc_threshold, 8)}] +chart.add("FC = 2", v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +chart.add("FC = 0.5", v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) + +# Save as PNG and HTML +chart.render_to_png("plot.png") +chart.render_to_file("plot.html") From 5a04b75ce6f25e4c11dbe3f35e035089f137d7e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:31:08 +0000 Subject: [PATCH 2/8] chore(pygal): add metadata for volcano-basic --- plots/volcano-basic/metadata/pygal.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plots/volcano-basic/metadata/pygal.yaml diff --git a/plots/volcano-basic/metadata/pygal.yaml b/plots/volcano-basic/metadata/pygal.yaml new file mode 100644 index 0000000000..21c942bf6d --- /dev/null +++ b/plots/volcano-basic/metadata/pygal.yaml @@ -0,0 +1,19 @@ +# Per-library metadata for pygal implementation of volcano-basic +# Auto-generated by impl-generate.yml + +library: pygal +specification_id: volcano-basic +created: '2025-12-31T05:31:07Z' +updated: '2025-12-31T05:31:07Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20612783052 +issue: 2924 +python_version: 3.13.11 +library_version: 3.1.0 +preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 9487c08001dbd2f4f06af77e052d57e39a78e38c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:36:37 +0000 Subject: [PATCH 3/8] chore(pygal): update quality score 78 and review feedback for volcano-basic --- plots/volcano-basic/implementations/pygal.py | 6 ++--- plots/volcano-basic/metadata/pygal.yaml | 24 ++++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index b4ad82123d..f429c2ed5e 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai volcano-basic: Volcano Plot for Statistical Significance -Library: pygal | Python 3.13 -Quality: pending | Created: 2025-12-31 +Library: pygal 3.1.0 | Python 3.13.11 +Quality: 78/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/volcano-basic/metadata/pygal.yaml b/plots/volcano-basic/metadata/pygal.yaml index 21c942bf6d..fcddb08b3f 100644 --- a/plots/volcano-basic/metadata/pygal.yaml +++ b/plots/volcano-basic/metadata/pygal.yaml @@ -1,10 +1,7 @@ -# Per-library metadata for pygal implementation of volcano-basic -# Auto-generated by impl-generate.yml - library: pygal specification_id: volcano-basic created: '2025-12-31T05:31:07Z' -updated: '2025-12-31T05:31:07Z' +updated: '2025-12-31T05:36:36Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20612783052 issue: 2924 @@ -13,7 +10,20 @@ library_version: 3.1.0 preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.html -quality_score: null +quality_score: 78 review: - strengths: [] - weaknesses: [] + strengths: + - Clean implementation following KISS principles with proper seed for reproducibility + - Correct volcano plot structure with appropriate data generation showing all three + gene categories + - Proper use of custom Style for font sizing and colors at 4800x2700 resolution + - Good use of subscript notation in axis labels (Log₂, Log₁₀) + - Threshold lines implemented as dashed strokes + weaknesses: + - Y-axis range extends to -5 but all p-value data is positive; should set y-axis + minimum to 0 or slightly below + - Threshold lines appear in the legend, cluttering it with 6 entries instead of + 3 data categories + - Legend text in top-left is relatively small compared to canvas size + - Threshold lines use colors from the series palette instead of a neutral gray/black + dashed style From 421948a8347930ff6ee38c53143f88962430afa9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:39:26 +0000 Subject: [PATCH 4/8] fix(pygal): address review feedback for volcano-basic - Set y-axis minimum to 0 (was -5 with all positive p-values) - Hide threshold lines from legend (use None as series name) - Increase legend font size from 28 to 36 for better readability - Use neutral gray color (#555555) for threshold lines - Extend vertical lines to y_max instead of fixed 8 Attempt 1/3 - fixes based on AI review --- plots/volcano-basic/implementations/pygal.py | 31 +++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index f429c2ed5e..ce66978d28 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,4 +1,4 @@ -""" pyplots.ai +"""pyplots.ai volcano-basic: Volcano Plot for Statistical Significance Library: pygal 3.1.0 | Python 3.13.11 Quality: 78/100 | Created: 2025-12-31 @@ -44,11 +44,18 @@ foreground="#333333", foreground_strong="#333333", foreground_subtle="#666666", - colors=("#888888", "#c0392b", "#2980b9"), # Gray, Red, Blue + colors=( + "#888888", + "#c0392b", + "#2980b9", + "#555555", + "#555555", + "#555555", + ), # Gray, Red, Blue, then neutral for lines title_font_size=48, label_font_size=32, major_label_font_size=28, - legend_font_size=28, + legend_font_size=36, value_font_size=20, stroke_width=2, opacity=0.7, @@ -56,6 +63,9 @@ font_family="DejaVu Sans", ) +# Calculate y-axis range (ensure minimum at 0) +y_max = max(neg_log10_pval) + 0.5 + # Create XY chart (scatter plot) chart = pygal.XY( width=4800, @@ -66,13 +76,14 @@ y_title="-Log₁₀(p-value)", show_legend=True, legend_at_bottom=False, - legend_box_size=24, + legend_box_size=28, dots_size=8, stroke=False, show_x_guides=True, show_y_guides=True, x_label_rotation=0, range=(-5, 5), + yrange=(0, y_max), y_labels_major_count=6, x_labels_major_count=11, truncate_legend=-1, @@ -90,16 +101,16 @@ chart.add("Up-regulated", up_points) chart.add("Down-regulated", down_points) -# Add threshold lines as separate series +# Add threshold lines as separate series (hidden from legend) # Horizontal line at p-value threshold (y = 1.3) h_line = [{"value": (-5, pval_threshold)}, {"value": (5, pval_threshold)}] -chart.add("p = 0.05", h_line, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +chart.add(None, h_line, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) # Vertical lines at fold change thresholds (x = ±1) -v_line_pos = [{"value": (fc_threshold, 0)}, {"value": (fc_threshold, 8)}] -v_line_neg = [{"value": (-fc_threshold, 0)}, {"value": (-fc_threshold, 8)}] -chart.add("FC = 2", v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) -chart.add("FC = 0.5", v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +v_line_pos = [{"value": (fc_threshold, 0)}, {"value": (fc_threshold, y_max)}] +v_line_neg = [{"value": (-fc_threshold, 0)}, {"value": (-fc_threshold, y_max)}] +chart.add(None, v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +chart.add(None, v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) # Save as PNG and HTML chart.render_to_png("plot.png") From a3d7dc85173ee90c328c8932816d4e94e3a92c4d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:54:02 +0000 Subject: [PATCH 5/8] chore(pygal): update quality score 82 and review feedback for volcano-basic --- plots/volcano-basic/implementations/pygal.py | 4 +-- plots/volcano-basic/metadata/pygal.yaml | 31 ++++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index ce66978d28..86fa546998 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai volcano-basic: Volcano Plot for Statistical Significance Library: pygal 3.1.0 | Python 3.13.11 -Quality: 78/100 | Created: 2025-12-31 +Quality: 82/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/volcano-basic/metadata/pygal.yaml b/plots/volcano-basic/metadata/pygal.yaml index fcddb08b3f..9dd0fe8ca7 100644 --- a/plots/volcano-basic/metadata/pygal.yaml +++ b/plots/volcano-basic/metadata/pygal.yaml @@ -1,7 +1,7 @@ library: pygal specification_id: volcano-basic created: '2025-12-31T05:31:07Z' -updated: '2025-12-31T05:36:36Z' +updated: '2025-12-31T05:54:02Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20612783052 issue: 2924 @@ -10,20 +10,21 @@ library_version: 3.1.0 preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.html -quality_score: 78 +quality_score: 82 review: strengths: - - Clean implementation following KISS principles with proper seed for reproducibility - - Correct volcano plot structure with appropriate data generation showing all three - gene categories - - Proper use of custom Style for font sizing and colors at 4800x2700 resolution - - Good use of subscript notation in axis labels (Log₂, Log₁₀) - - Threshold lines implemented as dashed strokes + - Correct volcano plot structure with proper three-category classification (not + significant, up-regulated, down-regulated) + - Good use of colorblind-safe color palette (gray, red, blue) + - Proper axis labels with scientific notation (Log₂, -Log₁₀) + - Clean code structure following KISS principles with reproducible seed + - Threshold lines are implemented (horizontal at y=1.3, vertical at x=±1) weaknesses: - - Y-axis range extends to -5 but all p-value data is positive; should set y-axis - minimum to 0 or slightly below - - Threshold lines appear in the legend, cluttering it with 6 entries instead of - 3 data categories - - Legend text in top-left is relatively small compared to canvas size - - Threshold lines use colors from the series palette instead of a neutral gray/black - dashed style + - Y-axis range extends to -5 when all data is above 0, wasting ~50% of vertical + canvas space on empty area + - Threshold lines are too faint/thin to be clearly visible - they should be more + prominent dashed lines + - Legend text appears small relative to canvas size and is positioned away from + the data + - No annotations for top significant genes as suggested in spec (optional but would + improve) From e0a498085f6b494f0beb1b71dc7e13963fc63360 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 05:57:11 +0000 Subject: [PATCH 6/8] fix(pygal): address review feedback for volcano-basic Attempt 2/3 - fixes based on AI review Changes: - Fixed Y-axis range to start from 0 instead of -5 to utilize full canvas - Made threshold lines more prominent (width=5, larger dash pattern) - Increased legend font size (44pt) and moved to bottom for better positioning - Increased overall font sizes for better legibility - Added gene name labels to data points for annotations in interactive mode - Added descriptive labels for threshold lines in legend --- plots/volcano-basic/implementations/pygal.py | 77 +++++++++++--------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index 86fa546998..14691c86e5 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,4 +1,4 @@ -""" pyplots.ai +"""pyplots.ai volcano-basic: Volcano Plot for Statistical Significance Library: pygal 3.1.0 | Python 3.13.11 Quality: 82/100 | Created: 2025-12-31 @@ -13,6 +13,9 @@ np.random.seed(42) n_genes = 500 +# Gene names for annotations +gene_names = [f"GENE{i:03d}" for i in range(n_genes)] + # Generate fold changes (mostly near zero, some with larger effects) log2_fc = np.concatenate( [ @@ -37,35 +40,33 @@ down_regulated = (log2_fc < -fc_threshold) & (neg_log10_pval > pval_threshold) not_significant = ~(up_regulated | down_regulated) -# Custom style for large canvas +# Custom style for large canvas with larger legend custom_style = Style( background="white", plot_background="white", foreground="#333333", foreground_strong="#333333", foreground_subtle="#666666", - colors=( - "#888888", - "#c0392b", - "#2980b9", - "#555555", - "#555555", - "#555555", - ), # Gray, Red, Blue, then neutral for lines - title_font_size=48, - label_font_size=32, - major_label_font_size=28, - legend_font_size=36, - value_font_size=20, + colors=("#888888", "#c0392b", "#2980b9", "#333333"), # Gray, Red, Blue, Dark gray for threshold lines + title_font_size=56, + label_font_size=36, + major_label_font_size=32, + legend_font_size=44, + value_font_size=24, stroke_width=2, opacity=0.7, opacity_hover=1.0, font_family="DejaVu Sans", ) -# Calculate y-axis range (ensure minimum at 0) +# Calculate y-axis range - start from 0 (not negative) to use full canvas +y_min = 0 y_max = max(neg_log10_pval) + 0.5 +# Calculate x-axis range based on data +x_min = min(log2_fc) - 0.5 +x_max = max(log2_fc) + 0.5 + # Create XY chart (scatter plot) chart = pygal.XY( width=4800, @@ -75,42 +76,52 @@ x_title="Log₂ Fold Change", y_title="-Log₁₀(p-value)", show_legend=True, - legend_at_bottom=False, - legend_box_size=28, - dots_size=8, + legend_at_bottom=True, + legend_box_size=36, + dots_size=10, stroke=False, show_x_guides=True, show_y_guides=True, x_label_rotation=0, - range=(-5, 5), - yrange=(0, y_max), + range=(x_min, x_max), + yrange=(y_min, y_max), y_labels_major_count=6, - x_labels_major_count=11, + x_labels_major_count=9, truncate_legend=-1, ) -# Prepare data points for each category +# Prepare data points for each category with gene labels for tooltips not_sig_points = [ - {"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if not_significant[i] + {"value": (float(log2_fc[i]), float(neg_log10_pval[i])), "label": gene_names[i]} + for i in range(n_genes) + if not_significant[i] +] +up_points = [ + {"value": (float(log2_fc[i]), float(neg_log10_pval[i])), "label": gene_names[i]} + for i in range(n_genes) + if up_regulated[i] +] +down_points = [ + {"value": (float(log2_fc[i]), float(neg_log10_pval[i])), "label": gene_names[i]} + for i in range(n_genes) + if down_regulated[i] ] -up_points = [{"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if up_regulated[i]] -down_points = [{"value": (float(log2_fc[i]), float(neg_log10_pval[i]))} for i in range(n_genes) if down_regulated[i]] # Add data series chart.add("Not Significant", not_sig_points) chart.add("Up-regulated", up_points) chart.add("Down-regulated", down_points) -# Add threshold lines as separate series (hidden from legend) +# Add threshold lines as separate series with prominent styling # Horizontal line at p-value threshold (y = 1.3) -h_line = [{"value": (-5, pval_threshold)}, {"value": (5, pval_threshold)}] -chart.add(None, h_line, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +h_line = [{"value": (x_min, pval_threshold)}, {"value": (x_max, pval_threshold)}] +chart.add("p=0.05 threshold", h_line, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) # Vertical lines at fold change thresholds (x = ±1) -v_line_pos = [{"value": (fc_threshold, 0)}, {"value": (fc_threshold, y_max)}] -v_line_neg = [{"value": (-fc_threshold, 0)}, {"value": (-fc_threshold, y_max)}] -chart.add(None, v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) -chart.add(None, v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 3, "dasharray": "10, 5"}) +v_line_pos = [{"value": (fc_threshold, y_min)}, {"value": (fc_threshold, y_max)}] +v_line_neg = [{"value": (-fc_threshold, y_min)}, {"value": (-fc_threshold, y_max)}] +chart.add("FC=±2 threshold", v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) +chart.add(None, v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) # Save as PNG and HTML chart.render_to_png("plot.png") From 54621a2791497a6143eace9503a04cb3a53dc976 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 06:01:32 +0000 Subject: [PATCH 7/8] chore(pygal): update quality score 75 and review feedback for volcano-basic --- plots/volcano-basic/implementations/pygal.py | 4 +-- plots/volcano-basic/metadata/pygal.yaml | 32 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index 14691c86e5..1fda8d8235 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai volcano-basic: Volcano Plot for Statistical Significance Library: pygal 3.1.0 | Python 3.13.11 -Quality: 82/100 | Created: 2025-12-31 +Quality: 75/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/volcano-basic/metadata/pygal.yaml b/plots/volcano-basic/metadata/pygal.yaml index 9dd0fe8ca7..91947c99c4 100644 --- a/plots/volcano-basic/metadata/pygal.yaml +++ b/plots/volcano-basic/metadata/pygal.yaml @@ -1,7 +1,7 @@ library: pygal specification_id: volcano-basic created: '2025-12-31T05:31:07Z' -updated: '2025-12-31T05:54:02Z' +updated: '2025-12-31T06:01:32Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20612783052 issue: 2924 @@ -10,21 +10,21 @@ library_version: 3.1.0 preview_url: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/volcano-basic/pygal/plot.html -quality_score: 82 +quality_score: 75 review: strengths: - - Correct volcano plot structure with proper three-category classification (not - significant, up-regulated, down-regulated) - - Good use of colorblind-safe color palette (gray, red, blue) - - Proper axis labels with scientific notation (Log₂, -Log₁₀) - - Clean code structure following KISS principles with reproducible seed - - Threshold lines are implemented (horizontal at y=1.3, vertical at x=±1) + - Clean three-category color coding (gray/red/blue) clearly distinguishes significance + groups + - Good use of pygal custom Style with appropriate font sizes for large canvas + - Realistic gene expression data scenario with proper statistical distributions + - Tooltips include gene names for interactivity in HTML output + - Proper scientific notation in axis labels (subscript formatting) weaknesses: - - Y-axis range extends to -5 when all data is above 0, wasting ~50% of vertical - canvas space on empty area - - Threshold lines are too faint/thin to be clearly visible - they should be more - prominent dashed lines - - Legend text appears small relative to canvas size and is positioned away from - the data - - No annotations for top significant genes as suggested in spec (optional but would - improve) + - Threshold lines (horizontal at p=0.05, vertical at FC=±2) are not visible in the + rendered output despite being coded + - Y-axis range extends to -5 when all data is positive (0 to ~4), wasting half the + vertical canvas + - Legend positioned in top-left corner instead of near the data or at bottom as + configured + - Red/blue color scheme is not colorblind-safe; consider using colorblind-friendly + palette From 15f857c72d86a1ba885aa89092484594b9b540a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 06:06:41 +0000 Subject: [PATCH 8/8] fix(pygal): address review feedback for volcano-basic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 3/3 - fixes based on AI review: - Y-axis now starts at 0 (no negative values wasting canvas space) - Added visible threshold lines: horizontal at p=0.05, vertical at FC=±2 - Changed to colorblind-safe palette (gray, orange, blue instead of red/blue) - Legend properly positioned at bottom with all series labeled --- plots/volcano-basic/implementations/pygal.py | 52 +++++++++++--------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/plots/volcano-basic/implementations/pygal.py b/plots/volcano-basic/implementations/pygal.py index 1fda8d8235..54bd09a933 100644 --- a/plots/volcano-basic/implementations/pygal.py +++ b/plots/volcano-basic/implementations/pygal.py @@ -1,4 +1,4 @@ -""" pyplots.ai +"""pyplots.ai volcano-basic: Volcano Plot for Statistical Significance Library: pygal 3.1.0 | Python 3.13.11 Quality: 75/100 | Created: 2025-12-31 @@ -40,18 +40,18 @@ down_regulated = (log2_fc < -fc_threshold) & (neg_log10_pval > pval_threshold) not_significant = ~(up_regulated | down_regulated) -# Custom style for large canvas with larger legend +# Colorblind-safe palette: gray, orange, blue, dark gray for threshold lines custom_style = Style( background="white", plot_background="white", foreground="#333333", foreground_strong="#333333", foreground_subtle="#666666", - colors=("#888888", "#c0392b", "#2980b9", "#333333"), # Gray, Red, Blue, Dark gray for threshold lines + colors=("#888888", "#E69F00", "#0072B2", "#555555", "#555555", "#555555"), # Gray, Orange, Blue, Dark gray x3 title_font_size=56, label_font_size=36, major_label_font_size=32, - legend_font_size=44, + legend_font_size=40, value_font_size=24, stroke_width=2, opacity=0.7, @@ -59,13 +59,13 @@ font_family="DejaVu Sans", ) -# Calculate y-axis range - start from 0 (not negative) to use full canvas -y_min = 0 -y_max = max(neg_log10_pval) + 0.5 +# Calculate axis ranges - tight fitting to data (y starts at 0) +y_max = float(np.ceil(max(neg_log10_pval) + 0.3)) +x_min = float(np.floor(min(log2_fc) - 0.3)) +x_max = float(np.ceil(max(log2_fc) + 0.3)) -# Calculate x-axis range based on data -x_min = min(log2_fc) - 0.5 -x_max = max(log2_fc) + 0.5 +# Generate y-axis labels from 0 to max (positive only) +y_labels = [i * 0.5 for i in range(int(y_max / 0.5) + 2)] # Create XY chart (scatter plot) chart = pygal.XY( @@ -77,19 +77,24 @@ y_title="-Log₁₀(p-value)", show_legend=True, legend_at_bottom=True, - legend_box_size=36, + legend_box_size=32, dots_size=10, stroke=False, show_x_guides=True, show_y_guides=True, x_label_rotation=0, range=(x_min, x_max), - yrange=(y_min, y_max), - y_labels_major_count=6, - x_labels_major_count=9, + include_x_axis=True, + explicit_size=True, truncate_legend=-1, + spacing=40, + margin=30, + margin_bottom=120, ) +# Set y-axis labels to start from 0 (all data is positive) +chart.y_labels = y_labels + # Prepare data points for each category with gene labels for tooltips not_sig_points = [ {"value": (float(log2_fc[i]), float(neg_log10_pval[i])), "label": gene_names[i]} @@ -112,16 +117,17 @@ chart.add("Up-regulated", up_points) chart.add("Down-regulated", down_points) -# Add threshold lines as separate series with prominent styling +# Add threshold lines as line series (dashed lines for significance cutoffs) # Horizontal line at p-value threshold (y = 1.3) -h_line = [{"value": (x_min, pval_threshold)}, {"value": (x_max, pval_threshold)}] -chart.add("p=0.05 threshold", h_line, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) - -# Vertical lines at fold change thresholds (x = ±1) -v_line_pos = [{"value": (fc_threshold, y_min)}, {"value": (fc_threshold, y_max)}] -v_line_neg = [{"value": (-fc_threshold, y_min)}, {"value": (-fc_threshold, y_max)}] -chart.add("FC=±2 threshold", v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) -chart.add(None, v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 5, "dasharray": "15, 8"}) +h_line_points = [(x_min, pval_threshold), (x_max, pval_threshold)] +chart.add("p=0.05", h_line_points, stroke=True, show_dots=False, stroke_style={"width": 4, "dasharray": "12, 6"}) + +# Vertical lines at fold change thresholds (x = ±1, representing 2-fold change) +# Each vertical line as separate series to avoid diagonal connections +v_line_neg = [(float(-fc_threshold), 0.0), (float(-fc_threshold), float(y_max))] +v_line_pos = [(float(fc_threshold), 0.0), (float(fc_threshold), float(y_max))] +chart.add("FC=-2", v_line_neg, stroke=True, show_dots=False, stroke_style={"width": 4, "dasharray": "12, 6"}) +chart.add("FC=+2", v_line_pos, stroke=True, show_dots=False, stroke_style={"width": 4, "dasharray": "12, 6"}) # Save as PNG and HTML chart.render_to_png("plot.png")