{"spec_id":"area-stacked-confidence","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\narea-stacked-confidence: Stacked Area Chart with Confidence Bands\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nfrom bokeh.io import output_file, save\nfrom bokeh.models import Band, ColumnDataSource, HoverTool, Legend\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\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 series is always #009E73\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Quarterly energy consumption by source with uncertainty\nnp.random.seed(42)\nquarters = pd.date_range(\"2020-01-01\", periods=24, freq=\"QE\")\nn = len(quarters)\n\n# Generate energy consumption data (GWh) with uncertainty\n# Solar - growing trend with increasing uncertainty\nsolar_base = 50 + np.linspace(0, 80, n) + np.random.normal(0, 5, n)\nsolar_lower = solar_base - (5 + np.linspace(0, 15, n))\nsolar_upper = solar_base + (5 + np.linspace(0, 15, n))\n\n# Wind - seasonal variation with moderate uncertainty\nwind_base = 80 + 20 * np.sin(np.linspace(0, 6 * np.pi, n)) + np.random.normal(0, 3, n)\nwind_lower = wind_base - 10\nwind_upper = wind_base + 10\n\n# Hydro - stable with low uncertainty\nhydro_base = 60 + np.random.normal(0, 2, n)\nhydro_lower = hydro_base - 5\nhydro_upper = hydro_base + 5\n\n# Stack the values for cumulative display\n# First series (Solar) starts at 0\nstack1_base = solar_base\nstack1_lower = solar_lower\nstack1_upper = solar_upper\n\n# Second series (Wind) stacks on top of Solar\nstack2_base = stack1_base + wind_base\nstack2_lower = stack1_base + wind_lower\nstack2_upper = stack1_base + wind_upper\n\n# Third series (Hydro) stacks on top of Wind\nstack3_base = stack2_base + hydro_base\nstack3_lower = stack2_base + hydro_lower\nstack3_upper = stack2_base + hydro_upper\n\n# Create figure with larger dimensions\np = figure(\n    width=4800,\n    height=2700,\n    title=\"area-stacked-confidence · Python · bokeh · anyplot.ai\",\n    x_axis_label=\"Quarter\",\n    y_axis_label=\"Energy Consumption (GWh)\",\n    x_axis_type=\"datetime\",\n)\n\n# Create data sources for each stacked area with bands\nsource_solar = ColumnDataSource(\n    data={\"x\": quarters, \"y\": stack1_base, \"y_lower\": stack1_lower, \"y_upper\": stack1_upper, \"base\": np.zeros(n)}\n)\n\nsource_wind = ColumnDataSource(\n    data={\"x\": quarters, \"y\": stack2_base, \"y_lower\": stack2_lower, \"y_upper\": stack2_upper, \"base\": stack1_base}\n)\n\nsource_hydro = ColumnDataSource(\n    data={\"x\": quarters, \"y\": stack3_base, \"y_lower\": stack3_lower, \"y_upper\": stack3_upper, \"base\": stack2_base}\n)\n\n# Plot confidence bands (back to front for proper layering)\nsolar_band = Band(\n    base=\"x\",\n    lower=\"y_lower\",\n    upper=\"y_upper\",\n    source=source_solar,\n    fill_alpha=0.2,\n    fill_color=IMPRINT[0],\n    line_color=IMPRINT[0],\n    line_alpha=0.3,\n)\np.add_layout(solar_band)\n\nwind_band = Band(\n    base=\"x\",\n    lower=\"y_lower\",\n    upper=\"y_upper\",\n    source=source_wind,\n    fill_alpha=0.2,\n    fill_color=IMPRINT[1],\n    line_color=IMPRINT[1],\n    line_alpha=0.3,\n)\np.add_layout(wind_band)\n\nhydro_band = Band(\n    base=\"x\",\n    lower=\"y_lower\",\n    upper=\"y_upper\",\n    source=source_hydro,\n    fill_alpha=0.2,\n    fill_color=IMPRINT[2],\n    line_color=IMPRINT[2],\n    line_alpha=0.3,\n)\np.add_layout(hydro_band)\n\n# Plot stacked areas using varea\nr_solar = p.varea(x=\"x\", y1=\"base\", y2=\"y\", source=source_solar, fill_color=IMPRINT[0], fill_alpha=0.7)\nr_wind = p.varea(x=\"x\", y1=\"base\", y2=\"y\", source=source_wind, fill_color=IMPRINT[1], fill_alpha=0.7)\nr_hydro = p.varea(x=\"x\", y1=\"base\", y2=\"y\", source=source_hydro, fill_color=IMPRINT[2], fill_alpha=0.7)\n\n# Add center lines for each series for better visibility\np.line(x=\"x\", y=\"y\", source=source_solar, line_color=IMPRINT[0], line_width=3, line_alpha=0.8)\np.line(x=\"x\", y=\"y\", source=source_wind, line_color=IMPRINT[1], line_width=3, line_alpha=0.8)\np.line(x=\"x\", y=\"y\", source=source_hydro, line_color=IMPRINT[2], line_width=3, line_alpha=0.8)\n\n# Create legend outside plot area (bottom right)\nlegend = Legend(\n    items=[\n        (\"Solar (± uncertainty)\", [r_solar]),\n        (\"Wind (± uncertainty)\", [r_wind]),\n        (\"Hydro (± uncertainty)\", [r_hydro]),\n    ],\n    location=\"bottom_right\",\n)\nlegend.click_policy = \"hide\"\np.add_layout(legend)\n\n# Add hover tool for interactivity\nhover = HoverTool(tooltips=[(\"Date\", \"@x{%F}\"), (\"Value\", \"@y{0,0.0}\")], formatters={\"@x\": \"datetime\"})\np.add_tools(hover)\n\n# Theme-adaptive styling\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\np.title.text_color = INK\np.title.text_font_size = \"28pt\"\n\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.axis_label_text_font_size = \"22pt\"\np.yaxis.axis_label_text_font_size = \"22pt\"\n\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.major_label_text_font_size = \"18pt\"\np.yaxis.major_label_text_font_size = \"18pt\"\n\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.10\np.ygrid.grid_line_alpha = 0.10\n\np.legend.background_fill_color = ELEVATED_BG\np.legend.border_line_color = INK_SOFT\np.legend.label_text_color = INK_SOFT\np.legend.label_text_font_size = \"16pt\"\n\n# Set y-axis to start at 0\np.y_range.start = 0\n\n# Add padding\np.min_border_left = 100\np.min_border_right = 100\np.min_border_top = 50\np.min_border_bottom = 100\n\n# Save output\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome\nW, H = 4800, 2700\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}