{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f8662384",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MeiraDavidson/biology_aging/blob/main/notebooks/build-telomere-weak-biomarker.ipynb)\n\n",
    "# A Real Number That Can't Tell Your Age 📏\n",
    "### A hands-on companion to Chapter 1\n",
    "\n",
    "Telomeres — the caps on your chromosomes — genuinely shorten as you age. So can we read your age, or your \"biological age,\" straight off your telomere length? In this notebook you'll simulate a realistic population and discover that the honest answer is mostly **no**: the scatter is so wide that telomere length barely pins down an individual. Then you'll see what a *strong* predictor (the Chapter 2 clock) looks like by comparison.\n",
    "\n",
    "The lesson is one of the most important in the whole book: **a number can be real at the population level and still be useless for you.**\n",
    "\n",
    "No setup. Runs in Colab. Press ▶ top to bottom."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "975e6f46",
   "metadata": {},
   "source": [
    "## Step 0 — Tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d01a9cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "rng = np.random.default_rng(1)\n",
    "print(\"Ready.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a43250dc",
   "metadata": {},
   "source": [
    "## Part 1 — Telomeres really do shorten with age\n",
    "\n",
    "We'll simulate **800 people**. Each is born with a telomere length drawn from a wide range (you *inherit* your starting length, and people differ a lot), then loses roughly **45 base pairs per year**, plus biological noise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5f57652",
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 800\n",
    "age = rng.uniform(20, 80, n)\n",
    "start_length = rng.normal(9.8, 1.6, n)               # inherited start (kb) - big spread\n",
    "telomere = start_length - 0.045*age + rng.normal(0, 0.4, n)   # ~45 bp/yr loss + noise\n",
    "\n",
    "r = np.corrcoef(telomere, age)[0,1]\n",
    "print(f\"Correlation between telomere length and age: r = {r:.2f}  (real-world: about -0.4)\")\n",
    "\n",
    "slope, intercept = np.polyfit(age, telomere, 1)\n",
    "plt.figure(figsize=(7,4.5))\n",
    "plt.scatter(age, telomere, s=10, alpha=0.4, color=\"#457b9d\")\n",
    "plt.plot(age, slope*age+intercept, color=\"#1d3557\", lw=2, label=\"average trend\")\n",
    "plt.xlabel(\"age (years)\"); plt.ylabel(\"telomere length (kb)\")\n",
    "plt.title(\"Yes - on average, telomeres shorten with age\"); plt.legend(); plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b57309d",
   "metadata": {},
   "source": [
    "The downward trend is real. A naive headline would stop here: *\"telomeres shorten with age, so measure yours to know your biological age!\"* Let's test whether that actually works."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "333715bf",
   "metadata": {},
   "source": [
    "## Part 2 — But can it tell *your* age?\n",
    "\n",
    "Flip the question around: given someone's telomere length, predict their age. We fit that line and measure two things — the typical error, and the **prediction band** (the range of ages consistent with a given telomere length)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8152bc6f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# predict age from telomere length\n",
    "b, a0 = np.polyfit(telomere, age, 1)\n",
    "pred_age = b*telomere + a0\n",
    "mae   = np.mean(np.abs(age - pred_age))\n",
    "band  = 1.96*np.std(age - pred_age)          # ~95% prediction band\n",
    "print(f\"Predicting age from telomere length: typical error = {mae:.0f} years\")\n",
    "print(f\"R^2 = {r**2:.2f}  (telomere length explains only ~{r**2*100:.0f}% of the age differences)\")\n",
    "\n",
    "# what does one person's telomere length tell us about their age?\n",
    "example = np.median(telomere)\n",
    "center  = b*example + a0\n",
    "print(f\"\\nSomeone with a telomere length of {example:.1f} kb could plausibly be anywhere\")\n",
    "print(f\"from age {center-band:.0f} to {center+band:.0f} - a {2*band:.0f}-year window.\")\n",
    "\n",
    "order = np.argsort(telomere)\n",
    "plt.figure(figsize=(7,4.5))\n",
    "plt.scatter(telomere, age, s=10, alpha=0.35, color=\"#457b9d\")\n",
    "plt.plot(telomere[order], (b*telomere+a0)[order], color=\"#1d3557\", lw=2, label=\"best guess\")\n",
    "plt.fill_between(telomere[order], (b*telomere+a0-band)[order], (b*telomere+a0+band)[order],\n",
    "                 color=\"#a8dadc\", alpha=0.5, label=\"ages that fit\")\n",
    "plt.xlabel(\"telomere length (kb)\"); plt.ylabel(\"true age (years)\")\n",
    "plt.title(f\"A real signal, drowned in scatter (off by ~{mae:.0f} years)\"); plt.legend()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d2b9e89",
   "metadata": {},
   "source": [
    "Knowing your telomere length barely narrows down your age — the band of \"ages that fit\" is enormous. The signal is *real* but *weak*: real enough to see across a whole population, far too noisy to tell you much about **you**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf4521f6",
   "metadata": {},
   "source": [
    "## Part 3 — What a *strong* predictor looks like\n",
    "\n",
    "For contrast, here's a strong biomarker — standing in for the **epigenetic clock** of Chapter 2 — measured on the same people. Same plot, same band. Watch the difference."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "718c8f88",
   "metadata": {},
   "outputs": [],
   "source": [
    "clock = age + rng.normal(0, 3.0, n)                 # a tight, strong predictor of age\n",
    "rc = np.corrcoef(clock, age)[0,1]\n",
    "bc, ac = np.polyfit(clock, age, 1)\n",
    "pred_c = bc*clock + ac\n",
    "mae_c  = np.mean(np.abs(age - pred_c))\n",
    "band_c = 1.96*np.std(age - pred_c)\n",
    "\n",
    "print(f\"Telomeres      : error ~{mae:.0f} yr,  R^2 = {r**2:.2f}\")\n",
    "print(f\"Clock (Ch.2)   : error ~{mae_c:.0f} yr,  R^2 = {rc**2:.2f}\")\n",
    "\n",
    "fig, ax = plt.subplots(1, 2, figsize=(11,4.3), sharey=True)\n",
    "for a_, (x, line_b, line_a, bd, ttl, col) in zip(\n",
    "        ax, [(telomere, b, a0, band, f\"Telomeres (off by ~{mae:.0f} yr)\", \"#457b9d\"),\n",
    "             (clock,    bc, ac, band_c, f\"Epigenetic clock (off by ~{mae_c:.0f} yr)\", \"#2a9d8f\")]):\n",
    "    o = np.argsort(x)\n",
    "    a_.scatter(x, age, s=8, alpha=0.3, color=col)\n",
    "    a_.plot(x[o], (line_b*x+line_a)[o], color=\"#1d3557\", lw=2)\n",
    "    a_.fill_between(x[o], (line_b*x+line_a-bd)[o], (line_b*x+line_a+bd)[o], color=col, alpha=0.25)\n",
    "    a_.set_title(ttl); a_.set_xlabel(\"biomarker value\")\n",
    "ax[0].set_ylabel(\"true age (years)\")\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a0c84ad",
   "metadata": {},
   "source": [
    "Same idea, opposite usefulness. Both telomeres and the clock are \"biomarkers of aging,\" but one is a thermometer that barely registers and the other reads your age to within a few years. That gap is *exactly* why the field needed the epigenetic clock, and why *\"measure your telomeres to learn your biological age\"* oversells a genuinely weak signal.\n",
    "\n",
    "> Keep two lessons side by side. **Real on average ≠ informative for you** (this notebook). And even the *strong* clock is still only a **predictor, not a lever** — moving it isn't the same as changing your aging, as you saw in the Chapter 2 notebook. A good biomarker has to clear *both* bars.\n",
    "\n",
    "### Go further\n",
    "- Widen or narrow the inherited starting-length spread (`1.6`). How tight would the scatter have to be before telomeres became useful for individuals?\n",
    "- How many people would you need to *reliably* detect a 2-year telomere difference between two groups? (Real telomere studies need thousands.)\n",
    "- Combine telomere length with a few other weak markers and see whether, together, they start to predict age better."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}