{"spec_id":"bar-grouped","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nbar-grouped: Grouped Bar Chart\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\nimport sys\n\n\n# Fix sys.path to avoid importing local matplotlib.py file\nif sys.path and sys.path[0] == os.path.dirname(__file__):\n    sys.path.pop(0)\n\nimport matplotlib.pyplot as plt\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, positions 1-3)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data: Customer satisfaction across departments and regions\n# North deliberately crosses the IT/HR ranking (HR > IT there) so the grouped\n# comparison isn't just a flat repeated ordering across all four regions.\ndata = {\n    \"Region\": [\"North\", \"North\", \"North\", \"South\", \"South\", \"South\", \"East\", \"East\", \"East\", \"West\", \"West\", \"West\"],\n    \"Department\": [\"IT\", \"HR\", \"Operations\"] * 4,\n    \"Score\": [78, 82, 75, 88, 84, 80, 85, 81, 79, 90, 86, 83],\n}\ndf = pd.DataFrame(data)\nregion_order = df.groupby(\"Region\")[\"Score\"].mean().sort_values(ascending=False).index\n\n# Set theme\nsns.set_theme(\n    style=\"ticks\",\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.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Create figure — 3200x1800 px canvas (Step 0 hard contract)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400)\n\n# Plot grouped bars, ordered by mean score and dodged with a seaborn 0.13 `gap`\n# for cleaner within-group separation than the matplotlib default\nsns.barplot(\n    data=df,\n    x=\"Region\",\n    y=\"Score\",\n    hue=\"Department\",\n    order=region_order,\n    palette=IMPRINT,\n    ax=ax,\n    edgecolor=\"white\",\n    linewidth=1,\n    gap=0.1,\n)\n\n# Value labels for precise comparisons\nfor container in ax.containers:\n    ax.bar_label(container, fontsize=8, color=INK_SOFT, padding=2, fmt=\"%.0f\")\n\n# Focal-point emphasis on the top-scoring region (West, first in region_order)\ntop_x = 0\nax.axvspan(top_x - 0.5, top_x + 0.5, color=IMPRINT[0], alpha=0.05, zorder=0)\nax.annotate(\n    \"Top performer\",\n    xy=(top_x, 92),\n    xytext=(top_x, 97),\n    ha=\"center\",\n    fontsize=8,\n    color=INK_SOFT,\n    style=\"italic\",\n    arrowprops={\"arrowstyle\": \"-\", \"color\": INK_SOFT, \"lw\": 0.8},\n)\n\n# Styling\nax.set_xlabel(\"Region\", fontsize=10, color=INK)\nax.set_ylabel(\"Satisfaction Score (0-100)\", fontsize=10, color=INK)\nax.set_title(\"bar-grouped · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nax.set_ylim(0, 100)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\n\n# Legend - moved below the plot with seaborn's move_legend helper, which\n# repositions the auto-generated hue legend without rebuilding it manually\nsns.move_legend(\n    ax,\n    \"upper center\",\n    bbox_to_anchor=(0.5, -0.14),\n    ncol=3,\n    title=\"Department\",\n    fontsize=8,\n    title_fontsize=8,\n    frameon=True,\n    edgecolor=INK_SOFT,\n)\n\n# Remove top and right spines, idiomatic seaborn cleanup\nsns.despine(ax=ax)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}