Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 43 additions & 24 deletions plots/manhattan-gwas/implementations/python/letsplot.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
""" pyplots.ai
""" anyplot.ai
manhattan-gwas: Manhattan Plot for GWAS
Library: letsplot 4.8.2 | Python 3.13.11
Quality: 92/100 | Created: 2025-12-31
Library: letsplot 4.9.0 | Python 3.13.13
Quality: 92/100 | Updated: 2026-05-15
"""

import os

import numpy as np
import pandas as pd
from lets_plot import *


LetsPlot.setup_html()

# Theme-adaptive colors
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
RULE = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"

# Okabe-Ito palette for alternating chromosomes
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"]

# Set seed for reproducibility
np.random.seed(42)

Expand All @@ -24,7 +38,7 @@

for i, chrom in enumerate(chromosomes):
# Random positions within chromosome (scaled by chromosome "size")
chrom_size = 250_000_000 - i * 5_000_000 # Varying chromosome sizes
chrom_size = 250_000_000 - i * 5_000_000
positions = np.sort(np.random.randint(1, chrom_size, n_snps_per_chrom))

# Generate p-values - mostly non-significant with some peaks
Expand Down Expand Up @@ -61,11 +75,9 @@

df = pd.DataFrame(data)

# Create chromosome index for coloring (alternating pattern)
# Assign alternating colors using Okabe-Ito palette
df["chrom_idx"] = df["chromosome"].astype(int) % 2

# Assign colors based on alternating pattern
df["color_group"] = df["chrom_idx"].map({0: "even", 1: "odd"})
df["color_group"] = df["chrom_idx"].map({0: OKABE_ITO[0], 1: OKABE_ITO[1]})

# Significance thresholds
genome_wide_threshold = -np.log10(5e-8) # ~7.3
Expand All @@ -80,33 +92,40 @@
# Create the Manhattan plot
plot = (
ggplot(df, aes(x="cumulative_pos", y="neg_log10_p", color="color_group"))
+ geom_point(size=1.5, alpha=0.7)
+ scale_color_manual(values=["#306998", "#7A9BBD"], guide="none")
+ geom_point(size=2, alpha=0.7)
+ scale_color_identity()
# Highlight significant points
+ geom_point(
data=df[df["significant"]], mapping=aes(x="cumulative_pos", y="neg_log10_p"), color="#DC2626", size=3, alpha=0.9
data=df[df["significant"]],
mapping=aes(x="cumulative_pos", y="neg_log10_p"),
color=OKABE_ITO[4],
size=3.5,
alpha=0.9,
)
# Genome-wide significance threshold line
+ geom_hline(yintercept=genome_wide_threshold, linetype="dashed", color="#DC2626", size=0.8)
+ geom_hline(yintercept=genome_wide_threshold, linetype="dashed", color=OKABE_ITO[4], size=1)
# Suggestive threshold line
+ geom_hline(yintercept=suggestive_threshold, linetype="dotted", color="#666666", size=0.6)
+ labs(title="manhattan-gwas · letsplot · pyplots.ai", x="Chromosome", y="-log₁₀(p-value)")
+ geom_hline(yintercept=suggestive_threshold, linetype="dotted", color=INK_MUTED, size=0.7)
+ labs(title="manhattan-gwas · letsplot · anyplot.ai", x="Chromosome", y="-log₁₀(p-value)")
+ scale_x_continuous(breaks=tick_positions, labels=chromosomes)
+ theme_minimal()
+ theme(
plot_title=element_text(size=28, face="bold"),
axis_title_x=element_text(size=22),
axis_title_y=element_text(size=22),
axis_text_x=element_text(size=14),
axis_text_y=element_text(size=16),
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_grid_major_y=element_line(color=RULE, size=0.3),
panel_grid_major_x=element_blank(),
panel_grid_minor=element_blank(),
plot_title=element_text(size=28, face="bold", color=INK),
axis_title_x=element_text(size=22, color=INK),
axis_title_y=element_text(size=22, color=INK),
axis_text_x=element_text(size=16, color=INK_SOFT),
axis_text_y=element_text(size=16, color=INK_SOFT),
axis_line_x=element_line(color=INK_SOFT, size=0.6),
axis_line_y=element_line(color=INK_SOFT, size=0.6),
)
+ ggsize(1600, 900)
)

# Save as PNG (scale 3x for 4800x2700)
ggsave(plot, "plot.png", path=".", scale=3)

# Save interactive HTML version
ggsave(plot, "plot.html", path=".")
# Save as PNG and HTML with theme suffix
ggsave(plot, f"plot-{THEME}.png", path=".", scale=3)
ggsave(plot, f"plot-{THEME}.html", path=".")
230 changes: 38 additions & 192 deletions plots/manhattan-gwas/metadata/python/letsplot.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,43 @@
library: letsplot
language: python
specification_id: manhattan-gwas
created: '2025-12-31T05:34:36Z'
updated: '2025-12-31T05:46:54Z'
generated_by: claude-opus-4-5-20251101
workflow_run: 20612795129
updated: '2026-05-15T03:43:49Z'
generated_by: claude-haiku
workflow_run: 25898824897
issue: 2925
python_version: 3.13.11
library_version: 4.8.2
preview_url: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/letsplot/plot.png
preview_html: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/letsplot/plot.html
python_version: 3.13.13
library_version: 4.9.0
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/python/letsplot/plot-light.png
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/python/letsplot/plot-dark.png
preview_html_light: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/python/letsplot/plot-light.html
preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/manhattan-gwas/python/letsplot/plot-dark.html
quality_score: 92
review:
strengths:
- Perfect technical execution across all visual quality criteria
- Excellent theme-adaptive design with proper color token usage for both light and
dark renders
- Realistic, comprehensive GWAS data example with significant peaks and suggestive
signals
- Clean, reproducible, idiomatic letsplot code following grammar of graphics idioms
- All specification requirements met with attention to detail and polish
weaknesses:
- Design excellence relies on library defaults rather than unique aesthetic innovation—consider
exploring custom color harmonies or refined visual treatments in future implementations
image_description: |-
Light render (plot-light.png):
Background: Warm off-white (#FAF8F1) as specified
Chrome: Title "manhattan-gwas · letsplot · anyplot.ai" in dark bold text; x-axis label "Chromosome" and y-axis label "-log₁₀(p-value)" clearly visible in dark text (INK token); tick labels (chromosome 1-22) and y-axis values readable in secondary dark color (INK_SOFT token); horizontal grid lines subtle with proper RULE token opacity
Data: Alternating chromosome bands in Okabe-Ito colors—green (#009E73) and orange (#D55E00) alternate across 22 chromosomes; significant SNPs above y≈7.3 threshold highlighted in brighter orange (#E69F00); yellow/orange dashed line at genome-wide significance threshold; dotted line at suggestive threshold (y=5); ~44,000 data points with alpha=0.7 preventing overplotting
Legibility verdict: PASS - All text readable against light background; data colors distinct and clear

Dark render (plot-dark.png):
Background: Warm near-black (#1A1A17) as specified
Chrome: Title in light text (INK token #F0EFE8); axis labels clearly visible in light color; tick labels in secondary light color (INK_SOFT token #B8B7B0); grid lines visible with proper dark-theme opacity; NO dark-on-dark failures observed
Data: Identical colors to light render—same green (#009E73) and orange (#D55E00) alternating pattern, same orange highlights for significant SNPs, confirming only chrome has adapted to theme while data colors remain constant
Legibility verdict: PASS - All text readable against dark background; data colors identical to light render, proving correct theme-adaptive implementation; no dark text on dark background issues
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
Expand All @@ -18,189 +46,7 @@ impl_tags:
- html-export
patterns:
- data-generation
- iteration-over-groups
dataprep: []
styling: []
review:
strengths:
- Excellent Manhattan plot structure with proper cumulative genomic positions and
chromosome labeling
- Effective use of alternating blue/gray colors for chromosome distinction
- Significant SNPs highlighted in red above genome-wide threshold line
- Both genome-wide (dashed red) and suggestive (dotted gray) threshold lines included
- Good data generation with realistic GWAS patterns including peaks on specific
chromosomes
weaknesses:
- Chromosome labels on x-axis could be slightly larger for better readability at
full resolution
image_description: The Manhattan plot displays GWAS results across 22 chromosomes
arranged along the x-axis with cumulative genomic positions. Points are colored
in alternating blue (#306998) and light blue (#7A9BBD) for adjacent chromosomes.
The y-axis shows -log₁₀(p-value) ranging from 0 to 12. A horizontal dashed red
line indicates the genome-wide significance threshold at approximately 7.3 (-log₁₀(5×10⁻⁸)),
and a dotted gray line marks the suggestive threshold at 5. Significant SNPs above
the genome-wide threshold are highlighted with larger red points. Clear peaks
are visible on chromosomes 2, 6, 11, and 17, with additional suggestive signals
on chromosomes 1, 5, 8, 14, and 19. The title "manhattan-gwas · letsplot · pyplots.ai"
is displayed at the top in bold. The overall layout is well-balanced with good
use of the canvas space.
criteria_checklist:
visual_quality:
score: 36
max: 40
items:
- id: VQ-01
name: Text Legibility
score: 9
max: 10
passed: true
comment: Title large and bold, axis labels clear, chromosome labels readable
but slightly small
- id: VQ-02
name: No Overlap
score: 8
max: 8
passed: true
comment: No overlapping text elements
- id: VQ-03
name: Element Visibility
score: 7
max: 8
passed: true
comment: Points well-sized with appropriate alpha for dense data, significant
points clearly highlighted
- id: VQ-04
name: Color Accessibility
score: 5
max: 5
passed: true
comment: Blue/gray alternating scheme is colorblind-safe, red highlights distinct
- id: VQ-05
name: Layout Balance
score: 5
max: 5
passed: true
comment: Good use of canvas, plot fills appropriate area
- id: VQ-06
name: Axis Labels
score: 1
max: 2
passed: true
comment: Y-axis has subscript notation (-log₁₀), but no units needed for this
type
- id: VQ-07
name: Grid & Legend
score: 1
max: 2
passed: true
comment: Minimal grid (major x removed), no legend needed (guide="none" appropriate)
spec_compliance:
score: 25
max: 25
items:
- id: SC-01
name: Plot Type
score: 8
max: 8
passed: true
comment: Correct Manhattan plot
- id: SC-02
name: Data Mapping
score: 5
max: 5
passed: true
comment: Cumulative position on X, -log₁₀(p) on Y
- id: SC-03
name: Required Features
score: 5
max: 5
passed: true
comment: 'All features: alternating colors, threshold lines, significant SNP
highlighting'
- id: SC-04
name: Data Range
score: 3
max: 3
passed: true
comment: All chromosomes 1-22 visible, y-axis shows full range
- id: SC-05
name: Legend Accuracy
score: 2
max: 2
passed: true
comment: Legend hidden appropriately (colors self-explanatory)
- id: SC-06
name: Title Format
score: 2
max: 2
passed: true
comment: 'Correct format: "manhattan-gwas · letsplot · pyplots.ai"'
data_quality:
score: 19
max: 20
items:
- id: DQ-01
name: Feature Coverage
score: 8
max: 8
passed: true
comment: Shows significant peaks, suggestive signals, and background noise
- id: DQ-02
name: Realistic Context
score: 7
max: 7
passed: true
comment: Simulated GWAS data with realistic patterns (44,000 SNPs across 22
chromosomes)
- id: DQ-03
name: Appropriate Scale
score: 4
max: 5
passed: true
comment: P-values span realistic range, chromosome sizes vary appropriately
code_quality:
score: 9
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: 'Clean linear flow: imports → data → plot → save'
- id: CQ-02
name: Reproducibility
score: 3
max: 3
passed: true
comment: np.random.seed(42) set
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: Only numpy, pandas, lets_plot used
- id: CQ-04
name: No Deprecated API
score: 1
max: 1
passed: true
comment: Current lets-plot API
- id: CQ-05
name: Output Correct
score: 0
max: 1
passed: false
comment: Saves to "plot.png" but uses path="." parameter
library_features:
score: 3
max: 5
items:
- id: LF-01
name: Uses distinctive library features
score: 3
max: 5
passed: true
comment: Good use of ggplot grammar, scale_x_continuous with custom breaks/labels,
theme customization, but could use more advanced lets-plot features
verdict: APPROVED
styling:
- grid-styling
- alpha-blending
Loading