{"spec_id":"lollipop-grouped","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nlollipop-grouped: Grouped Lollipop Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 81/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\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\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Apply seaborn theme with theme-adaptive colors\nsns.set_theme(\n    style=\"whitegrid\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data - Quarterly revenue by product line across regions\nnp.random.seed(42)\ncategories = [\"North\", \"South\", \"East\", \"West\"]\nseries = [\"Electronics\", \"Clothing\", \"Food\"]\nn_categories = len(categories)\nn_series = len(series)\n\n# Generate realistic revenue data with wider range (in millions)\ndata = []\nbase_values = {\"Electronics\": 50, \"Clothing\": 35, \"Food\": 25}\nfor cat in categories:\n    for s in series:\n        # Wider range to improve data quality\n        value = base_values[s] + np.random.uniform(-15, 25)\n        data.append({\"Region\": cat, \"series\": s, \"Revenue (M$)\": value})\n\ndf = pd.DataFrame(data)\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9))\n\n# Okabe-Ito palette (positions 1, 2, 3) - first series is always #009E73\ncolors = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Calculate positions for grouped lollipops\nx = np.arange(n_categories)\nwidth = 0.25\noffsets = np.linspace(-width * (n_series - 1) / 2, width * (n_series - 1) / 2, n_series)\n\n# Plot lollipops for each series using seaborn styling + manual positioning\nfor i, (s, color) in enumerate(zip(series, colors, strict=True)):\n    series_data = df[df[\"series\"] == s]\n    positions = x + offsets[i]\n    values = series_data[\"Revenue (M$)\"].values\n\n    # Draw stems (vertical lines from 0 to value)\n    for pos, val in zip(positions, values, strict=True):\n        ax.plot([pos, pos], [0, val], color=color, linewidth=2.5, zorder=1)\n\n    # Draw markers at the top with white edges for definition\n    ax.scatter(positions, values, s=280, color=color, zorder=2, label=s, edgecolors=\"white\", linewidths=1.5)\n\n# Customize axes\nax.set_xticks(x)\nax.set_xticklabels(categories)\nax.set_xlabel(\"Region\", fontsize=20, color=INK)\nax.set_ylabel(\"Revenue (M$)\", fontsize=20, color=INK)\nax.set_title(\"lollipop-grouped · seaborn · pyplots.ai\", fontsize=24, color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Set y-axis to start from 0\nax.set_ylim(0, df[\"Revenue (M$)\"].max() * 1.15)\n\n# Grid styling (y-axis only, subtle)\nax.grid(True, axis=\"y\", alpha=0.10, linestyle=\"-\", linewidth=0.8)\nax.set_axisbelow(True)\n\n# Spine styling\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Legend styling\nlegend = ax.legend(title=\"series\", fontsize=14, title_fontsize=16, loc=\"upper right\", framealpha=0.95)\nlegend.get_frame().set_facecolor(ELEVATED_BG)\nlegend.get_frame().set_edgecolor(INK_SOFT)\nif legend.get_title():\n    legend.get_title().set_color(INK)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}