{"spec_id":"sequence-logo-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nsequence-logo-basic: Sequence Logo for Motif Visualization\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport matplotlib.transforms as transforms\nimport numpy as np\nfrom matplotlib.font_manager import FontProperties\nfrom matplotlib.lines import Line2D\nfrom matplotlib.patches import FancyBboxPatch, PathPatch\nfrom matplotlib.textpath import TextPath\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\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\"\nANYPLOT_AMBER = \"#DDCC77\"  # warning / caution anchor — used for conserved core highlight\n\n# DNA colors — semantic exception: standard ACGT associations map to Imprint palette\n# A=green → #009E73, C=blue → #4467A3, G=orange/ochre → #BD8233, T=red → #AE3030\ndna_colors = {\"A\": \"#009E73\", \"C\": \"#4467A3\", \"G\": \"#BD8233\", \"T\": \"#AE3030\"}\n\n# Data — 10-position ETS-family DNA transcription factor binding site motif\nposition_freqs = [\n    {\"A\": 0.25, \"C\": 0.25, \"G\": 0.25, \"T\": 0.25},\n    {\"A\": 0.10, \"C\": 0.60, \"G\": 0.10, \"T\": 0.20},\n    {\"A\": 0.05, \"C\": 0.05, \"G\": 0.85, \"T\": 0.05},\n    {\"A\": 0.90, \"C\": 0.02, \"G\": 0.03, \"T\": 0.05},\n    {\"A\": 0.02, \"C\": 0.02, \"G\": 0.94, \"T\": 0.02},\n    {\"A\": 0.02, \"C\": 0.02, \"G\": 0.02, \"T\": 0.94},\n    {\"A\": 0.15, \"C\": 0.35, \"G\": 0.15, \"T\": 0.35},\n    {\"A\": 0.30, \"C\": 0.20, \"G\": 0.30, \"T\": 0.20},\n    {\"A\": 0.05, \"C\": 0.05, \"G\": 0.05, \"T\": 0.85},\n    {\"A\": 0.25, \"C\": 0.25, \"G\": 0.25, \"T\": 0.25},\n]\n\nletters = [\"A\", \"C\", \"G\", \"T\"]\nn_positions = len(position_freqs)\nmax_bits = 2.0\n\n# Compute information content per position (Shannon entropy method)\ninfo_contents = []\nfor freqs in position_freqs:\n    entropy = sum(-f * np.log2(f) for f in freqs.values() if f > 0)\n    info_contents.append(max_bits - entropy)\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\nfp = FontProperties(family=\"DejaVu Sans\", weight=\"bold\")\nbar_width = 0.9\n\n# Highlight conserved core region (positions 3-6) using amber caution anchor\ncore_start, core_end = 3, 6\nhighlight = FancyBboxPatch(\n    (core_start - 0.48, -0.02),\n    core_end - core_start + 0.96,\n    max_bits + 0.04,\n    boxstyle=\"round,pad=0.02\",\n    facecolor=ANYPLOT_AMBER,\n    edgecolor=INK_MUTED,\n    alpha=0.18,\n    linewidth=0.8,\n    zorder=0,\n)\nax.add_patch(highlight)\n\nfor pos_idx, freqs in enumerate(position_freqs):\n    ic = info_contents[pos_idx]\n    letter_heights = {lt: freqs[lt] * ic for lt in letters}\n    sorted_letters = sorted(letters, key=lambda lt: letter_heights[lt])\n\n    y_offset = 0.0\n    x_start = pos_idx + 1 - bar_width / 2\n    for letter in sorted_letters:\n        h = letter_heights[letter]\n        if h < 0.01:\n            continue\n        tp = TextPath((0, 0), letter, size=1, prop=fp)\n        bbox = tp.get_extents()\n        if bbox.width == 0 or bbox.height == 0:\n            continue\n        sx = bar_width / bbox.width\n        sy = h / bbox.height\n        t = transforms.Affine2D().translate(-bbox.x0, -bbox.y0).scale(sx, sy).translate(x_start, y_offset)\n        patch = PathPatch(tp.transformed(t), facecolor=dna_colors[letter], edgecolor=\"none\", linewidth=0, zorder=2)\n        ax.add_patch(patch)\n        y_offset += h\n\n# Annotate conserved core region\nax.annotate(\n    \"Conserved core\",\n    xy=((core_start + core_end) / 2, max_bits * 0.92),\n    fontsize=8,\n    fontweight=\"medium\",\n    color=INK_MUTED,\n    ha=\"center\",\n    va=\"center\",\n    zorder=3,\n)\n\n# Nucleotide color legend\nlegend_handles = [\n    Line2D([0], [0], marker=\"s\", color=\"w\", markerfacecolor=dna_colors[lt], markersize=8, label=lt, linewidth=0)\n    for lt in letters\n]\nleg = ax.legend(\n    handles=legend_handles,\n    loc=\"upper right\",\n    fontsize=8,\n    framealpha=0.9,\n    edgecolor=INK_SOFT,\n    handletextpad=0.4,\n    labelspacing=0.3,\n)\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\n# Style\ntitle = \"sequence-logo-basic · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title))) if len(title) > 67 else 12\nax.set_xlim(0.5, n_positions + 0.5)\nax.set_ylim(0, max_bits)\nax.set_xticks(range(1, n_positions + 1))\nax.set_xticklabels(range(1, n_positions + 1))\nax.set_xlabel(\"Position\", fontsize=10, color=INK)\nax.set_ylabel(\"Information content (bits)\", fontsize=10, color=INK)\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK, zorder=0)\n\n# Save\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}