{"spec_id":"lollipop-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nlollipop-basic: Basic Lollipop Chart\nLibrary: matplotlib 3.11.0 | Python 3.13.14\nQuality: 87/100 | Updated: 2026-07-01\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\n\n\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 palette — first series always brand green\nBRAND_GREEN = \"#009E73\"\n\n# Data: retail category sales (thousands)\ncategories = [\n    \"Electronics\",\n    \"Clothing\",\n    \"Home & Garden\",\n    \"Sports\",\n    \"Books\",\n    \"Toys\",\n    \"Beauty\",\n    \"Automotive\",\n    \"Food & Beverages\",\n    \"Office Supplies\",\n]\nvalues = [87, 72, 65, 58, 52, 45, 41, 38, 32, 25]\n\n# Sort descending for clear ranking story\norder = np.argsort(values)[::-1]\ncategories = [categories[i] for i in order]\nvalues = np.array([values[i] for i in order])\n\navg_val = values.mean()\n\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\nx = np.arange(len(categories))\n\n# Stems\nax.vlines(x, ymin=0, ymax=values, color=BRAND_GREEN, linewidth=2.0, zorder=2)\n\n# Markers — slightly larger for the top performer to create focal point\nmarker_sizes = np.where(x == 0, 120, 80)\nax.scatter(x, values, color=BRAND_GREEN, s=marker_sizes, zorder=3, edgecolors=PAGE_BG, linewidths=1.0)\n\n# Value labels above each marker\nfor xi, v in zip(x, values, strict=False):\n    ax.text(xi, v + 2.5, f\"{v}K\", ha=\"center\", va=\"bottom\", fontsize=8, color=INK_SOFT, fontweight=\"medium\")\n\n# Average reference line — structural anchor for context\nax.axhline(avg_val, color=INK_MUTED, linewidth=0.9, linestyle=\"--\", zorder=1)\nax.text(len(x) - 0.5, avg_val + 1.5, f\"avg {avg_val:.0f}K\", ha=\"right\", va=\"bottom\", fontsize=7.5, color=INK_MUTED)\n\n# Callout annotation on the top performer using matplotlib's annotation API\nax.annotate(\n    \"Top performer\",\n    xy=(x[0], values[0]),\n    xytext=(x[0] + 0.9, values[0] + 13),\n    fontsize=6.5,\n    color=INK,\n    ha=\"center\",\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_MUTED, \"lw\": 0.8},\n    bbox={\"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"boxstyle\": \"round,pad=0.3\", \"linewidth\": 0.6},\n)\n\ntitle = \"lollipop-basic · python · matplotlib · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", color=INK, pad=8)\nax.set_xlabel(\"Product Category\", fontsize=10, color=INK)\nax.set_ylabel(\"Sales (thousands)\", fontsize=10, color=INK)\n\nax.set_xticks(x)\nax.set_xticklabels(categories, rotation=45, ha=\"right\", fontsize=8)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\n\nax.set_ylim(0, max(values) * 1.38)\n\n# Tidy y-axis numeric labels via FuncFormatter\nax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda v, _: f\"{v:.0f}\"))\n\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)\n\nax.yaxis.grid(True, alpha=0.15, color=INK, linewidth=0.8)\nax.set_axisbelow(True)\n\n# Manual margins — bbox_inches must stay None (default) to preserve exact canvas size\nfig.subplots_adjust(left=0.10, right=0.97, top=0.91, bottom=0.26)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}