{"spec_id":"genome-track-multi","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\ngenome-track-multi: Genome Track Viewer\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\nimport pandas as pd\n\n\n# Work around naming conflict with plotnine.py script and plotnine package\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nfor _p in [_script_dir, \"\", \".\"]:\n    if _p in sys.path:\n        sys.path.remove(_p)\n\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_point,\n    geom_rect,\n    geom_ribbon,\n    geom_segment,\n    geom_text,\n    ggplot,\n    guide_legend,\n    guides,\n    labs,\n    scale_color_manual,\n    scale_fill_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nnp.random.seed(42)\n\n# Theme-adaptive chrome — Imprint palette\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — 8 hues, hybrid-v3 sort\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Genomic region: chr7, EGFR locus (~55kb region)\nchrom = \"chr7\"\nregion_start = 55_086_000\nregion_end = 55_141_000\n\n# Track vertical layout (bottom to top):\n# Regulatory:  0.0 – 1.0\n# Variants:    1.5 – 3.5\n# Coverage:    4.0 – 6.0\n# Genes:       6.5 – 7.5\n\n# Shaded track backgrounds for regulatory and coverage tracks (theme-adaptive)\ntrack_bg_shaded = pd.DataFrame(\n    {\"xmin\": [region_start, region_start], \"xmax\": [region_end, region_end], \"ymin\": [0.0, 4.0], \"ymax\": [1.0, 6.0]}\n)\n\n# Track labels (bold, theme-adaptive ink)\ntrack_labels = pd.DataFrame(\n    {\n        \"x\": [region_start + 800] * 4,\n        \"y\": [0.88, 3.35, 5.88, 7.38],\n        \"label\": [\"Regulatory\", \"Variants\", \"Coverage\", \"Genes\"],\n    }\n)\n\n# --- Gene Track (center at y=7.0) ---\ngene_center = 7.0\nexon_half = 0.25\n\nexons = pd.DataFrame(\n    {\n        \"start\": [\n            55_086_725,\n            55_087_058,\n            55_088_850,\n            55_092_340,\n            55_095_262,\n            55_097_600,\n            55_099_310,\n            55_110_700,\n            55_117_550,\n            55_124_950,\n            55_131_800,\n            55_136_500,\n            55_139_800,\n        ],\n        \"end\": [\n            55_087_020,\n            55_087_350,\n            55_089_150,\n            55_092_580,\n            55_095_530,\n            55_097_870,\n            55_099_550,\n            55_110_960,\n            55_117_810,\n            55_125_200,\n            55_132_050,\n            55_136_750,\n            55_140_100,\n        ],\n    }\n)\nexons[\"ymin\"] = gene_center - exon_half\nexons[\"ymax\"] = gene_center + exon_half\n\nintron_lines = pd.DataFrame(\n    {\n        \"x\": exons[\"end\"].iloc[:-1].values,\n        \"xend\": exons[\"start\"].iloc[1:].values,\n        \"y\": [gene_center] * (len(exons) - 1),\n        \"yend\": [gene_center] * (len(exons) - 1),\n    }\n)\n\n# Strand direction chevrons (+ strand) — more prominent than previous iteration\nchevron_x = np.linspace(region_start + 3000, region_end - 3000, 25)\nchevron_size = 400\nchevrons_up = pd.DataFrame(\n    {\n        \"x\": chevron_x,\n        \"xend\": chevron_x + chevron_size,\n        \"y\": [gene_center] * len(chevron_x),\n        \"yend\": [gene_center + 0.14] * len(chevron_x),\n    }\n)\nchevrons_down = pd.DataFrame(\n    {\n        \"x\": chevron_x,\n        \"xend\": chevron_x + chevron_size,\n        \"y\": [gene_center] * len(chevron_x),\n        \"yend\": [gene_center - 0.14] * len(chevron_x),\n    }\n)\n\ngene_label = pd.DataFrame({\"x\": [(region_start + region_end) / 2], \"y\": [gene_center + 0.42], \"label\": [\"EGFR  (+)\"]})\n\n# --- Coverage Track (y: 4.0 – 6.0) ---\ncov_base = 4.0\ncov_height = 2.0\npositions = np.arange(region_start, region_end, 150)\nraw_coverage = 25 + 12 * np.sin(np.linspace(0, 3.5 * np.pi, len(positions)))\n\nfor _, exon in exons.iterrows():\n    mask = (positions >= exon[\"start\"] - 800) & (positions <= exon[\"end\"] + 800)\n    raw_coverage[mask] += np.random.uniform(15, 45)\n\nraw_coverage += np.random.normal(0, 4, len(positions))\nraw_coverage = np.maximum(raw_coverage, 0)\nmax_cov = raw_coverage.max()\nnormalized_cov = raw_coverage / max_cov * cov_height\n\ncoverage_df = pd.DataFrame({\"x\": positions, \"ymin\": [cov_base] * len(positions), \"ymax\": cov_base + normalized_cov})\n\n# --- Variant Track (y: 1.5 – 3.5) ---\nvar_base = 1.5\nvar_height = 2.0\nn_variants = 18\nvariant_positions = np.sort(np.random.randint(region_start + 500, region_end - 500, n_variants))\nvariant_quality = np.random.uniform(10, 100, n_variants)\nvariant_types = np.random.choice([\"SNP\", \"Indel\"], n_variants, p=[0.75, 0.25])\nnormalized_quality = variant_quality / 100.0 * var_height\n\nvariant_stems = pd.DataFrame(\n    {\n        \"x\": variant_positions,\n        \"xend\": variant_positions,\n        \"y\": [var_base] * n_variants,\n        \"yend\": var_base + normalized_quality,\n    }\n)\n\nvariant_heads = pd.DataFrame(\n    {\n        \"x\": variant_positions,\n        \"y\": var_base + normalized_quality,\n        \"variant_type\": pd.Categorical(variant_types, categories=[\"SNP\", \"Indel\"], ordered=True),\n    }\n)\n\n# Imprint positions 1→2: SNP (brand green, first series), Indel (lavender — no proximity with ochre Enhancers)\nvariant_palette = {\"SNP\": IMPRINT[0], \"Indel\": IMPRINT[1]}\n\n# --- Regulatory Track (y: 0.0 – 1.0, center at 0.5) ---\nreg_center = 0.5\nreg_half = 0.25\n\nregulatory_elements = pd.DataFrame(\n    {\n        \"start\": [55_086_200, 55_088_400, 55_093_100, 55_098_900, 55_112_300, 55_120_000, 55_128_500, 55_135_200],\n        \"end\": [55_086_700, 55_088_800, 55_093_500, 55_099_250, 55_112_700, 55_120_450, 55_128_900, 55_135_600],\n        \"feature_type\": pd.Categorical(\n            [\"Promoter\", \"Enhancer\", \"Enhancer\", \"Promoter\", \"Enhancer\", \"Insulator\", \"Enhancer\", \"Promoter\"],\n            categories=[\"Promoter\", \"Enhancer\", \"Insulator\"],\n            ordered=True,\n        ),\n    }\n)\nregulatory_elements[\"ymin\"] = reg_center - reg_half\nregulatory_elements[\"ymax\"] = reg_center + reg_half\n\n# Imprint positions 3→5: Promoter (blue), Enhancer (ochre), Insulator (red)\nregulatory_palette = {\"Promoter\": IMPRINT[2], \"Enhancer\": IMPRINT[3], \"Insulator\": IMPRINT[4]}\n\n# Track separator lines (theme-adaptive dashed)\nseparators = pd.DataFrame({\"yintercept\": [1.25, 3.75, 6.25]})\n\n# Build plot with grammar-of-graphics layer composition\nplot = (\n    ggplot()\n    # Shaded track backgrounds (regulatory and coverage tracks)\n    + geom_rect(\n        data=track_bg_shaded,\n        mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=ELEVATED_BG,\n        alpha=0.8,\n    )\n    # Track separators (theme-adaptive)\n    + geom_hline(data=separators, mapping=aes(yintercept=\"yintercept\"), color=INK_MUTED, size=0.4, linetype=\"dashed\")\n    # Gene track: intron connector lines\n    + geom_segment(data=intron_lines, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_SOFT, size=0.6)\n    # Gene track: strand chevrons — increased size and alpha vs previous iteration\n    + geom_segment(\n        data=chevrons_up, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_SOFT, size=0.55, alpha=0.85\n    )\n    + geom_segment(\n        data=chevrons_down, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_SOFT, size=0.55, alpha=0.85\n    )\n    # Gene track: exon rectangles (Imprint blue)\n    + geom_rect(\n        data=exons,\n        mapping=aes(xmin=\"start\", xmax=\"end\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT[2],\n        color=PAGE_BG,\n        size=0.25,\n    )\n    # Gene track: EGFR gene label\n    + geom_text(\n        data=gene_label,\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=3.5,\n        color=INK,\n        fontstyle=\"italic\",\n        fontweight=\"bold\",\n    )\n    # Coverage track: filled area (Imprint cyan)\n    + geom_ribbon(\n        data=coverage_df,\n        mapping=aes(x=\"x\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT[5],\n        alpha=0.65,\n        color=IMPRINT[5],\n        size=0.2,\n    )\n    # Variant track: stems (muted structural lines)\n    + geom_segment(data=variant_stems, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_MUTED, size=0.5)\n    # Variant track: lollipop heads with Imprint colors (generates native legend)\n    + geom_point(data=variant_heads, mapping=aes(x=\"x\", y=\"y\", color=\"variant_type\"), size=3, alpha=0.9)\n    + scale_color_manual(name=\"Variant Type\", values=variant_palette, guide=guide_legend(order=1))\n    # Regulatory track: colored rectangles with Imprint palette (generates native legend)\n    + geom_rect(\n        data=regulatory_elements, mapping=aes(xmin=\"start\", xmax=\"end\", ymin=\"ymin\", ymax=\"ymax\", fill=\"feature_type\")\n    )\n    + scale_fill_manual(name=\"Regulatory\", values=regulatory_palette, guide=guide_legend(order=2))\n    # Track labels (theme-adaptive, bold)\n    + geom_text(\n        data=track_labels,\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=3.5,\n        ha=\"left\",\n        fontweight=\"bold\",\n        color=INK_SOFT,\n    )\n    # Axes\n    + scale_x_continuous(\n        labels=lambda x: [f\"{int(v):,}\" for v in x], limits=(region_start, region_end), expand=(0.01, 0)\n    )\n    + scale_y_continuous(limits=(-0.2, 7.8), breaks=[], expand=(0, 0))\n    + labs(title=\"genome-track-multi · python · plotnine · anyplot.ai\", x=f\"Genomic Position ({chrom})\", y=\"\")\n    # Theme-adaptive chrome (Imprint palette canonical tokens)\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_title=element_text(size=12, weight=\"bold\", color=INK),\n        axis_title_x=element_text(size=10, color=INK),\n        axis_title_y=element_blank(),\n        axis_text_x=element_text(size=8, color=INK_SOFT),\n        axis_text_y=element_blank(),\n        axis_ticks_major_y=element_blank(),\n        legend_title=element_text(size=8, weight=\"bold\", color=INK),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_position=\"right\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_key_size=10,\n        panel_grid_minor=element_blank(),\n        panel_grid_major_x=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_grid_major_y=element_blank(),\n        panel_background=element_rect(fill=PAGE_BG, color=None),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    )\n    + guides(fill=guide_legend(override_aes={\"alpha\": 1}))\n)\n\n# Save — landscape 3200×1800 px (8 in × 4.5 in @ 400 dpi)\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\")\n"}