{"spec_id":"area-stacked-confidence","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\narea-stacked-confidence: Stacked Area Chart with Confidence Bands\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 99/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_ribbon,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_fill_manual,\n    scale_x_continuous,\n    theme,\n    theme_minimal,\n)\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# Okabe-Ito palette (first three series)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Quarterly energy consumption forecast by source with prediction intervals\nnp.random.seed(42)\n\nquarters = np.arange(1, 21)  # 20 quarters (5 years)\n\n# Base values for three energy sources (in TWh)\nsolar_base = 50 + quarters * 3 + np.random.randn(20) * 2\nwind_base = 80 + quarters * 2.5 + np.random.randn(20) * 3\nhydro_base = 120 + quarters * 0.5 + np.random.randn(20) * 2\n\n# Confidence intervals (uncertainty grows over time for forecasts)\nuncertainty_factor = 1 + quarters * 0.08\n\nsolar_lower = solar_base - 8 * uncertainty_factor\nsolar_upper = solar_base + 8 * uncertainty_factor\n\nwind_lower = wind_base - 10 * uncertainty_factor\nwind_upper = wind_base + 10 * uncertainty_factor\n\nhydro_lower = hydro_base - 6 * uncertainty_factor\nhydro_upper = hydro_base + 6 * uncertainty_factor\n\n# Create stacked values (cumulative)\n# Layer 1: Solar (bottom)\nsolar_cum = solar_base\nsolar_cum_lower = solar_lower\nsolar_cum_upper = solar_upper\n\n# Layer 2: Wind (stacked on solar)\nwind_cum = solar_base + wind_base\nwind_cum_lower = solar_base + wind_lower\nwind_cum_upper = solar_base + wind_upper\n\n# Layer 3: Hydro (stacked on wind + solar)\nhydro_cum = solar_base + wind_base + hydro_base\nhydro_cum_lower = solar_base + wind_base + hydro_lower\nhydro_cum_upper = solar_base + wind_base + hydro_upper\n\n# Create long-form dataframe for proper legend support\ndf_areas = pd.concat(\n    [\n        pd.DataFrame(\n            {\n                \"quarter\": quarters,\n                \"y\": solar_cum,\n                \"ymin\": np.zeros(20),\n                \"ymax\": solar_cum,\n                \"lower\": solar_cum_lower,\n                \"upper\": solar_cum_upper,\n                \"series\": \"Solar\",\n            }\n        ),\n        pd.DataFrame(\n            {\n                \"quarter\": quarters,\n                \"y\": wind_cum,\n                \"ymin\": solar_cum,\n                \"ymax\": wind_cum,\n                \"lower\": wind_cum_lower,\n                \"upper\": wind_cum_upper,\n                \"series\": \"Wind\",\n            }\n        ),\n        pd.DataFrame(\n            {\n                \"quarter\": quarters,\n                \"y\": hydro_cum,\n                \"ymin\": wind_cum,\n                \"ymax\": hydro_cum,\n                \"lower\": hydro_cum_lower,\n                \"upper\": hydro_cum_upper,\n                \"series\": \"Hydro\",\n            }\n        ),\n    ],\n    ignore_index=True,\n)\n\n# Set order for legend\ndf_areas[\"series\"] = pd.Categorical(df_areas[\"series\"], categories=[\"Solar\", \"Wind\", \"Hydro\"], ordered=True)\n\n# Color mapping using Okabe-Ito palette\ncolor_map = {\"Solar\": IMPRINT[0], \"Wind\": IMPRINT[1], \"Hydro\": IMPRINT[2]}\n\n# Theme customization\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_blank(),\n    panel_border=element_rect(color=INK_SOFT, fill=None, size=0.5),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(color=INK, size=24, weight=\"medium\"),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK, size=18),\n    figure_size=(16, 9),\n)\n\n# Create the plot with stacked areas and confidence bands\nplot = (\n    ggplot(df_areas, aes(x=\"quarter\"))\n    # Confidence bands (lighter, drawn first)\n    + geom_ribbon(aes(ymin=\"lower\", ymax=\"upper\", fill=\"series\"), alpha=0.25)\n    # Stacked areas (main fill)\n    + geom_ribbon(aes(ymin=\"ymin\", ymax=\"ymax\", fill=\"series\"), alpha=0.7)\n    # Central lines for each series\n    + geom_line(aes(y=\"y\", color=\"series\"), size=1.5)\n    # Color scales\n    + scale_fill_manual(values=color_map, name=\"Energy Source\\n(with 90% CI)\")\n    + scale_color_manual(values=color_map, guide=None)\n    # Labels and styling\n    + labs(x=\"Quarter\", y=\"Energy Consumption (TWh)\", title=\"area-stacked-confidence · Python · plotnine · anyplot.ai\")\n    + scale_x_continuous(breaks=range(1, 21, 2))\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Save the plot\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}