{"spec_id":"manhattan-gwas","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nmanhattan-gwas: Manhattan Plot for GWAS\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 84/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_point,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Data - Simulated GWAS results with independent peaks\nnp.random.seed(123)  # Different seed to produce different peaks\n\n# Chromosome sizes (approximate in Mb)\nchr_sizes = {\n    \"1\": 249,\n    \"2\": 243,\n    \"3\": 198,\n    \"4\": 191,\n    \"5\": 182,\n    \"6\": 171,\n    \"7\": 159,\n    \"8\": 145,\n    \"9\": 138,\n    \"10\": 134,\n    \"11\": 135,\n    \"12\": 133,\n    \"13\": 115,\n    \"14\": 107,\n    \"15\": 102,\n    \"16\": 90,\n    \"17\": 83,\n    \"18\": 80,\n    \"19\": 59,\n    \"20\": 64,\n    \"21\": 47,\n    \"22\": 51,\n}\n\n# Generate SNPs per chromosome (proportional to size)\nchromosomes = list(chr_sizes.keys())\nsnp_data = []\n\n# Cumulative positions for x-axis\ncumulative_offset = 0\nchr_offsets = {}\nchr_midpoints = {}\n\nfor chrom in chromosomes:\n    chr_offsets[chrom] = cumulative_offset\n    size = chr_sizes[chrom]\n    n_snps = int(size * 40)  # ~40 SNPs per Mb = ~8000 total\n\n    # Generate positions\n    positions = np.sort(np.random.uniform(0, size * 1e6, n_snps))\n\n    # Generate p-values (mostly non-significant, with some peaks)\n    p_values = np.random.uniform(0.001, 1, n_snps)\n\n    # Add significant peaks on specific chromosomes (different from other libraries)\n    if chrom in [\"1\", \"7\", \"9\", \"18\"]:\n        # Add 20-40 highly significant SNPs\n        n_sig = np.random.randint(20, 40)\n        peak_idx = np.random.choice(n_snps, n_sig, replace=False)\n        p_values[peak_idx] = 10 ** np.random.uniform(-10, -7.3, n_sig)\n\n    # Add some suggestive signals on other chromosomes\n    if chrom in [\"5\", \"12\", \"14\", \"19\"]:\n        n_sug = np.random.randint(10, 20)\n        sug_idx = np.random.choice(n_snps, n_sug, replace=False)\n        p_values[sug_idx] = 10 ** np.random.uniform(-7, -5, n_sug)\n\n    # Calculate cumulative position\n    cumulative_positions = positions + cumulative_offset\n\n    chr_midpoints[chrom] = cumulative_offset + (size * 1e6) / 2\n\n    for i in range(n_snps):\n        snp_data.append(\n            {\n                \"chromosome\": chrom,\n                \"position\": positions[i],\n                \"cumulative_pos\": cumulative_positions[i],\n                \"p_value\": p_values[i],\n            }\n        )\n\n    cumulative_offset += size * 1e6\n\n# Create DataFrame\ndf = pd.DataFrame(snp_data)\n\n# Calculate -log10(p-value)\ndf[\"neg_log_p\"] = -np.log10(df[\"p_value\"])\n\n# Assign alternating colors based on chromosome\nchr_order = {c: i for i, c in enumerate(chromosomes)}\ndf[\"chr_num\"] = df[\"chromosome\"].map(chr_order)\ndf[\"color_group\"] = df[\"chr_num\"].apply(lambda x: \"odd\" if x % 2 == 0 else \"even\")\n\n# Threshold lines\ngenome_wide_threshold = -np.log10(5e-8)  # ~7.3\nsuggestive_threshold = -np.log10(1e-5)  # 5\n\n# Identify top SNPs for potential labeling (above genome-wide significance)\ndf[\"significant\"] = df[\"neg_log_p\"] > genome_wide_threshold\n\n# Create chromosome tick positions and labels\nchr_ticks = [chr_midpoints[c] / 1e6 for c in chromosomes]  # Convert to Mb for display\nchr_labels = chromosomes\n\n# Scale positions to Mb for cleaner axis\ndf[\"cumulative_pos_mb\"] = df[\"cumulative_pos\"] / 1e6\n\n# Plot\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_border=element_rect(color=INK_SOFT, fill=None),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_text_x=element_text(size=14),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(color=INK, size=24, hjust=0.5),\n    legend_position=\"none\",\n)\n\nplot = (\n    ggplot(df, aes(x=\"cumulative_pos_mb\", y=\"neg_log_p\", color=\"color_group\"))\n    + geom_point(size=1.5, alpha=0.7)\n    + geom_hline(yintercept=genome_wide_threshold, linetype=\"dashed\", color=\"#E31A1C\", size=1)\n    + geom_hline(yintercept=suggestive_threshold, linetype=\"dotted\", color=\"#FF7F00\", size=0.8)\n    + scale_color_manual(values={\"odd\": \"#4467A3\", \"even\": \"#C475FD\"})\n    + scale_x_continuous(breaks=chr_ticks, labels=chr_labels)\n    + scale_y_continuous(limits=(0, max(df[\"neg_log_p\"]) * 1.05))\n    + labs(x=\"Chromosome\", y=\"-log₁₀(p-value)\", title=\"manhattan-gwas · plotnine · anyplot.ai\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300, width=16, height=9)\n"}