{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-000",
   "metadata": {},
   "source": [
    "# Module 8 (Part B) — A Taste of Python\n",
    "\n",
    "**Companion notebook** for the Module 8B slides. Save it as `notebooks/02_taste.ipynb`\n",
    "in your `actuarial_intro` project (with `numpy`, `pandas`, `matplotlib`, and `seaborn`\n",
    "added via `uv add`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-001",
   "metadata": {},
   "outputs": [],
   "source": [
    "def check(label, got, expected, tol=1e-6):\n",
    "    \"\"\"Compare your answer with the expected value; prints ✅ or ❌ (never raises).\"\"\"\n",
    "    import math\n",
    "    try:\n",
    "        if isinstance(expected, float) or isinstance(got, float):\n",
    "            ok = math.isclose(float(got), float(expected), rel_tol=tol, abs_tol=tol)\n",
    "        elif isinstance(expected, (list, tuple)) and expected and isinstance(expected[0], str):\n",
    "            ok = list(got) == list(expected)\n",
    "        elif hasattr(expected, \"__len__\") and not isinstance(expected, str):\n",
    "            import numpy as np\n",
    "            ok = np.allclose(np.asarray(got, dtype=float), np.asarray(expected, dtype=float), rtol=tol, atol=tol)\n",
    "        else:\n",
    "            ok = got == expected\n",
    "    except Exception:\n",
    "        ok = False\n",
    "    print((\"✅ \" if ok else \"❌ \") + f\"{label}: got {got!r}, expected {expected!r}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-002",
   "metadata": {},
   "source": [
    "## 1. `print()` and f-strings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-003",
   "metadata": {},
   "outputs": [],
   "source": [
    "name, rate, reserve = \"Jane\", 0.0372, 1234567.891\n",
    "print(\"Hello, actuaries\")\n",
    "print(\"PV =\", 772.17)\n",
    "print(\"a\", \"b\", \"c\", sep=\" | \")\n",
    "print(f\"Analyst: {name}\")\n",
    "print(f\"Lapse rate: {rate:.1%}\")\n",
    "print(f\"Reserve: {reserve:,.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-004",
   "metadata": {},
   "source": [
    "## 2. Python as a financial calculator\n",
    "Use `**` for powers — `^` means something else in Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-005",
   "metadata": {},
   "outputs": [],
   "source": [
    "i, n = 0.05, 10\n",
    "v = 1 / (1 + i)\n",
    "print(round(1000 * (1 + i) ** n, 2))   # accumulate 1,000 for 10 years\n",
    "print(round(1000 * v ** n, 2))         # present value of 1,000 in 10 years\n",
    "a_n = (1 - v ** n) / i\n",
    "round(a_n, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-006",
   "metadata": {},
   "source": [
    "## 3. NumPy: whole columns at once"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-007",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "t = np.arange(1, 11)\n",
    "disc = 1.05 ** -t\n",
    "cf = np.full(10, 100.0)\n",
    "print(round((cf * disc).sum(), 2), cf @ disc)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-008",
   "metadata": {},
   "source": [
    "## 4. pandas: load and summarize a dataset\n",
    "This cell creates `../data/policies.csv` if it isn't there yet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-009",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "csv = Path(\"../data/policies.csv\")\n",
    "if not csv.exists():\n",
    "    csv.parent.mkdir(parents=True, exist_ok=True)\n",
    "    csv.write_text(\n",
    "        \"policy_id,issue_age,sex,product,sum_assured,annual_premium,status\\n\"\n",
    "        \"P001,34,F,TERM,250000,310.50,IN_FORCE\\nP002,45,M,TERM,500000,980.00,LAPSED\\n\"\n",
    "        \"P003,29,F,WHOLE_LIFE,100000,1450.00,IN_FORCE\\nP004,52,M,WHOLE_LIFE,200000,3890.00,IN_FORCE\\n\"\n",
    "        \"P005,41,F,TERM,300000,455.25,LAPSED\\nP006,38,M,TERM,400000,620.00,IN_FORCE\\n\"\n",
    "        \"P007,60,F,WHOLE_LIFE,150000,4210.00,DEATH_CLAIM\\nP008,33,M,TERM,350000,402.75,IN_FORCE\\n\")\n",
    "df = pd.read_csv(csv)\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-010",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(df.shape)\n",
    "df[\"status\"].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-011",
   "metadata": {},
   "outputs": [],
   "source": [
    "df.groupby(\"product\")[\"annual_premium\"].mean().round(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-012",
   "metadata": {},
   "source": [
    "## 5. Charts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-013",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "sns.set_theme(style=\"whitegrid\")\n",
    "sns.scatterplot(data=df, x=\"issue_age\", y=\"annual_premium\", hue=\"product\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-014",
   "metadata": {},
   "source": [
    "---\n",
    "## Your turn 1 — solve a system of linear equations\n",
    "\n",
    "Three bonds pay cash flows at times 1, 2, 3 (rows of `C`) and have prices `P`. Find the\n",
    "discount factors `d` with `np.linalg.solve`, then the spot rates\n",
    "$r_t = d_t^{-1/t} - 1$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-015",
   "metadata": {},
   "outputs": [],
   "source": [
    "C = np.array([[105, 0, 0], [6, 106, 0], [4, 4, 104]])\n",
    "P = np.array([100.00, 101.00, 96.50])\n",
    "d = spot = None\n",
    "...  # your code here\n",
    "check(\"discount factors\", d, [0.9524, 0.8989, 0.8567], tol=1e-4)\n",
    "check(\"spot rates\", spot, [0.05, 0.0547, 0.0529], tol=1e-3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-016",
   "metadata": {},
   "source": [
    "## Your turn 2 — pandas questions\n",
    "\n",
    "1. `mean_premium`: the average annual premium over all policies\n",
    "2. `older`: the policy IDs (as a list) with issue age 50 or more\n",
    "3. `lapsed`: how many policies have status `LAPSED`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-017",
   "metadata": {},
   "outputs": [],
   "source": [
    "mean_premium = older = lapsed = None\n",
    "...  # your code here\n",
    "check(\"mean premium\", mean_premium, 1539.8125)\n",
    "check(\"issue age 50+\", older, [\"P004\", \"P007\"])\n",
    "check(\"lapsed\", lapsed, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-018",
   "metadata": {},
   "source": [
    "## Share it\n",
    "Restart the kernel, **Run All**, then export to HTML from the terminal:\n",
    "\n",
    "```\n",
    "uv run jupyter nbconvert --to html --execute notebooks/02_taste.ipynb --output-dir output\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
