{"spec_id":"manhattan-gwas","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nmanhattan-gwas: Manhattan Plot for GWAS\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\nnp.random.seed(42)\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Chromosome lengths (simplified, in Mb)\nchrom_lengths = {\n    \"1\": 249,\n    \"2\": 243,\n    \"3\": 198,\n    \"4\": 191,\n    \"5\": 182,\n    \"6\": 171,\n    \"7\": 159,\n    \"8\": 146,\n    \"9\": 141,\n    \"10\": 136,\n    \"11\": 135,\n    \"12\": 134,\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\nchromosomes = list(chrom_lengths.keys())\nn_snps_per_chrom = 500\n\n# Storage for data\nall_chroms = []\nall_cumulative_pos = []\nall_pvalues = []\n\n# Track chromosome boundaries for x-axis labels\nchrom_midpoints = []\ncumulative_offset = 0\n\nfor chrom in chromosomes:\n    length = chrom_lengths[chrom]\n    positions = np.sort(np.random.uniform(0, length, n_snps_per_chrom))\n\n    # Store chromosome midpoint for x-axis label\n    chrom_midpoints.append(cumulative_offset + length / 2)\n\n    # Generate p-values: mostly uniform with some significant hits\n    pvalues = np.random.uniform(0, 1, n_snps_per_chrom)\n\n    # Add significant peaks on selected chromosomes\n    if chrom in [\"6\", \"11\", \"16\"]:\n        n_sig = np.random.randint(3, 8)\n        sig_indices = np.random.choice(n_snps_per_chrom, n_sig, replace=False)\n        pvalues[sig_indices] = 10 ** (-np.random.uniform(8, 15, n_sig))\n\n    # Add suggestive signals\n    n_sugg = np.random.randint(5, 15)\n    sugg_indices = np.random.choice(n_snps_per_chrom, n_sugg, replace=False)\n    pvalues[sugg_indices] = 10 ** (-np.random.uniform(5, 8, n_sugg))\n\n    cumulative_positions = positions + cumulative_offset\n    all_chroms.extend([chrom] * n_snps_per_chrom)\n    all_cumulative_pos.extend(cumulative_positions)\n    all_pvalues.extend(pvalues)\n\n    cumulative_offset += length\n\n# Convert to -log10 p-values\nneg_log_pvalues = -np.log10(np.array(all_pvalues))\n\n# Thresholds\ngenome_wide_sig = -np.log10(5e-8)  # ~7.3\nsuggestive_sig = 5.0  # -log10(1e-5)\n\n# Custom style with theme-adaptive colors\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n)\n\n# Create XY scatter chart\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"manhattan-gwas · pygal · anyplot.ai\",\n    x_title=\"Chromosome\",\n    y_title=\"-log₁₀(p-value)\",\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=5,\n    legend_box_size=16,\n    stroke=False,\n    dots_size=4,\n    show_x_guides=False,\n    show_y_guides=True,\n    truncate_label=-1,\n    print_values=False,\n    x_label_rotation=0,\n    range=(0, 16),\n    xrange=(0, cumulative_offset),\n    tooltip_border_radius=5,\n    explicit_size=True,\n    spacing=20,\n    margin=50,\n    margin_bottom=150,\n    margin_top=80,\n)\n\n# Set x-axis labels at chromosome midpoints\nchart.x_labels = chrom_midpoints\n\n\ndef format_x_label(x_val):\n    \"\"\"Find closest chromosome midpoint and return chromosome number.\"\"\"\n    if not chrom_midpoints:\n        return \"\"\n    min_dist = float(\"inf\")\n    closest_idx = 0\n    for i, pos in enumerate(chrom_midpoints):\n        dist = abs(x_val - pos)\n        if dist < min_dist:\n            min_dist = dist\n            closest_idx = i\n    # Only return label if close to midpoint\n    if min_dist < 50:\n        return str(closest_idx + 1)\n    return \"\"\n\n\nchart.x_value_formatter = lambda x: format_x_label(x)\n\n# Prepare data by chromosome with alternating colors\nodd_chrom_points = []\neven_chrom_points = []\nsignificant_points = []\n\nfor idx, chrom in enumerate(chromosomes):\n    chrom_mask = [c == chrom for c in all_chroms]\n    chrom_x = [all_cumulative_pos[j] for j in range(len(all_chroms)) if chrom_mask[j]]\n    chrom_y = [neg_log_pvalues[j] for j in range(len(all_chroms)) if chrom_mask[j]]\n\n    for x, y in zip(chrom_x, chrom_y, strict=True):\n        point = {\"value\": (x, y), \"label\": f\"Chr {chrom}: {x:.1f} Mb, -log₁₀(p)={y:.2f}\"}\n        if y >= genome_wide_sig:\n            significant_points.append(point)\n        elif idx % 2 == 0:\n            odd_chrom_points.append(point)\n        else:\n            even_chrom_points.append(point)\n\n# Add data series\nchart.add(\"Odd chromosomes\", odd_chrom_points, stroke=False, show_dots=True)\nchart.add(\"Even chromosomes\", even_chrom_points, stroke=False, show_dots=True)\nchart.add(\"Significant (p<5×10⁻⁸)\", significant_points, stroke=False, show_dots=True)\n\n# Add threshold lines\nn_line_points = 200\nthreshold_x = np.linspace(10, cumulative_offset - 10, n_line_points)\n\ngw_line_points = [\n    {\"value\": (x, genome_wide_sig), \"label\": f\"Genome-wide threshold: -log₁₀(5×10⁻⁸) = {genome_wide_sig:.1f}\"}\n    for x in threshold_x\n]\nchart.add(\"p = 5×10⁻⁸ threshold\", gw_line_points, stroke=True, show_dots=True, dots_size=2)\n\nsugg_line_points = [\n    {\"value\": (x, suggestive_sig), \"label\": f\"Suggestive threshold: -log₁₀(1×10⁻⁵) = {suggestive_sig:.1f}\"}\n    for x in threshold_x\n]\nchart.add(\"p = 1×10⁻⁵ threshold\", sugg_line_points, stroke=True, show_dots=True, dots_size=2)\n\n# Save outputs\nchart.render_to_file(f\"plot-{THEME}.html\")\nchart.render_to_png(f\"plot-{THEME}.png\")\n"}