{"spec_id":"histogram-epidemic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nhistogram-epidemic: Epidemic Curve (Epi Curve)\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 84/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Imprint palette — theme-independent data colors\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Theme-adaptive chrome\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.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Data — two influenza-like illness seasons over 104 weeks (weekly bins)\n# Seasonal cycling pattern: distinct winter peaks, low endemic summer baseline\nnp.random.seed(17)\nweeks = pd.date_range(\"2022-09-05\", periods=104, freq=\"W-MON\")\n\nt = np.arange(104)\n# Season 1 peak: ~week 20 (mid-Jan 2023); Season 2 peak: ~week 72 (mid-Jan 2024)\nseason1 = 320 * np.exp(-0.5 * ((t - 20) / 7) ** 2)\nseason2 = 275 * np.exp(-0.5 * ((t - 72) / 8) ** 2)\nendemic = 35 + 12 * np.sin(2 * np.pi * t / 52 - np.pi / 2)\n\nbase_cases = np.maximum(0, season1 + season2 + endemic + np.random.poisson(8, 104))\n\nconfirmed = np.round(base_cases * 0.58).astype(int)\nprobable = np.round(base_cases * 0.30).astype(int)\nsuspect = np.round(base_cases * 0.12).astype(int)\ncumulative = np.cumsum(confirmed + probable + suspect)\n\n# Convert to strings for kaleido JSON serialization\nweeks_str = [d.strftime(\"%Y-%m-%d\") for d in weeks]\n\nfig = go.Figure()\n\nfig.add_trace(\n    go.Bar(\n        x=weeks_str,\n        y=confirmed,\n        name=\"Confirmed\",\n        marker={\"color\": IMPRINT_PALETTE[0], \"line\": {\"color\": PAGE_BG, \"width\": 0.4}},\n        hovertemplate=\"%{y} cases<extra></extra>\",\n    )\n)\n\nfig.add_trace(\n    go.Bar(\n        x=weeks_str,\n        y=probable,\n        name=\"Probable\",\n        marker={\"color\": IMPRINT_PALETTE[1], \"line\": {\"color\": PAGE_BG, \"width\": 0.4}},\n        hovertemplate=\"%{y} cases<extra></extra>\",\n    )\n)\n\nfig.add_trace(\n    go.Bar(\n        x=weeks_str,\n        y=suspect,\n        name=\"Suspect\",\n        marker={\"color\": IMPRINT_PALETTE[2], \"line\": {\"color\": PAGE_BG, \"width\": 0.4}},\n        hovertemplate=\"%{y} cases<extra></extra>\",\n    )\n)\n\n# Cumulative burden line on secondary y-axis\nfig.add_trace(\n    go.Scatter(\n        x=weeks_str,\n        y=cumulative,\n        name=\"Cumulative\",\n        yaxis=\"y2\",\n        mode=\"lines\",\n        line={\"color\": IMPRINT_PALETTE[3], \"width\": 2.5},\n        hovertemplate=\"%{y:,} total<extra></extra>\",\n    )\n)\n\n# Intervention annotation lines: vaccination campaign rollouts before each season peak\nfor evt_date, evt_label, evt_color in [\n    (weeks_str[13], \"Flu vaccine rollout\", IMPRINT_PALETTE[4]),  # Dec 2022, before Season 1 peak\n    (weeks_str[60], \"Flu vaccine rollout\", IMPRINT_PALETTE[5]),  # Oct 2023, before Season 2 peak\n]:\n    fig.add_shape(\n        type=\"line\",\n        x0=evt_date,\n        x1=evt_date,\n        y0=0,\n        y1=0.78,\n        yref=\"paper\",\n        line={\"color\": evt_color, \"width\": 2, \"dash\": \"dashdot\"},\n    )\n    fig.add_annotation(\n        x=evt_date,\n        y=0.85,\n        yref=\"paper\",\n        text=f\"<b>{evt_label}</b>\",\n        showarrow=True,\n        arrowhead=0,\n        arrowwidth=1.5,\n        arrowcolor=evt_color,\n        ax=0,\n        ay=-30,\n        font={\"size\": 10, \"color\": evt_color, \"family\": \"Arial\"},\n        align=\"center\",\n        bgcolor=ELEVATED_BG,\n        bordercolor=evt_color,\n        borderwidth=1,\n        borderpad=3,\n    )\n\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": \"histogram-epidemic · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"color\": INK, \"family\": \"Arial\"},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n        \"y\": 0.93,\n        \"yanchor\": \"top\",\n    },\n    hovermode=\"x unified\",\n    xaxis={\n        \"title\": {\"text\": \"Week of Symptom Onset\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickformat\": \"%b %Y\",\n        \"dtick\": \"M3\",\n        \"tickangle\": -30,\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": False,\n        \"spikemode\": \"across\",\n        \"spikethickness\": 1,\n        \"spikecolor\": INK_SOFT,\n        \"spikedash\": \"dot\",\n    },\n    yaxis={\n        \"title\": {\"text\": \"Weekly New Cases\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"zeroline\": False,\n        \"showline\": False,\n        \"rangemode\": \"tozero\",\n    },\n    yaxis2={\n        \"title\": {\"text\": \"Cumulative Cases\", \"font\": {\"size\": 12, \"color\": IMPRINT_PALETTE[3]}},\n        \"tickfont\": {\"size\": 10, \"color\": IMPRINT_PALETTE[3]},\n        \"overlaying\": \"y\",\n        \"side\": \"right\",\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"rangemode\": \"tozero\",\n    },\n    barmode=\"stack\",\n    bargap=0.15,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK, \"family\": \"Arial\"},\n    legend={\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"orientation\": \"h\",\n        \"traceorder\": \"normal\",\n        \"yanchor\": \"bottom\",\n        \"y\": 1.02,\n        \"xanchor\": \"center\",\n        \"x\": 0.5,\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n    },\n    margin={\"l\": 80, \"r\": 80, \"t\": 90, \"b\": 100},\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}