{"spec_id":"box-grouped","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nbox-grouped: Grouped Box Plot\nLibrary: seaborn 0.13.2 | Python 3.13.15\nQuality: 91/100 | Updated: 2026-08-18\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\"\n\n# Imprint palette - first series always #009E73\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data - Temperature distributions by region and season\nnp.random.seed(42)\n\nregions = [\"North\", \"South\", \"East\", \"West\"]\nseasons = [\"Winter\", \"Spring\", \"Summer\", \"Fall\"]\n\ndata = []\nfor region in regions:\n    for season in seasons:\n        n = np.random.randint(35, 50)\n        # Different temperature distributions per region/season\n        season_base = {\"Winter\": 5, \"Spring\": 15, \"Summer\": 28, \"Fall\": 18}[season]\n        region_offset = {\"North\": -3, \"South\": 2, \"East\": 0, \"West\": 1}[region]\n        base = season_base + region_offset\n\n        season_spread = {\"Winter\": 4, \"Spring\": 5, \"Summer\": 6, \"Fall\": 5}[season]\n        values = np.random.normal(base, season_spread, n)\n        # Add realistic outliers (unusual temperature days)\n        if np.random.random() > 0.6:\n            values = np.append(values, base + season_spread * np.random.choice([-2.5, 2.5], size=1))\n        values = np.clip(values, -10, 40)\n\n        for v in values:\n            data.append({\"Region\": region, \"Season\": season, \"Temperature (°C)\": v})\n\ndf = pd.DataFrame(data)\n\n# Plot setup\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.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400)\n\n# Create grouped box plot, mean diamonds add a distinctive second statistic\n# beyond the median line without cluttering the chart\nsns.boxplot(\n    data=df,\n    x=\"Region\",\n    y=\"Temperature (°C)\",\n    hue=\"Season\",\n    palette=IMPRINT,\n    ax=ax,\n    width=0.7,\n    linewidth=1.5,\n    fliersize=4,\n    order=regions,\n    hue_order=seasons,\n    showmeans=True,\n    meanprops={\"marker\": \"D\", \"markerfacecolor\": INK, \"markeredgecolor\": PAGE_BG, \"markersize\": 5},\n)\n\n# Styling\nax.set_xlabel(\"Region\", fontsize=10, color=INK)\nax.set_ylabel(\"Temperature (°C)\", fontsize=10, color=INK)\nax.set_title(\"box-grouped · python · seaborn · anyplot.ai\", fontsize=12, color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\n# Legend placed outside the axes so it never overlaps the West/Summer boxes\nax.legend(title=\"Season\", fontsize=8, title_fontsize=8, loc=\"upper left\", bbox_to_anchor=(1.01, 1.0), borderaxespad=0)\nax.set_ylim(-12, 42)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8)\nax.xaxis.grid(False)\n\n# Trim spines away from the data range for a cleaner, less boxed-in frame\nsns.despine(ax=ax, offset=6)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\nfig.subplots_adjust(left=0.09, right=0.85, top=0.90, bottom=0.15)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}