{"spec_id":"timeseries-forecast-uncertainty","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ntimeseries-forecast-uncertainty: Time Series Forecast with Uncertainty Band\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\n\n\n# Theme and chrome colors\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\"\nGRID = \"rgba(26, 26, 23, 0.10)\" if THEME == \"light\" else \"rgba(240, 239, 232, 0.10)\"\n\n# Okabe-Ito palette\nCOLOR_HISTORICAL = \"#009E73\"  # Position 1 - brand green\nCOLOR_FORECAST = \"#C475FD\"  # Position 2 - vermillion\n\n# Data generation\nnp.random.seed(42)\nn_historical = 36\nn_forecast = 12\ndates_hist_range = pd.date_range(\"2023-01-01\", periods=n_historical, freq=\"MS\")\ndates_historical = dates_hist_range.strftime(\"%Y-%m-%d\").tolist()\ndates_forecast_range = pd.date_range(dates_hist_range[-1] + pd.DateOffset(months=1), periods=n_forecast, freq=\"MS\")\ndates_forecast = dates_forecast_range.strftime(\"%Y-%m-%d\").tolist()\n\n# Historical data: trend + seasonality + noise\ntime_idx = np.arange(n_historical)\ntrend = 100 + 2.5 * time_idx\nseasonality = 15 * np.sin(2 * np.pi * time_idx / 12)\nnoise = np.random.normal(0, 8, n_historical)\nactual = trend + seasonality + noise\n\n# Forecast values\nforecast_time_idx = np.arange(n_historical, n_historical + n_forecast)\nforecast_trend = 100 + 2.5 * forecast_time_idx\nforecast_seasonality = 15 * np.sin(2 * np.pi * forecast_time_idx / 12)\nforecast_values = forecast_trend + forecast_seasonality\n\n# Confidence intervals (widening over time)\nuncertainty_base = 10\nuncertainty_growth = np.sqrt(np.arange(1, n_forecast + 1)) * 5\nlower_80 = forecast_values - (uncertainty_base + uncertainty_growth * 0.8)\nupper_80 = forecast_values + (uncertainty_base + uncertainty_growth * 0.8)\nlower_95 = forecast_values - (uncertainty_base + uncertainty_growth * 1.3)\nupper_95 = forecast_values + (uncertainty_base + uncertainty_growth * 1.3)\n\n# Create figure\nfig = go.Figure()\n\n# 95% confidence band (lighter)\nfig.add_trace(\n    go.Scatter(\n        x=dates_forecast + dates_forecast[::-1],\n        y=np.concatenate([upper_95, lower_95[::-1]]),\n        fill=\"toself\",\n        fillcolor=\"rgba(196, 117, 253, 0.10)\",\n        line=dict(color=\"rgba(196, 117, 253, 0)\"),\n        name=\"95% CI\",\n        showlegend=True,\n        hoverinfo=\"skip\",\n    )\n)\n\n# 80% confidence band (darker)\nfig.add_trace(\n    go.Scatter(\n        x=dates_forecast + dates_forecast[::-1],\n        y=np.concatenate([upper_80, lower_80[::-1]]),\n        fill=\"toself\",\n        fillcolor=\"rgba(196, 117, 253, 0.32)\",\n        line=dict(color=\"rgba(196, 117, 253, 0)\"),\n        name=\"80% CI\",\n        showlegend=True,\n        hoverinfo=\"skip\",\n    )\n)\n\n# Forecast start marker\nforecast_start = dates_forecast[0]\nfig.add_shape(\n    type=\"line\",\n    x0=forecast_start,\n    x1=forecast_start,\n    y0=0,\n    y1=1,\n    yref=\"paper\",\n    line=dict(color=INK_SOFT, width=2, dash=\"dash\"),\n)\n\n# Forecast start annotation\nfig.add_annotation(\n    x=forecast_start, y=1.02, yref=\"paper\", text=\"Forecast Start\", showarrow=False, font=dict(size=16, color=INK_SOFT)\n)\n\n# Historical data (solid line)\nfig.add_trace(\n    go.Scatter(\n        x=dates_historical,\n        y=actual,\n        mode=\"lines\",\n        name=\"Historical\",\n        line=dict(color=COLOR_HISTORICAL, width=3),\n        hovertemplate=\"Date: %{x}<br>Sales: %{y:.1f}<extra></extra>\",\n    )\n)\n\n# Forecast line (dashed)\nfig.add_trace(\n    go.Scatter(\n        x=dates_forecast,\n        y=forecast_values,\n        mode=\"lines\",\n        name=\"Forecast\",\n        line=dict(color=COLOR_FORECAST, width=3, dash=\"dash\"),\n        hovertemplate=\"Date: %{x}<br>Forecast: %{y:.1f}<extra></extra>\",\n    )\n)\n\n# Connection line between historical and forecast\nfig.add_trace(\n    go.Scatter(\n        x=[dates_historical[-1], dates_forecast[0]],\n        y=[actual[-1], forecast_values[0]],\n        mode=\"lines\",\n        line=dict(color=COLOR_HISTORICAL, width=2, dash=\"dot\"),\n        showlegend=False,\n        hoverinfo=\"skip\",\n    )\n)\n\n# Layout with theme-adaptive colors and chrome\nfig.update_layout(\n    title=dict(\n        text=\"timeseries-forecast-uncertainty · python · plotly · anyplot.ai\",\n        font=dict(size=28, color=INK),\n        x=0.5,\n        xanchor=\"center\",\n    ),\n    xaxis=dict(\n        title=dict(text=\"Date\", font=dict(size=22, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=False,\n        showline=True,\n        mirror=False,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Monthly Sales (Units)\", font=dict(size=22, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=True,\n        gridcolor=GRID,\n        showline=True,\n        mirror=False,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    legend=dict(\n        font=dict(size=18, color=INK_SOFT),\n        x=0.02,\n        y=0.98,\n        xanchor=\"left\",\n        yanchor=\"top\",\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n    ),\n    margin=dict(l=100, r=60, t=100, b=100),\n    hovermode=\"x unified\",\n)\n\n# Save outputs\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}