{"spec_id":"donut-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ndonut-basic: Basic Donut Chart\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-06-25\n\"\"\"\n\nimport os\nimport sys\n\n\n# matplotlib.py in this directory (sibling impl) shadows the real package — remove it from path.\n_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path[:] = [p for p in sys.path if os.path.normpath(p or \".\") != os.path.normpath(_dir)]\n\nimport matplotlib.patches as mpatches\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# Imprint palette — canonical order, first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Data — departmental budget allocation (ordered largest → smallest for readability)\nbudget = pd.DataFrame(\n    {\n        \"department\": [\"Engineering\", \"Sales\", \"Operations\", \"Marketing\", \"R&D\", \"HR\"],\n        \"amount\": [520_000, 310_000, 240_000, 150_000, 95_000, 45_000],\n    }\n)\ntotal = budget[\"amount\"].sum()\nbudget[\"share\"] = budget[\"amount\"] / total * 100\n\n# Theme — apply Imprint palette and background tokens via seaborn; set_context scales text proportionally\nsns.set_theme(style=\"white\", rc={\"figure.facecolor\": PAGE_BG, \"axes.facecolor\": PAGE_BG, \"text.color\": INK})\nsns.set_context(\"notebook\", font_scale=0.85)\nwedge_colors = sns.color_palette(IMPRINT_PALETTE, n_colors=len(budget)).as_hex()\n\n# Plot — square canvas 2400×2400 (figsize=(6, 6) @ dpi=400)\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Engineering segment emphasized with clearly visible explode offset\nexplode = [0.07 if i == 0 else 0.0 for i in range(len(budget))]\n\nwedges, _ = ax.pie(\n    budget[\"amount\"],\n    colors=wedge_colors,\n    startangle=90,\n    counterclock=False,\n    explode=explode,\n    wedgeprops={\"width\": 0.38, \"edgecolor\": PAGE_BG, \"linewidth\": 2},\n)\n\n# Center card — subtle filled circle contains the metric and provides visual elevation\ncenter_circle = mpatches.Circle((0, 0), radius=0.52, facecolor=ELEVATED_BG, edgecolor=INK_SOFT, linewidth=0.5, zorder=2)\nax.add_patch(center_circle)\n\n# External labels — staggered radii prevent crowding for small segments\nfor wedge, dept, share in zip(wedges, budget[\"department\"], budget[\"share\"], strict=True):\n    angle = (wedge.theta2 + wedge.theta1) / 2\n    angle_rad = np.deg2rad(angle)\n    cos_a, sin_a = np.cos(angle_rad), np.sin(angle_rad)\n    ha = \"left\" if cos_a >= 0 else \"right\"\n\n    if share < 5:\n        # Tiny segment: leader line + label at larger radius to clear adjacent text\n        x_outer, y_outer = 1.03 * cos_a, 1.03 * sin_a\n        r_label = 1.42\n        x_label, y_label = r_label * cos_a, r_label * sin_a\n        ax.plot([x_outer, x_label], [y_outer, y_label], color=INK_SOFT, lw=0.8, solid_capstyle=\"round\")\n        offset = 0.04 if ha == \"left\" else -0.04\n        ax.text(\n            x_label + offset,\n            y_label,\n            f\"{dept}\\n{share:.1f}%\",\n            ha=ha,\n            va=\"center\",\n            fontsize=8,\n            color=INK,\n            linespacing=1.3,\n        )\n    elif share < 10:\n        radius = 1.28\n        ax.text(\n            radius * cos_a,\n            radius * sin_a,\n            f\"{dept}\\n{share:.1f}%\",\n            ha=ha,\n            va=\"center\",\n            fontsize=9,\n            color=INK,\n            linespacing=1.3,\n        )\n    else:\n        radius = 1.16\n        ax.text(\n            radius * cos_a,\n            radius * sin_a,\n            f\"{dept}\\n{share:.1f}%\",\n            ha=ha,\n            va=\"center\",\n            fontsize=10,\n            color=INK,\n            linespacing=1.3,\n        )\n\n# Center metric: label, separator, bold total, and narrative note (all zorder=3 to appear above circle)\nax.text(0, 0.22, \"Total Budget\", ha=\"center\", va=\"center\", fontsize=8, color=INK_SOFT, zorder=3)\nax.plot([-0.10, 0.10], [0.11, 0.11], color=INK_SOFT, linewidth=1.0, solid_capstyle=\"round\", zorder=3)\nax.text(\n    0,\n    -0.02,\n    f\"${total / 1_000_000:.2f}M\",\n    ha=\"center\",\n    va=\"center\",\n    fontsize=20,\n    fontweight=\"bold\",\n    color=INK,\n    zorder=3,\n)\n# Narrative callout: top-2 segments dominate budget\ntop2_pct = budget[\"share\"].iloc[:2].sum()\nax.text(\n    0,\n    -0.27,\n    f\"Eng + Sales: {top2_pct:.0f}%\",\n    ha=\"center\",\n    va=\"center\",\n    fontsize=7,\n    color=INK_SOFT,\n    style=\"italic\",\n    zorder=3,\n)\n\n# Title — descriptive prefix adds context; n>67 chars so fontsize scaled down\ntitle = \"FY2026 Budget Allocation · donut-basic · python · seaborn · anyplot.ai\"\nn = len(title)\ntitle_fontsize = max(8, round(12 * 67 / n)) if n > 67 else 12\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, pad=16)\n\nax.set_aspect(\"equal\")\nax.set_xlim(-1.55, 1.55)\nax.set_ylim(-1.55, 1.55)\nsns.despine(ax=ax, top=True, bottom=True, left=True, right=True)\nax.set_axis_off()\n\nfig.subplots_adjust(top=0.88, bottom=0.06, left=0.05, right=0.95)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}