{ "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.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }