{
"cells": [
{
"cell_type": "markdown",
"id": "231c773a",
"metadata": {},
"source": [
"# 01 — Benefits Analysis\n",
"\n",
"**Study:** Forrester *Total Economic Impact™ Of Amazon Connect* (Feb 2026)\n",
"\n",
"Quantify the five benefit categories Forrester identified for the\n",
"composite organization, push them into Athena, and verify the totals\n",
"match the published study (Benefits PV ≈ **$101.7M**)."
]
},
{
"cell_type": "markdown",
"id": "110d7e61",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"We add the project root to `sys.path` so the notebook can import `core` and\n",
"the study's local modules without `pip install -e .`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c83c2758",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Project root: /home/robert/notebook/git/palladium\n",
"Study root: /home/robert/notebook/git/palladium/studies/202602_AmazonConnect\n"
]
}
],
"source": [
"import sys\n",
"from pathlib import Path\n",
"\n",
"ROOT = Path.cwd().resolve()\n",
"while ROOT != ROOT.parent and not (ROOT / 'core').is_dir():\n",
" ROOT = ROOT.parent\n",
"if str(ROOT) not in sys.path:\n",
" sys.path.insert(0, str(ROOT))\n",
"\n",
"STUDY = ROOT / 'studies' / '202602_AmazonConnect'\n",
"if str(STUDY) not in sys.path:\n",
" sys.path.insert(0, str(STUDY))\n",
"print(f'Project root: {ROOT}')\n",
"print(f'Study root: {STUDY}')"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c371ef85",
"metadata": {},
"outputs": [
{
"ename": "ModuleNotFoundError",
"evalue": "No module named 'pandas'",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m config\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m seed_data\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m core.calculations \u001b[38;5;28;01mimport\u001b[39;00m npv, risk_adjust_benefit\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m core.notebook_helpers \u001b[38;5;28;01mimport\u001b[39;00m charts, display, tables\n\u001b[32m 5\u001b[39m \n\u001b[32m 6\u001b[39m display.alert(\n\u001b[32m 7\u001b[39m f'Study: {config.STUDY_SLUG} • discount rate {config.DISCOUNT_RATE:.0%} '\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/notebook/git/palladium/core/notebook_helpers/__init__.py:3\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[33;03m\"\"\"Notebook helpers — pandas tables, plotly charts, IPython display.\"\"\"\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mcore\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mnotebook_helpers\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m charts, display, tables\n\u001b[32m 5\u001b[39m __all__ = [\u001b[33m\"\u001b[39m\u001b[33mcharts\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mdisplay\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mtables\u001b[39m\u001b[33m\"\u001b[39m]\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/notebook/git/palladium/core/notebook_helpers/tables.py:13\u001b[39m\n\u001b[32m 9\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01m__future__\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m annotations\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mtyping\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Any, Iterable\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpandas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpd\u001b[39;00m\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mcore\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mcalculations\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m risk_adjust_benefit, risk_adjust_cost\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_years_in_data\u001b[39m(items: Iterable[\u001b[38;5;28mdict\u001b[39m]) -> \u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mint\u001b[39m]:\n",
"\u001b[31mModuleNotFoundError\u001b[39m: No module named 'pandas'"
]
}
],
"source": [
"import config\n",
"import seed_data\n",
"from core.calculations import npv, risk_adjust_benefit\n",
"from core.notebook_helpers import charts, display, tables\n",
"\n",
"display.alert(\n",
" f'Study: {config.STUDY_SLUG} • discount rate {config.DISCOUNT_RATE:.0%} '\n",
" f'• {config.ANALYSIS_YEARS}-year horizon',\n",
" 'info',\n",
")"
]
},
{
"cell_type": "markdown",
"id": "fd94503d",
"metadata": {},
"source": [
"## Benefits — nominal & risk-adjusted\n",
"\n",
"Forrester quantifies five benefit categories:\n",
"\n",
"| Ref | Benefit | Y1 | Y2 | Y3 | Risk Adj |\n",
"|---|---|---|---|---|---|\n",
"| At | AI-driven contact resolution efficiency | $13.9M | $23.9M | $37.8M | 15% |\n",
"| Bt | AI-powered content & sentiment analysis | $4.6M | $5.4M | $6.3M | 15% |\n",
"| Ct | AI-enabled forecasting & supervision | $6.7M | $9.1M | $12.4M | 15% |\n",
"| Dt | Data-driven profit lift (conversion +20%) | $1.2M | $1.6M | $2.0M | 20% |\n",
"| Et | Legacy solution cost savings | $6.2M | $8.0M | $10.4M | 20% |\n",
"\n",
"All five are seeded in `seed_data.BENEFITS` with full source notes."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6177ea7c",
"metadata": {},
"outputs": [],
"source": [
"df = tables.benefits_table(seed_data.BENEFITS)\n",
"df.style.format({col: '${:,.0f}' for col in df.columns if col not in ('field_key','label','category','risk_adjustment')})"
]
},
{
"cell_type": "markdown",
"id": "573f12d8",
"metadata": {},
"source": [
"## Local validation against the PDF\n",
"\n",
"Re-derive the per-benefit risk-adjusted PV and confirm we land on Forrester's\n",
"**$101,696,791** total within rounding."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8cf32003",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"rows = []\n",
"for b in seed_data.BENEFITS:\n",
" rf = b['risk_adjustment']\n",
" yr = [b['year_values'][str(y)] for y in (1, 2, 3)]\n",
" yr_ra = [risk_adjust_benefit(v, rf) for v in yr]\n",
" pv = npv(yr_ra, config.DISCOUNT_RATE)\n",
" rows.append({\n",
" 'Benefit': b['label'],\n",
" 'Y1 (RA)': yr_ra[0],\n",
" 'Y2 (RA)': yr_ra[1],\n",
" 'Y3 (RA)': yr_ra[2],\n",
" 'PV': pv,\n",
" })\n",
"df_check = pd.DataFrame(rows)\n",
"df_check.loc[len(df_check)] = ['TOTAL', df_check['Y1 (RA)'].sum(), df_check['Y2 (RA)'].sum(), df_check['Y3 (RA)'].sum(), df_check['PV'].sum()]\n",
"df_check.style.format({c: '${:,.0f}' for c in df_check.columns if c != 'Benefit'})"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ded50c8",
"metadata": {},
"outputs": [],
"source": [
"expected_pv = 101_696_791\n",
"computed_pv = df_check.iloc[-1]['PV']\n",
"delta = computed_pv - expected_pv\n",
"kind = 'success' if abs(delta) < 1_000 else 'warning'\n",
"display.alert(\n",
" f'Computed Benefits PV: ${computed_pv:,.0f}
'\n",
" f'Forrester target: ${expected_pv:,.0f}
'\n",
" f'Δ = ${delta:,.0f} (rounding)',\n",
" kind,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a5ad453a",
"metadata": {},
"source": [
"## Visualize\n",
"\n",
"Horizontal bar chart of risk-adjusted three-year totals — mirrors the PDF p.6\n",
"*Benefits (Three-Year)* graphic."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "452b8408",
"metadata": {},
"outputs": [],
"source": [
"charts.benefits_bar(seed_data.BENEFITS).show()"
]
},
{
"cell_type": "markdown",
"id": "1c4591f5",
"metadata": {},
"source": [
"## Push to Athena\n",
"\n",
"When `config.TOOL_PUBLIC_ID` is set, persist the seed values to the live\n",
"TEI tool. Otherwise this cell is a no-op so the notebook still runs\n",
"offline."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d10a54b6",
"metadata": {},
"outputs": [],
"source": [
"if config.TOOL_PUBLIC_ID:\n",
" from core.tei_client import TEIClient\n",
"\n",
" client = TEIClient()\n",
" result = client.update_values(config.TOOL_PUBLIC_ID, seed_data.BENEFITS)\n",
" display.alert(f'Pushed {len(seed_data.BENEFITS)} benefit rows to '\n",
" f'tool {config.TOOL_PUBLIC_ID}.', 'success')\n",
"else:\n",
" display.alert(\n",
" 'No TOOL_PUBLIC_ID set in config.py — skipped Athena push. '\n",
" 'Set PALLADIUM_TOOL_PUBLIC_ID in your environment '\n",
" 'or edit config.py to enable.',\n",
" 'info',\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "78693c14",
"metadata": {},
"source": [
"---\n",
"\n",
"Continue with [`02_costs.ipynb`](02_costs.ipynb) →"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}