{"spec_id":"manhattan-gwas","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nmanhattan-gwas: Manhattan Plot for GWAS\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\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# Okabe-Ito palette for alternating chromosomes\nOKABE_ITO_1 = \"#009E73\"  # bluish green (brand)\nOKABE_ITO_2 = \"#C475FD\"  # vermillion\n\n# Data - Simulate GWAS data with realistic structure\nnp.random.seed(42)\n\n# Define chromosomes with approximate sizes (in Mb)\nchromosomes = [str(i) for i in range(1, 23)]\nchrom_sizes = [250, 243, 198, 190, 182, 171, 159, 145, 138, 133, 135, 133, 114, 107, 102, 90, 83, 80, 59, 64, 47, 51]\n\n# Generate SNPs for each chromosome\ndata = []\ncumulative_pos = 0\nchrom_centers = {}\nchrom_boundaries = [0]\n\nfor chrom, size in zip(chromosomes, chrom_sizes, strict=True):\n    # Number of SNPs proportional to chromosome size\n    n_snps = int(size * 40)  # ~40 SNPs per Mb, total ~10k SNPs\n\n    # Random positions along chromosome\n    positions = np.sort(np.random.randint(0, size * 1_000_000, n_snps))\n\n    # Generate p-values - mostly non-significant with some peaks\n    # Use beta distribution to get realistic p-value distribution\n    p_values = np.random.beta(1, 1, n_snps)\n\n    # Add significant peaks on specific chromosomes\n    if chrom == \"6\":  # Major peak on chr6 (like MHC region)\n        peak_region = (positions > 25_000_000) & (positions < 35_000_000)\n        p_values[peak_region] = 10 ** (-np.random.uniform(7, 12, peak_region.sum()))\n    elif chrom == \"11\":  # Moderate peak\n        peak_region = (positions > 60_000_000) & (positions < 70_000_000)\n        p_values[peak_region] = 10 ** (-np.random.uniform(6, 9, peak_region.sum()))\n    elif chrom == \"2\":  # Smaller peak\n        peak_region = (positions > 100_000_000) & (positions < 110_000_000)\n        p_values[peak_region] = 10 ** (-np.random.uniform(5.5, 8, peak_region.sum()))\n\n    # Calculate cumulative position\n    cumulative_positions = positions + cumulative_pos\n\n    # Store center for axis label\n    chrom_centers[chrom] = cumulative_pos + (size * 1_000_000) / 2\n\n    for pos, cum_pos, pval in zip(positions, cumulative_positions, p_values, strict=True):\n        data.append(\n            {\n                \"chromosome\": chrom,\n                \"position\": pos,\n                \"cumulative_position\": cum_pos,\n                \"p_value\": pval,\n                \"neg_log_p\": -np.log10(pval),\n            }\n        )\n\n    cumulative_pos += size * 1_000_000\n    chrom_boundaries.append(cumulative_pos)\n\ndf = pd.DataFrame(data)\n\n# Create alternating color groups for chromosomes\ndf[\"color_group\"] = df[\"chromosome\"].apply(lambda x: OKABE_ITO_1 if int(x) % 2 == 1 else OKABE_ITO_2)\n\n# Configure theme-adaptive styling\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\n\n# Plot non-significant SNPs with alternating colors\nfor color in [OKABE_ITO_1, OKABE_ITO_2]:\n    subset = df[df[\"color_group\"] == color]\n    ax.scatter(\n        subset[\"cumulative_position\"], subset[\"neg_log_p\"], c=color, s=20, alpha=0.7, edgecolor=\"none\", rasterized=True\n    )\n\n# Highlight significant SNPs (above genome-wide threshold)\nsignificant = df[df[\"neg_log_p\"] > 7.3]\nif len(significant) > 0:\n    ax.scatter(\n        significant[\"cumulative_position\"],\n        significant[\"neg_log_p\"],\n        c=OKABE_ITO_1,\n        s=60,\n        alpha=0.9,\n        edgecolor=INK,\n        linewidth=0.8,\n        zorder=5,\n    )\n\n# Genome-wide significance threshold\nax.axhline(y=7.3, color=INK_SOFT, linestyle=\"--\", linewidth=2, alpha=0.6, label=\"Genome-wide significance (p < 5×10⁻⁸)\")\n\n# Suggestive threshold\nax.axhline(y=5, color=INK_SOFT, linestyle=\":\", linewidth=1.5, alpha=0.4, label=\"Suggestive (p < 1×10⁻⁵)\")\n\n# Set x-axis ticks at chromosome centers\nax.set_xticks([chrom_centers[c] for c in chromosomes])\nax.set_xticklabels(chromosomes, fontsize=16)\n\n# Styling\nax.set_xlabel(\"Chromosome\", fontsize=20, color=INK)\nax.set_ylabel(\"-log₁₀(p-value)\", fontsize=20, color=INK)\nax.set_title(\"manhattan-gwas · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16)\n\n# Set axis limits\nax.set_xlim(0, cumulative_pos)\nax.set_ylim(0, max(df[\"neg_log_p\"]) * 1.05)\n\n# Remove top and right spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Add legend\nax.legend(loc=\"upper right\", fontsize=16, framealpha=0.95, edgecolor=INK_SOFT)\n\n# Subtle grid on y-axis only\nax.yaxis.grid(True, alpha=0.10, linewidth=0.8)\nax.xaxis.grid(False)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}