{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d1387a94",
   "metadata": {},
   "source": [
    "# Which marketing channels actually drive sales — a runnable Markov attribution notebook\n",
    "\n",
    "Most customers touch several channels before they buy: a display ad, a search a\n",
    "week later, an email, then a direct visit. **Last-click attribution** hands the\n",
    "whole sale to the final touch and zeroes out everything before it — which slowly\n",
    "starves the channels that *start* journeys.\n",
    "\n",
    "This notebook builds the data-driven alternative: it models journeys as an\n",
    "**absorbing Markov chain** and scores each channel by its **removal effect** — how\n",
    "much the overall conversion rate drops when that channel is taken out of the\n",
    "journey network. Then it shows, side by side, how differently last-click and\n",
    "Markov split the credit.\n",
    "\n",
    "**Everything here runs locally.** The worked example uses synthetic data,\n",
    "clearly labeled as synthetic. The last section lets you drop in your own journey\n",
    "data — it never leaves your machine. No hand-typed numbers: every figure and\n",
    "every quoted value is computed by the cells below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ecae4f84",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:28.326578Z",
     "iopub.execute_input": "2026-07-18T11:44:28.326578Z",
     "shell.execute_reply": "2026-07-18T11:44:29.343742Z",
     "iopub.status.idle": "2026-07-18T11:44:29.343742Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Setup complete. numpy 1.26.4\n"
     ]
    }
   ],
   "source": [
    "# Standard scientific-Python stack. Nothing here reaches the network.\n",
    "import csv\n",
    "import io\n",
    "import json\n",
    "from collections import Counter, defaultdict\n",
    "from datetime import datetime\n",
    "from pathlib import Path\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.ticker as mticker\n",
    "import numpy as np\n",
    "\n",
    "# Reproducibility: one seed drives the whole synthetic corpus.\n",
    "SEED = 20260716\n",
    "rng = np.random.default_rng(SEED)\n",
    "\n",
    "# Brand palette (validated colour-blind-safe, light + dark, via the design system).\n",
    "C_MARKOV     = \"#0a8f5f\"   # Markov removal-effect credit  (emerald)\n",
    "C_LASTCLICK  = \"#2a78d6\"   # last-click credit             (blue)\n",
    "C_INK        = \"#1f2124\"   # near-black text\n",
    "C_MUTED      = \"#8a8f98\"   # recessive grid / axis\n",
    "PAPER        = \"#fdfcf9\"   # warm paper surface\n",
    "\n",
    "plt.rcParams.update({\n",
    "    \"figure.facecolor\": PAPER,\n",
    "    \"axes.facecolor\":   PAPER,\n",
    "    \"savefig.facecolor\": PAPER,\n",
    "    \"font.size\": 11,\n",
    "    \"axes.edgecolor\": C_MUTED,\n",
    "    \"axes.labelcolor\": C_INK,\n",
    "    \"text.color\": C_INK,\n",
    "    \"xtick.color\": C_INK,\n",
    "    \"ytick.color\": C_INK,\n",
    "    \"axes.spines.top\": False,\n",
    "    \"axes.spines.right\": False,\n",
    "})\n",
    "\n",
    "FIGS = Path(\"figures\")\n",
    "DATA = Path(\"data\")\n",
    "FIGS.mkdir(exist_ok=True)\n",
    "DATA.mkdir(exist_ok=True)\n",
    "print(\"Setup complete. numpy\", np.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5208a26",
   "metadata": {},
   "source": [
    "## The model in one paragraph\n",
    "\n",
    "We list the *states* a customer can be in: a **START** origin, one state per\n",
    "marketing channel, and two terminal (\"absorbing\") states — **CONV** (they\n",
    "bought) and **NULL** (they left without buying). A **transition matrix** `T`\n",
    "holds the probability of moving from each state to each other state; we estimate\n",
    "it by counting the moves in the observed journeys. From `T` the algebra of\n",
    "absorbing Markov chains gives the baseline conversion probability in a single\n",
    "matrix inversion — no simulation, no loop. The **removal effect** of a channel is\n",
    "the drop in that baseline when the channel is removed and its traffic is\n",
    "redistributed across the channels that remain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2c8621aa",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:29.343742Z",
     "iopub.execute_input": "2026-07-18T11:44:29.343742Z",
     "shell.execute_reply": "2026-07-18T11:44:29.363014Z",
     "iopub.status.idle": "2026-07-18T11:44:29.363014Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Engine ready. States: ['START', 'Display', 'Search', 'Email', 'CONV', 'NULL']\n"
     ]
    }
   ],
   "source": [
    "# ---- Core Markov-attribution engine -------------------------------------\n",
    "# States, in a fixed order. Indices 0..3 are transient (START + 3 channels);\n",
    "# indices 4,5 are absorbing (CONV, NULL).\n",
    "STATES    = [\"START\", \"Display\", \"Search\", \"Email\", \"CONV\", \"NULL\"]\n",
    "CHANNELS  = [\"Display\", \"Search\", \"Email\"]\n",
    "IDX       = {s: i for i, s in enumerate(STATES)}\n",
    "TRANSIENT = [IDX[\"START\"], IDX[\"Display\"], IDX[\"Search\"], IDX[\"Email\"]]\n",
    "ABSORBING = [IDX[\"CONV\"], IDX[\"NULL\"]]\n",
    "CHAN_IDX  = [IDX[c] for c in CHANNELS]\n",
    "\n",
    "\n",
    "def p_convert(T, transient, absorbing):\n",
    "    \"\"\"Absorption-probability matrix B for the sub-chain on `transient` states.\n",
    "\n",
    "    N = (I - Q)^-1 is the Fundamental Matrix; B = N @ R gives, in B[i, j], the\n",
    "    probability a journey starting at transient state i ends in absorbing state j.\n",
    "    \"\"\"\n",
    "    Q = T[np.ix_(transient, transient)]\n",
    "    R = T[np.ix_(transient, absorbing)]\n",
    "    N = np.linalg.inv(np.eye(len(transient)) - Q)\n",
    "    return N @ R\n",
    "\n",
    "\n",
    "def baseline_conversion(T):\n",
    "    \"\"\"P(convert) for a journey starting at START under the full model.\"\"\"\n",
    "    B = p_convert(T, TRANSIENT, ABSORBING)\n",
    "    return B[0, 0]  # START is first transient row; CONV is first absorbing col\n",
    "\n",
    "\n",
    "def conversion_without(T, ch):\n",
    "    \"\"\"P(convert) after removing channel `ch` (a state index).\n",
    "\n",
    "    Every remaining transient row has the probability mass that used to flow into\n",
    "    `ch` redistributed across its other destinations, in proportion to their\n",
    "    existing shares (the standard proportional-substitution counterfactual).\n",
    "    \"\"\"\n",
    "    Tr = T.astype(float).copy()\n",
    "    kept_transient = [i for i in TRANSIENT if i != ch]\n",
    "    for i in kept_transient:\n",
    "        scale = 1.0 - T[i, ch]\n",
    "        if scale <= 1e-12:\n",
    "            # Degenerate: state i went only to ch. Spread uniformly across the\n",
    "            # states that remain, so the row still sums to 1.\n",
    "            others = [j for j in range(len(STATES)) if j != ch]\n",
    "            Tr[i, :] = 0.0\n",
    "            for j in others:\n",
    "                Tr[i, j] = 1.0 / len(others)\n",
    "        else:\n",
    "            Tr[i, :] = T[i, :] / scale\n",
    "            Tr[i, ch] = 0.0\n",
    "    B = p_convert(Tr, kept_transient, ABSORBING)\n",
    "    return B[0, 0]\n",
    "\n",
    "\n",
    "def removal_effects(T, clip_negative=True):\n",
    "    \"\"\"Raw removal effect R(c) = P(full) - P(without c) for each channel.\"\"\"\n",
    "    p_full = baseline_conversion(T)\n",
    "    raw = {}\n",
    "    for c in CHANNELS:\n",
    "        drop = p_full - conversion_without(T, IDX[c])\n",
    "        raw[c] = max(0.0, drop) if clip_negative else drop\n",
    "    return raw\n",
    "\n",
    "\n",
    "def markov_credit(T):\n",
    "    \"\"\"Removal effects normalised to credit percentages that sum to 100.\"\"\"\n",
    "    raw = removal_effects(T, clip_negative=True)\n",
    "    total = sum(raw.values())\n",
    "    if total <= 0:\n",
    "        return {c: 0.0 for c in CHANNELS}\n",
    "    return {c: 100.0 * raw[c] / total for c in CHANNELS}\n",
    "\n",
    "\n",
    "def analytic_last_click(T):\n",
    "    \"\"\"Model-implied last-click shares, straight from T (no corpus needed).\n",
    "\n",
    "    A journey's last touch before CONV is the state it took the CONV step from.\n",
    "    The expected number of journeys whose last touch is channel c equals the\n",
    "    expected number of visits to c, N[START, c], times the direct-conversion\n",
    "    probability T[c, CONV]. Normalise across channels to get shares.\n",
    "    \"\"\"\n",
    "    N = np.linalg.inv(np.eye(len(TRANSIENT)) - T[np.ix_(TRANSIENT, TRANSIENT)])\n",
    "    w = {c: N[0, TRANSIENT.index(IDX[c])] * T[IDX[c], IDX[\"CONV\"]] for c in CHANNELS}\n",
    "    total = sum(w.values())\n",
    "    if total <= 0:\n",
    "        return {c: 0.0 for c in CHANNELS}\n",
    "    return {c: 100.0 * w[c] / total for c in CHANNELS}\n",
    "\n",
    "\n",
    "print(\"Engine ready. States:\", STATES)\n",
    "\n",
    "\n",
    "def conversion_without_lost(T, ch):\n",
    "    \"\"\"P(convert) after removing channel `ch` when its traffic is LOST rather than\n",
    "    rerouted: every remaining row sends the mass that used to flow into `ch`\n",
    "    straight to NULL. This is the s=0 pole (customers are lost), the opposite of\n",
    "    conversion_without's proportional substitution (s=1).\"\"\"\n",
    "    Tr = T.astype(float).copy()\n",
    "    kept_transient = [i for i in TRANSIENT if i != ch]\n",
    "    for i in kept_transient:\n",
    "        Tr[i, IDX[\"NULL\"]] += Tr[i, ch]\n",
    "        Tr[i, ch] = 0.0\n",
    "    B = p_convert(Tr, kept_transient, ABSORBING)\n",
    "    return B[0, 0]\n",
    "\n",
    "\n",
    "def substitution_band(T):\n",
    "    \"\"\"Per-channel credit range [low, high] between the two substitution poles.\n",
    "\n",
    "    Low  = the standard removal effect (markov_credit; the channel's traffic\n",
    "           reroutes onto the others) - the point estimate the article reports.\n",
    "    High = that ONE channel's worst case: recompute only its removal effect with\n",
    "           its traffic lost (s=0) while every other channel still substitutes\n",
    "           (s=1), then renormalise. A per-channel scenario, so the high ends do\n",
    "           not add to 100. Reproduces the article's range table.\n",
    "    \"\"\"\n",
    "    p_full = baseline_conversion(T)\n",
    "    r1 = removal_effects(T, clip_negative=True)   # s=1, every channel\n",
    "    low = markov_credit(T)                        # normalised s=1 point estimate\n",
    "    band = {}\n",
    "    for c in CHANNELS:\n",
    "        r0c = max(0.0, p_full - conversion_without_lost(T, IDX[c]))\n",
    "        denom = r0c + sum(r1[o] for o in CHANNELS if o != c)\n",
    "        high = 100.0 * r0c / denom if denom > 0 else 0.0\n",
    "        band[c] = (round(float(low[c]), 4), round(float(high), 4))\n",
    "    return band"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62ae4f6f",
   "metadata": {},
   "source": [
    "## A synthetic customer-journey dataset (labeled synthetic)\n",
    "\n",
    "To have something concrete to run on, we generate journeys from a known\n",
    "\"ground-truth\" flow across three channels — **Display**, **Search**, and\n",
    "**Email**. The flow is deliberately realistic: Display *starts* a lot of\n",
    "journeys and mostly hands off to Search; Search is where most conversions\n",
    "actually happen; Email quietly feeds Search. This is exactly the shape where\n",
    "last-click and Markov disagree.\n",
    "\n",
    "We sample the journeys, then **estimate** the transition matrix back from them —\n",
    "the same thing you do with real data: you observe journeys, you count the moves."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "63715040",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:29.366486Z",
     "iopub.execute_input": "2026-07-18T11:44:29.366486Z",
     "iopub.status.idle": "2026-07-18T11:44:29.571708Z",
     "shell.execute_reply": "2026-07-18T11:44:29.571708Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Generated 4000 synthetic journeys\n",
      "  converted:        1281  (32.0%)\n",
      "  single-touch:     2159  (54.0% of all journeys)\n",
      "  mean touch count: 1.49 (journeys with >=1 touch)\n"
     ]
    }
   ],
   "source": [
    "# ---- Ground-truth generator (synthetic) ---------------------------------\n",
    "# Rows are transition probabilities out of each state; every transient row\n",
    "# sums to 1. This is the \"truth\" we sample from; the model never sees it — it\n",
    "# only sees the sampled journeys and has to recover the structure.\n",
    "GEN = np.zeros((6, 6))\n",
    "GEN[IDX[\"START\"],   IDX[\"Display\"]] = 0.55\n",
    "GEN[IDX[\"START\"],   IDX[\"Search\"]]  = 0.10\n",
    "GEN[IDX[\"START\"],   IDX[\"Email\"]]   = 0.35\n",
    "\n",
    "GEN[IDX[\"Display\"], IDX[\"Search\"]]  = 0.65   # Display is the main feeder into Search\n",
    "GEN[IDX[\"Display\"], IDX[\"CONV\"]]    = 0.05   # rarely closes on its own\n",
    "GEN[IDX[\"Display\"], IDX[\"NULL\"]]    = 0.30\n",
    "\n",
    "GEN[IDX[\"Search\"],  IDX[\"CONV\"]]    = 0.45   # Search is the closer\n",
    "GEN[IDX[\"Search\"],  IDX[\"Email\"]]   = 0.05\n",
    "GEN[IDX[\"Search\"],  IDX[\"NULL\"]]    = 0.50\n",
    "\n",
    "GEN[IDX[\"Email\"],   IDX[\"Search\"]]  = 0.30   # Email is a weaker feeder with high drop-off\n",
    "GEN[IDX[\"Email\"],   IDX[\"CONV\"]]    = 0.10\n",
    "GEN[IDX[\"Email\"],   IDX[\"NULL\"]]    = 0.60\n",
    "\n",
    "GEN[IDX[\"CONV\"], IDX[\"CONV\"]] = 1.0\n",
    "GEN[IDX[\"NULL\"], IDX[\"NULL\"]] = 1.0\n",
    "\n",
    "assert np.allclose(GEN[TRANSIENT].sum(axis=1), 1.0), \"transient rows must sum to 1\"\n",
    "\n",
    "\n",
    "def sample_journey(gen, max_steps=25):\n",
    "    \"\"\"Walk the chain from START to an absorbing state. Returns the list of\n",
    "    channel touchpoints (excluding START) and whether it converted.\"\"\"\n",
    "    state = IDX[\"START\"]\n",
    "    touches = []\n",
    "    for _ in range(max_steps):\n",
    "        nxt = rng.choice(len(STATES), p=gen[state])\n",
    "        if nxt == IDX[\"CONV\"]:\n",
    "            return touches, True\n",
    "        if nxt == IDX[\"NULL\"]:\n",
    "            return touches, False\n",
    "        touches.append(STATES[nxt])\n",
    "        state = nxt\n",
    "    return touches, False  # ran out of steps -> treat as non-conversion\n",
    "\n",
    "\n",
    "N_JOURNEYS = 4000\n",
    "journeys = [sample_journey(GEN) for _ in range(N_JOURNEYS)]\n",
    "\n",
    "n_conv = sum(1 for _, c in journeys if c)\n",
    "n_single = sum(1 for t, _ in journeys if len(t) == 1)\n",
    "avg_len = np.mean([len(t) for t, _ in journeys if len(t) > 0])\n",
    "print(f\"Generated {N_JOURNEYS} synthetic journeys\")\n",
    "print(f\"  converted:        {n_conv}  ({100*n_conv/N_JOURNEYS:.1f}%)\")\n",
    "print(f\"  single-touch:     {n_single}  ({100*n_single/N_JOURNEYS:.1f}% of all journeys)\")\n",
    "print(f\"  mean touch count: {avg_len:.2f} (journeys with >=1 touch)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dc4f6ad1",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:29.572712Z",
     "iopub.execute_input": "2026-07-18T11:44:29.572712Z",
     "shell.execute_reply": "2026-07-18T11:44:29.590776Z",
     "iopub.status.idle": "2026-07-18T11:44:29.590776Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Estimated transition matrix T (rows = from, cols = to):\n",
      "\n",
      "from \\ to    START  Display   Search    Email     CONV     NULL\n",
      "START        0.000    0.546    0.101    0.353    0.000    0.000\n",
      "Display      0.000    0.000    0.642    0.000    0.057    0.301\n",
      "Search       0.000    0.000    0.000    0.045    0.444    0.511\n",
      "Email        0.000    0.000    0.304    0.000    0.098    0.597\n"
     ]
    }
   ],
   "source": [
    "# ---- Estimate the transition matrix from the observed journeys ----------\n",
    "def fit_transition_matrix(journeys):\n",
    "    \"\"\"Maximum-likelihood transition matrix: count moves, normalise each row.\"\"\"\n",
    "    counts = np.zeros((6, 6))\n",
    "    for touches, converted in journeys:\n",
    "        chain = [\"START\"] + touches + ([\"CONV\"] if converted else [\"NULL\"])\n",
    "        for a, b in zip(chain[:-1], chain[1:]):\n",
    "            counts[IDX[a], IDX[b]] += 1\n",
    "    T = np.zeros((6, 6))\n",
    "    for i in range(6):\n",
    "        row = counts[i].sum()\n",
    "        if row > 0:\n",
    "            T[i] = counts[i] / row\n",
    "    T[IDX[\"CONV\"], IDX[\"CONV\"]] = 1.0\n",
    "    T[IDX[\"NULL\"], IDX[\"NULL\"]] = 1.0\n",
    "    return T, counts\n",
    "\n",
    "\n",
    "T_hat, counts = fit_transition_matrix(journeys)\n",
    "\n",
    "# Show the estimated matrix (transient rows only) as a readable table.\n",
    "print(\"Estimated transition matrix T (rows = from, cols = to):\\n\")\n",
    "header = \"from \\\\ to\".ljust(9) + \"\".join(s.rjust(9) for s in STATES)\n",
    "print(header)\n",
    "for i in TRANSIENT:\n",
    "    line = STATES[i].ljust(9) + \"\".join(f\"{T_hat[i, j]:9.3f}\" for j in range(6))\n",
    "    print(line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "278aaa62",
   "metadata": {},
   "source": [
    "## Last-click vs Markov — where the credit goes\n",
    "\n",
    "**Last-click** gives 100% of every sale to the final channel touched before the\n",
    "purchase. **Markov** scores each channel by its removal effect and normalises\n",
    "those to shares. Run both on the same journeys and compare."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7dfd6362",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:29.590776Z",
     "iopub.execute_input": "2026-07-18T11:44:29.594890Z",
     "iopub.status.idle": "2026-07-18T11:44:29.694690Z",
     "shell.execute_reply": "2026-07-18T11:44:29.694690Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Baseline conversion probability (from START): 0.320\n",
      "\n",
      "channel     last-click %    Markov %  removal effect\n",
      "Display             9.8%       17.2%          0.0348\n",
      "Search             78.6%       82.8%          0.1676\n",
      "Email              11.6%        0.0%          0.0000\n"
     ]
    }
   ],
   "source": [
    "# ---- Last-click credit (from the raw journeys) --------------------------\n",
    "def last_click_credit(journeys):\n",
    "    counts = Counter()\n",
    "    for touches, converted in journeys:\n",
    "        if converted and touches:\n",
    "            counts[touches[-1]] += 1\n",
    "    total = sum(counts.values())\n",
    "    return {c: (100.0 * counts.get(c, 0) / total if total else 0.0) for c in CHANNELS}\n",
    "\n",
    "\n",
    "lc = last_click_credit(journeys)\n",
    "mk = markov_credit(T_hat)\n",
    "raw = removal_effects(T_hat, clip_negative=True)\n",
    "raw_unclipped = removal_effects(T_hat, clip_negative=False)\n",
    "p_full = baseline_conversion(T_hat)\n",
    "\n",
    "print(f\"Baseline conversion probability (from START): {p_full:.3f}\\n\")\n",
    "print(f\"{'channel':<10}{'last-click %':>14}{'Markov %':>12}{'removal effect':>16}\")\n",
    "for c in CHANNELS:\n",
    "    print(f\"{c:<10}{lc[c]:>13.1f}%{mk[c]:>11.1f}%{raw[c]:>16.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "eb89eead",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:29.694690Z",
     "iopub.execute_input": "2026-07-18T11:44:29.699696Z",
     "shell.execute_reply": "2026-07-18T11:44:30.015881Z",
     "iopub.status.idle": "2026-07-18T11:44:30.015881Z"
    }
   },
   "outputs": [
    {
     "output_type": "display_data",
     "metadata": {},
     "data": {
      "text/plain": [
       "<Figure size 740x440 with 1 Axes>"
      ],
      "image/png": "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"
     }
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "saved figures/fig_last_click_vs_markov.png\n"
     ]
    }
   ],
   "source": [
    "# ---- Figure 1: last-click vs Markov, side by side -----------------------\n",
    "def grouped_bar(lc, mk, path):\n",
    "    labels = CHANNELS\n",
    "    x = np.arange(len(labels))\n",
    "    w = 0.38\n",
    "    fig, ax = plt.subplots(figsize=(7.4, 4.4))\n",
    "\n",
    "    b1 = ax.bar(x - w/2 - 0.01, [lc[c] for c in labels], w,\n",
    "                label=\"Last-click\", color=C_LASTCLICK)\n",
    "    b2 = ax.bar(x + w/2 + 0.01, [mk[c] for c in labels], w,\n",
    "                label=\"Markov removal effect\", color=C_MARKOV)\n",
    "\n",
    "    ax.set_title(\"Who gets the credit? Last-click vs Markov\",\n",
    "                 fontsize=13, fontweight=\"bold\", loc=\"left\", color=C_INK)\n",
    "    ax.set_xlabel(\"Channel\")\n",
    "    ax.set_ylabel(\"Share of conversion credit (%)\")\n",
    "    ax.set_xticks(x, labels)\n",
    "    top = max(max(lc.values()), max(mk.values()))\n",
    "    ax.set_ylim(0, top * 1.18)\n",
    "    ax.yaxis.set_major_formatter(mticker.FormatStrFormatter(\"%d%%\"))\n",
    "    ax.grid(axis=\"y\", color=C_MUTED, alpha=0.25, linewidth=0.8)\n",
    "    ax.set_axisbelow(True)\n",
    "    for bars in (b1, b2):\n",
    "        for r in bars:\n",
    "            ax.annotate(f\"{r.get_height():.0f}%\",\n",
    "                        (r.get_x() + r.get_width()/2, r.get_height()),\n",
    "                        ha=\"center\", va=\"bottom\", fontsize=10, color=C_INK,\n",
    "                        xytext=(0, 2), textcoords=\"offset points\")\n",
    "    ax.legend(frameon=False, loc=\"upper center\", bbox_to_anchor=(0.5, -0.16),\n",
    "              ncol=2)\n",
    "    fig.tight_layout()\n",
    "    fig.savefig(path, dpi=150, bbox_inches=\"tight\")\n",
    "    plt.show()\n",
    "    plt.close(fig)\n",
    "\n",
    "\n",
    "grouped_bar(lc, mk, FIGS / \"fig_last_click_vs_markov.png\")\n",
    "print(\"saved figures/fig_last_click_vs_markov.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1854d62",
   "metadata": {},
   "source": [
    "Two things move when you switch from last-click to Markov. **Display** — almost\n",
    "never the final touch, so last-click barely credits it — gains, because removing\n",
    "Display from the network starves the Search conversions that depend on it.\n",
    "**Email** goes the other way: last-click credits it for sitting near the close,\n",
    "but the model finds its visitors could have reached a sale through the other\n",
    "channels anyway, so its structural credit nearly vanishes. Last-click was\n",
    "crediting *position in the journey*; the removal effect credits *whether the\n",
    "channel is load-bearing*. (Search still takes the lion's share — hold that\n",
    "thought for the \"caveats to watch out for\" section, because part of it is a\n",
    "known bias.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c26f4dad",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.018451Z",
     "iopub.execute_input": "2026-07-18T11:44:30.018451Z",
     "shell.execute_reply": "2026-07-18T11:44:30.295596Z",
     "iopub.status.idle": "2026-07-18T11:44:30.295596Z"
    }
   },
   "outputs": [
    {
     "output_type": "display_data",
     "metadata": {},
     "data": {
      "text/plain": [
       "<Figure size 740x440 with 1 Axes>"
      ],
      "image/png": "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gxgIEBERERHpIAYCREREREQ6iIEAEREREZEOYiBARERERKSDGAgQEREREekgBgJERERERDqIgQARERERkQ5iIEBEREREpIMYCBARERER6SAGAkREREREOoiBABERERGRDmIgQERERESkgxgIEBERERHpIAYCREREREQ6iIEAEREREZEOYiBARERERKSDGAgQEREREekgBgJERERERDqIgQARERERkQ5iIEBEREREpIMYCBARERER6SAGAkREREREOoiBABERERGRDmIgQERERESkgxgIEBERERHpIAYCREREREQ6iIEAEREREZEOMtB2BQAg6NlzzPhjNm7cvA1TExO0atkcE8aPhrGx8U+X3X/gEFavXY/Xr9+gWNEiGDF8CJo3a6KUx9HZVWU5a2sr/PO/ixrbBiIiIiKinETrgYBUKkVPz36wt7fFqhWLERb2EbPm+CA8IgKLF8774bLHT5zC+IlTMXjQAHjUcseZM2cxfORYmOXNCw+Pmkp5e3v2QOtWLYRpQ7FhpmwPEREREVFOoPVAYKfvHkRKpfA7sh/58lkCAPT19TF67EQM9fZC8eJOqS67eMkKNG/WBBPGjQYA1KheDYFBQVi8dIVKIGBnZ4sKFcpl3oYQEREREeUgWh8jcP7CJdR0ry4EAQDQtEljiMVinL9wKdXlXr16jcCgILRq2VwpvXWrFrh77z98/BieaXUmIiIiIsrptH5HIDAwCB07tlNKMzISo2iRwggIDEp1ua/zijs5KqUXL+4EhUKBwKAg5MtXSUhf8+d6zF+4BCYmJqjtUROTJo6FvZ1dimXHxckgk8mEaZksHgAgl8shl8vTtoGU4/Ezp+yA7ZCyC7ZFyg600Q719LR+/VzjtB4IREqlkEjMVNIl5hJERESmvlykNCnfd8uam0u+zP+2bPt2rVG/Xl1YW1nhib8/Vqxcg85dPXH86AGYm5urlL167TosW75KmDYUi+E1eARCQkJhaKj1XUZZ7E3wO21XgYjtkLINtkXKDrTRDgsXss3ydWa2bHFWK4JINVGhgCiFZNWFlTMpFKplLvCZI/xftWplVK5UEa3bdsKu3fswyKu/SpHegwaif9/ewrRMFo/NOw/B1tYGRmKxGpWi3MTeroC2q0DEdkjZBtsiZQdsh5qh9UDAXCJBpFSqki6VRsHJKfWBwl+v/EsjpbCxtk623Jc7BV/mp6RUyRJwdCiG+w8epjjfyEgMI6NvJ/xxX7oJ6enp5crbQvRj/MwpO2A7pOyCbZGyA7ZDzdD6XnRyckTgd2MB4uJkePHylUr//+S+zvt+HEFAQCBEIhGcHFNfFgAUX28dEBERERHpIK0HAnXreODq/64hPDxCSDt95i/IZDLUreOR6nKFCxeCk6Mjjh0/qZR+1O84yrmVVXoK0fcePnyEZ89fwK1smQzXn4iIiIgoJ9J616Du3Tpj67ad8PIehuFDBye9UGy2D9q0bqn0DoGJk6fjwMHD8H98T0gbPWoYho8ciyJFCqNWzRo489dZXLp8FZs3rBXyrFu/CS9fvUK1qlVgZZUPT58GYOXqtbC1LYgunTtk6bYSEREREWUXWg8EJBIJtm/biN9nzIL30FEwMTZGq5bNMXHCGKV88sREJCYmKqU1b9YEsbGxWLVmHdav34SiRYtg+dKFSi8Tc3QohpOnzsDv2AlER8cgXz5L1KtbB2NHj4BEkvo4AiIiIiKi3EzrgQCQdLK+ZdO6H+aZ7zMb831mq6R3aN8WHdq3TXW5Bg3qoUGDehmtIhERERFRrqL1MQJERERERJT1GAgQEREREekgBgJERERERDqIgQARERERkQ5iIEBEREREpIMYCBARERER6SAGAkREREREOoiBABERERGRDmIgQERERESkgxgIEBERERHpIAYCREREREQ6iIEAEREREZEOYiBARERERKSDGAgQEREREekgBgJERERERDqIgQARERERkQ5iIEBEREREpIMYCBARERER6SAGAkREREREOoiBABERERGRDmIgQERERESkgxgIEBERERHpIAYCREREREQ6iIEAEREREZEOYiBARERERKSDGAgQEREREekgBgJERERERDqIgQARERERkQ5iIEBEREREpIMYCBARERER6aBsEQgEPXuOPv284OpWGVWqeWDGH3Pw+fNntZbdf+AQGjZpiZKuFdC0eRscP3FKJU98fDx8FixGNfc6KF22Err37INHj59oejOIiIiIiHIMrQcCUqkUPT37ITo6GqtWLMbkSeNw+KgfJk/99afLHj9xCuMnTkXjRg2wacMauNeojuEjx+LSpStK+WbOnoft230xeuQwrF29HPr6BvDs1R+hoaGZtVlERERERNmagbYrsNN3DyKlUvgd2Y98+SwBAPr6+hg9diKG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"
     }
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "saved figures/fig_removal_effect.png\n"
     ]
    }
   ],
   "source": [
    "# ---- Figure 2: the removal effect itself --------------------------------\n",
    "# The baseline conversion probability, and what it falls to when each channel is\n",
    "# removed. The drop *is* the removal effect.\n",
    "def removal_figure(T, path):\n",
    "    p0 = baseline_conversion(T)\n",
    "    without = {c: conversion_without(T, IDX[c]) for c in CHANNELS}\n",
    "    fig, ax = plt.subplots(figsize=(7.4, 4.4))\n",
    "    xs = [\"Full model\"] + [f\"minus {c}\" for c in CHANNELS]\n",
    "    ys = [p0] + [without[c] for c in CHANNELS]\n",
    "    colors = [C_MUTED] + [C_MARKOV] * len(CHANNELS)\n",
    "    bars = ax.bar(xs, ys, color=colors, width=0.6)\n",
    "    ax.axhline(p0, color=C_MUTED, linewidth=1, linestyle=(0, (4, 3)), alpha=0.7)\n",
    "    ax.set_title(\"Removal effect: conversion probability when a channel is taken out\",\n",
    "                 fontsize=13, fontweight=\"bold\", loc=\"left\", color=C_INK)\n",
    "    ax.set_xlabel(\"Model\")\n",
    "    ax.set_ylabel(\"P(convert) from START\")\n",
    "    ax.set_ylim(0, p0 * 1.25)\n",
    "    ax.grid(axis=\"y\", color=C_MUTED, alpha=0.25, linewidth=0.8)\n",
    "    ax.set_axisbelow(True)\n",
    "    for r, y in zip(bars, ys):\n",
    "        ax.annotate(f\"{y:.3f}\", (r.get_x() + r.get_width()/2, y),\n",
    "                    ha=\"center\", va=\"bottom\", fontsize=10, color=C_INK,\n",
    "                    xytext=(0, 2), textcoords=\"offset points\")\n",
    "    fig.tight_layout()\n",
    "    fig.savefig(path, dpi=150, bbox_inches=\"tight\")\n",
    "    plt.show()\n",
    "    plt.close(fig)\n",
    "\n",
    "\n",
    "removal_figure(T_hat, FIGS / \"fig_removal_effect.png\")\n",
    "print(\"saved figures/fig_removal_effect.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26f7a10a",
   "metadata": {},
   "source": [
    "## Pinned results — the numbers the article and the widget both use\n",
    "\n",
    "The interactive widget that accompanies this post recomputes the exact same math\n",
    "in the browser. To keep the two honest, we pin a set of transition matrices here\n",
    "with their expected outputs; the widget must reproduce them within a tight\n",
    "tolerance, and the article quotes these values rather than any typed-in number."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "dcf1e0c8",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.295596Z",
     "iopub.execute_input": "2026-07-18T11:44:30.295596Z",
     "shell.execute_reply": "2026-07-18T11:44:30.328001Z",
     "iopub.status.idle": "2026-07-18T11:44:30.328001Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "wrote data/checks.json with 4 pinned sets\n",
      "wrote data/worked_example.json\n",
      "{\n",
      "  \"n_journeys\": 4000,\n",
      "  \"n_converted\": 1281,\n",
      "  \"conversion_rate_pct\": 32.0,\n",
      "  \"single_touch_pct\": 54.0,\n",
      "  \"baseline_conversion_probability\": 0.32,\n",
      "  \"last_click_pct\": {\n",
      "    \"Display\": 9.8,\n",
      "    \"Search\": 78.6,\n",
      "    \"Email\": 11.6\n",
      "  },\n",
      "  \"analytic_last_click_pct\": {\n",
      "    \"Display\": 9.8,\n",
      "    \"Search\": 78.6,\n",
      "    \"Email\": 11.6\n",
      "  },\n",
      "  \"markov_pct\": {\n",
      "    \"Display\": 17.2,\n",
      "    \"Search\": 82.8,\n",
      "    \"Email\": 0.0\n",
      "  },\n",
      "  \"removal_effect\": {\n",
      "    \"Display\": 0.0348,\n",
      "    \"Search\": 0.1676,\n",
      "    \"Email\": 0.0\n",
      "  },\n",
      "  \"removal_effect_unclipped\": {\n",
      "    \"Display\": 0.0348,\n",
      "    \"Search\": 0.1676,\n",
      "    \"Email\": -0.0526\n",
      "  },\n",
      "  \"markov_band\": {\n",
      "    \"Display\": [\n",
      "      17.2036,\n",
      "      53.2086\n",
      "    ],\n",
      "    \"Search\": [\n",
      "      82.7964,\n",
      "      87.952\n",
      "    ],\n",
      "    \"Email\": [\n",
      "      0.0,\n",
      "      30.3845\n",
      "    ]\n",
      "  }\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "# ---- Emit data/checks.json (widget authority) and worked_example.json ----\n",
    "def T_to_dict(T):\n",
    "    \"\"\"Nested {from: {to: prob}} for transient rows, dropping ~0 entries.\"\"\"\n",
    "    out = {}\n",
    "    for i in TRANSIENT:\n",
    "        row = {}\n",
    "        for j in range(len(STATES)):\n",
    "            if T[i, j] > 1e-9:\n",
    "                row[STATES[j]] = round(float(T[i, j]), 9)\n",
    "        out[STATES[i]] = row\n",
    "    return out\n",
    "\n",
    "\n",
    "def build_T(spec):\n",
    "    \"\"\"Build a full 6x6 T from a {from: {to: prob}} spec; rows must sum to 1.\"\"\"\n",
    "    T = np.zeros((6, 6))\n",
    "    for frm, row in spec.items():\n",
    "        for to, p in row.items():\n",
    "            T[IDX[frm], IDX[to]] = p\n",
    "    T[IDX[\"CONV\"], IDX[\"CONV\"]] = 1.0\n",
    "    T[IDX[\"NULL\"], IDX[\"NULL\"]] = 1.0\n",
    "    bad = [f for f in spec if abs(T[IDX[f]].sum() - 1.0) > 1e-6]\n",
    "    assert not bad, f\"rows not summing to 1: {bad}\"\n",
    "    return T\n",
    "\n",
    "\n",
    "def expected_block(T):\n",
    "    return {\n",
    "        \"p_conv_full\": round(float(baseline_conversion(T)), 6),\n",
    "        \"removal_effect\": {c: round(float(removal_effects(T)[c]), 6) for c in CHANNELS},\n",
    "        \"credit_pct\": {c: round(float(markov_credit(T)[c]), 4) for c in CHANNELS},\n",
    "        \"lastclick_pct\": {c: round(float(analytic_last_click(T)[c]), 4) for c in CHANNELS},\n",
    "    }\n",
    "\n",
    "\n",
    "# Three clean, hand-specified matrices plus the fitted one from the corpus above.\n",
    "PINNED_SPECS = {\n",
    "    \"display-primes-search\": {\n",
    "        \"START\":   {\"Display\": 0.55, \"Search\": 0.10, \"Email\": 0.35},\n",
    "        \"Display\": {\"Search\": 0.65, \"CONV\": 0.05, \"NULL\": 0.30},\n",
    "        \"Search\":  {\"CONV\": 0.45, \"Email\": 0.05, \"NULL\": 0.50},\n",
    "        \"Email\":   {\"Search\": 0.30, \"CONV\": 0.10, \"NULL\": 0.60},\n",
    "    },\n",
    "    \"even-mix\": {\n",
    "        \"START\":   {\"Display\": 0.34, \"Search\": 0.33, \"Email\": 0.33},\n",
    "        \"Display\": {\"Search\": 0.20, \"Email\": 0.20, \"CONV\": 0.20, \"NULL\": 0.40},\n",
    "        \"Search\":  {\"Display\": 0.20, \"Email\": 0.20, \"CONV\": 0.20, \"NULL\": 0.40},\n",
    "        \"Email\":   {\"Display\": 0.20, \"Search\": 0.20, \"CONV\": 0.20, \"NULL\": 0.40},\n",
    "    },\n",
    "    \"email-assist\": {\n",
    "        \"START\":   {\"Display\": 0.25, \"Search\": 0.30, \"Email\": 0.45},\n",
    "        \"Display\": {\"Search\": 0.30, \"Email\": 0.15, \"CONV\": 0.10, \"NULL\": 0.45},\n",
    "        \"Search\":  {\"CONV\": 0.40, \"Email\": 0.10, \"NULL\": 0.50},\n",
    "        \"Email\":   {\"Search\": 0.40, \"Display\": 0.10, \"CONV\": 0.20, \"NULL\": 0.30},\n",
    "    },\n",
    "}\n",
    "\n",
    "def make_set(name, T):\n",
    "    \"\"\"Store T exactly as the widget will read it, and compute the expected\n",
    "    outputs from that same stored matrix so checks.json is self-consistent.\"\"\"\n",
    "    stored = T_to_dict(T)\n",
    "    T_roundtrip = build_T(stored)          # the exact matrix the widget reconstructs\n",
    "    return {\"name\": name, \"channels\": CHANNELS,\n",
    "            \"T\": stored, \"expected\": expected_block(T_roundtrip)}\n",
    "\n",
    "\n",
    "sets = [make_set(name, build_T(spec)) for name, spec in PINNED_SPECS.items()]\n",
    "# The fitted-from-corpus matrix, so the widget can reproduce the worked example.\n",
    "sets.append(make_set(\"fitted-example\", T_hat))\n",
    "\n",
    "checks = {\n",
    "    \"method\": \"Markov chain attribution (removal effect)\",\n",
    "    \"states\": STATES,\n",
    "    \"transient\": [\"START\", \"Display\", \"Search\", \"Email\"],\n",
    "    \"absorbing\": [\"CONV\", \"NULL\"],\n",
    "    \"tolerance\": 1e-4,\n",
    "    \"note\": \"Given the transition matrix T, the widget must reproduce p_conv_full, \"\n",
    "            \"removal_effect and credit_pct within tolerance.\",\n",
    "    \"sets\": sets,\n",
    "}\n",
    "(DATA / \"checks.json\").write_text(json.dumps(checks, indent=2), encoding=\"utf-8\")\n",
    "print(\"wrote data/checks.json with\", len(sets), \"pinned sets\")\n",
    "\n",
    "# Worked-example numbers, so the article quotes computed values (no hand-typing).\n",
    "worked = {\n",
    "    \"n_journeys\": N_JOURNEYS,\n",
    "    \"n_converted\": int(n_conv),\n",
    "    \"conversion_rate_pct\": round(100 * n_conv / N_JOURNEYS, 1),\n",
    "    \"single_touch_pct\": round(100 * n_single / N_JOURNEYS, 1),\n",
    "    \"baseline_conversion_probability\": round(float(p_full), 3),\n",
    "    \"last_click_pct\": {c: round(lc[c], 1) for c in CHANNELS},\n",
    "    \"analytic_last_click_pct\": {c: round(analytic_last_click(T_hat)[c], 1) for c in CHANNELS},\n",
    "    \"markov_pct\": {c: round(mk[c], 1) for c in CHANNELS},\n",
    "    \"removal_effect\": {c: round(raw[c], 4) for c in CHANNELS},\n",
    "    \"removal_effect_unclipped\": {c: round(raw_unclipped[c], 4) for c in CHANNELS},\n",
    "}\n",
    "# Credit as a range: the two substitution poles, computed (never hand-typed).\n",
    "worked[\"markov_band\"] = {c: list(substitution_band(T_hat)[c]) for c in CHANNELS}\n",
    "(DATA / \"worked_example.json\").write_text(json.dumps(worked, indent=2), encoding=\"utf-8\")\n",
    "print(\"wrote data/worked_example.json\")\n",
    "print(json.dumps(worked, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37464c3c",
   "metadata": {},
   "source": [
    "## Run it on your own data\n",
    "\n",
    "Everything above is synthetic. To run the same attribution on **your** journeys,\n",
    "you need a CSV of conversion **paths**. This is the format the widely-used\n",
    "open-source attribution tools accept, and it keeps your raw event data on your\n",
    "machine — you aggregate to paths first, then feed the aggregate here.\n",
    "\n",
    "**Column contract** (three columns, header row required):\n",
    "\n",
    "| column | meaning | example |\n",
    "|---|---|---|\n",
    "| `path` | channels in order, separated by ` > ` | `Display > Search > Email` |\n",
    "| `conversions` | how many journeys followed this path **and converted** | `12` |\n",
    "| `non_conversions` | how many followed this path and **did not** convert | `40` |\n",
    "\n",
    "One example row: `Display > Search, 30, 120` means 30 people went\n",
    "Display then Search and bought, and 120 more went Display then Search and did not.\n",
    "\n",
    "The loader below validates the columns, builds the transition matrix, runs\n",
    "last-click and Markov, and — importantly — **warns in plain words when your data\n",
    "is too thin to trust**, instead of printing a confident number it cannot\n",
    "support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "37e1da1d",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.328001Z",
     "iopub.execute_input": "2026-07-18T11:44:30.328001Z",
     "shell.execute_reply": "2026-07-18T11:44:30.350611Z",
     "iopub.status.idle": "2026-07-18T11:44:30.350611Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Loader ready. Use attribute_paths(csv_text) or set YOUR_CSV_PATH below.\n"
     ]
    }
   ],
   "source": [
    "# ---- Load + attribute a paths CSV (runs entirely locally) ---------------\n",
    "REQUIRED_COLUMNS = [\"path\", \"conversions\", \"non_conversions\"]\n",
    "\n",
    "# Rules-of-thumb for \"is there enough data\" — general statistical caution for a\n",
    "# public notebook, not tuned platform settings. Adjust to taste.\n",
    "MIN_CONVERSIONS      = 300   # fewer converting journeys -> estimates are noisy\n",
    "MIN_CHANNEL_JOURNEYS = 30    # a channel seen in fewer journeys -> unreliable credit\n",
    "MAX_SINGLE_TOUCH_PCT = 60    # more single-touch than this -> likely broken journey stitching\n",
    "MAX_CONVERSION_RATE_PCT = 90 # more journeys converting than this -> the flag is probably customer-level, not per-session\n",
    "\n",
    "\n",
    "class DataProblem(Exception):\n",
    "    \"\"\"Raised with a plain-words message when the input can't be attributed.\"\"\"\n",
    "\n",
    "\n",
    "def read_paths_csv(text):\n",
    "    \"\"\"Parse CSV text into rows, with friendly errors for a wrong shape.\"\"\"\n",
    "    reader = csv.DictReader(io.StringIO(text))\n",
    "    if reader.fieldnames is None:\n",
    "        raise DataProblem(\"The file is empty - expected a header row with \"\n",
    "                          f\"columns: {', '.join(REQUIRED_COLUMNS)}.\")\n",
    "    have = [c.strip() for c in reader.fieldnames]\n",
    "    missing = [c for c in REQUIRED_COLUMNS if c not in have]\n",
    "    if missing:\n",
    "        raise DataProblem(\n",
    "            \"Missing column(s): \" + \", \".join(missing) + \".\\n\"\n",
    "            f\"Expected exactly these columns: {', '.join(REQUIRED_COLUMNS)}.\\n\"\n",
    "            f\"Found instead: {', '.join(have)}.\")\n",
    "    rows = []\n",
    "    for n, r in enumerate(reader, start=2):  # row 1 is the header\n",
    "        path = (r.get(\"path\") or \"\").strip()\n",
    "        if not path:\n",
    "            continue\n",
    "        try:\n",
    "            conv = int(float(r[\"conversions\"]))\n",
    "            nonc = int(float(r[\"non_conversions\"]))\n",
    "        except (ValueError, TypeError):\n",
    "            raise DataProblem(\n",
    "                f\"Row {n}: conversions and non_conversions must be whole numbers, \"\n",
    "                f\"got conversions={r.get('conversions')!r}, \"\n",
    "                f\"non_conversions={r.get('non_conversions')!r}.\")\n",
    "        if conv < 0 or nonc < 0:\n",
    "            raise DataProblem(f\"Row {n}: counts cannot be negative.\")\n",
    "        channels = [c.strip() for c in path.split(\">\") if c.strip()]\n",
    "        if not channels:\n",
    "            raise DataProblem(f\"Row {n}: path has no channels.\")\n",
    "        rows.append({\"channels\": channels, \"conversions\": conv, \"non_conversions\": nonc})\n",
    "    if not rows:\n",
    "        raise DataProblem(\"No usable rows found under the header.\")\n",
    "    return rows\n",
    "\n",
    "\n",
    "def attribute_paths(text, verbose=True):\n",
    "    \"\"\"Full pipeline on a paths CSV: build T, run both methods, warn on thin data.\"\"\"\n",
    "    rows = read_paths_csv(text)\n",
    "\n",
    "    channel_names = sorted({c for r in rows for c in r[\"channels\"]})\n",
    "    states = [\"START\"] + channel_names + [\"CONV\", \"NULL\"]\n",
    "    idx = {s: i for i, s in enumerate(states)}\n",
    "    transient = [idx[\"START\"]] + [idx[c] for c in channel_names]\n",
    "    absorbing = [idx[\"CONV\"], idx[\"NULL\"]]\n",
    "    n = len(states)\n",
    "\n",
    "    counts = np.zeros((n, n))\n",
    "    total_conv = total_nonconv = 0\n",
    "    single_touch = 0\n",
    "    journeys_with_channel = Counter()\n",
    "    last_click = Counter()\n",
    "\n",
    "    for r in rows:\n",
    "        chans = r[\"channels\"]\n",
    "        for c in set(chans):\n",
    "            journeys_with_channel[c] += r[\"conversions\"] + r[\"non_conversions\"]\n",
    "        if len(chans) == 1:\n",
    "            single_touch += r[\"conversions\"] + r[\"non_conversions\"]\n",
    "        # converting journeys\n",
    "        if r[\"conversions\"]:\n",
    "            chain = [\"START\"] + chans + [\"CONV\"]\n",
    "            for a, b in zip(chain[:-1], chain[1:]):\n",
    "                counts[idx[a], idx[b]] += r[\"conversions\"]\n",
    "            last_click[chans[-1]] += r[\"conversions\"]\n",
    "            total_conv += r[\"conversions\"]\n",
    "        # non-converting journeys\n",
    "        if r[\"non_conversions\"]:\n",
    "            chain = [\"START\"] + chans + [\"NULL\"]\n",
    "            for a, b in zip(chain[:-1], chain[1:]):\n",
    "                counts[idx[a], idx[b]] += r[\"non_conversions\"]\n",
    "            total_nonconv += r[\"non_conversions\"]\n",
    "\n",
    "    T = np.zeros((n, n))\n",
    "    for i in range(n):\n",
    "        s = counts[i].sum()\n",
    "        if s > 0:\n",
    "            T[i] = counts[i] / s\n",
    "    T[idx[\"CONV\"], idx[\"CONV\"]] = 1.0\n",
    "    T[idx[\"NULL\"], idx[\"NULL\"]] = 1.0\n",
    "\n",
    "    # --- attribution on this arbitrary-size chain (generalised engine) ---\n",
    "    def p_conv(Tm, tr, ab):\n",
    "        Q = Tm[np.ix_(tr, tr)]\n",
    "        R = Tm[np.ix_(tr, ab)]\n",
    "        return (np.linalg.inv(np.eye(len(tr)) - Q) @ R)[0, 0]\n",
    "\n",
    "    def without(Tm, ch):\n",
    "        Tr = Tm.astype(float).copy()\n",
    "        kept = [i for i in transient if i != ch]\n",
    "        for i in kept:\n",
    "            sc = 1.0 - Tm[i, ch]\n",
    "            if sc <= 1e-12:\n",
    "                others = [j for j in range(n) if j != ch]\n",
    "                Tr[i, :] = 0.0\n",
    "                for j in others:\n",
    "                    Tr[i, j] = 1.0 / len(others)\n",
    "            else:\n",
    "                Tr[i, :] = Tm[i, :] / sc\n",
    "                Tr[i, ch] = 0.0\n",
    "        return p_conv(Tr, kept, absorbing)\n",
    "\n",
    "    p0 = p_conv(T, transient, absorbing)\n",
    "    raw = {c: max(0.0, p0 - without(T, idx[c])) for c in channel_names}\n",
    "    raw_unclipped = {c: p0 - without(T, idx[c]) for c in channel_names}\n",
    "    tot = sum(raw.values())\n",
    "    markov = {c: (100 * raw[c] / tot if tot else 0.0) for c in channel_names}\n",
    "    lc_tot = sum(last_click.values())\n",
    "    lastc = {c: (100 * last_click.get(c, 0) / lc_tot if lc_tot else 0.0) for c in channel_names}\n",
    "\n",
    "    # --- sufficiency + integrity warnings, in plain words ---\n",
    "    warnings = []\n",
    "    total_journeys = total_conv + total_nonconv\n",
    "    if total_conv < MIN_CONVERSIONS:\n",
    "        warnings.append(\n",
    "            f\"Only {total_conv} converting journeys (rule of thumb: at least \"\n",
    "            f\"{MIN_CONVERSIONS}). Treat every credit share as directional, not exact.\")\n",
    "    thin = [c for c in channel_names\n",
    "            if journeys_with_channel[c] < MIN_CHANNEL_JOURNEYS]\n",
    "    if thin:\n",
    "        warnings.append(\n",
    "            \"These channels appear in very few journeys, so their credit is \"\n",
    "            f\"unreliable: {', '.join(thin)} \"\n",
    "            f\"(fewer than {MIN_CHANNEL_JOURNEYS} journeys each).\")\n",
    "    st_pct = 100 * single_touch / total_journeys if total_journeys else 0\n",
    "    if st_pct > MAX_SINGLE_TOUCH_PCT:\n",
    "        warnings.append(\n",
    "            f\"{st_pct:.0f}% of journeys have a single touch. That usually means \"\n",
    "            \"journeys are being split apart (cookie loss, cross-device) rather \"\n",
    "            \"than customers really touching one channel - attribution will lean \"\n",
    "            \"toward whatever closes.\")\n",
    "    conv_rate = 100 * total_conv / total_journeys if total_journeys else 0\n",
    "    if conv_rate > MAX_CONVERSION_RATE_PCT:\n",
    "        warnings.append(\n",
    "            f\"{conv_rate:.0f}% of journeys converted - implausibly high. Check that your \"\n",
    "            \"conversion flag marks the session where the purchase happened, not 'this customer \"\n",
    "            \"ever bought'; the second kind marks every session as converted and makes the \"\n",
    "            \"result meaningless.\")\n",
    "    neg = {c: v for c, v in raw_unclipped.items() if v < -1e-9}\n",
    "    if neg:\n",
    "        warnings.append(\n",
    "            \"Removing \" + \", \".join(neg) + \" did not lower the modelled conversion \"\n",
    "            \"rate (it slightly rose), so their removal effect was clipped to zero and \"\n",
    "            \"they earn no Markov credit. Their visitors can still reach a sale through \"\n",
    "            \"the other channels - genuine substitutability if those channels are well \"\n",
    "            \"represented, or too little data if they are thin. Zero is not proof the \"\n",
    "            \"channel is worthless.\")\n",
    "\n",
    "    report = {\n",
    "        \"channels\": channel_names,\n",
    "        \"total_conversions\": total_conv,\n",
    "        \"total_journeys\": total_journeys,\n",
    "        \"single_touch_pct\": round(st_pct, 1),\n",
    "        \"baseline_conversion_probability\": round(float(p0), 4),\n",
    "        \"last_click_pct\": {c: round(lastc[c], 1) for c in channel_names},\n",
    "        \"markov_pct\": {c: round(markov[c], 1) for c in channel_names},\n",
    "        \"warnings\": warnings,\n",
    "    }\n",
    "    if verbose:\n",
    "        print(f\"Channels: {', '.join(channel_names)}\")\n",
    "        print(f\"Converting journeys: {total_conv:,}   Total journeys: {total_journeys:,}\"\n",
    "              f\"   Single-touch: {st_pct:.0f}%\")\n",
    "        print(f\"Baseline P(convert): {p0:.4f}\\n\")\n",
    "        print(f\"{'channel':<12}{'last-click %':>14}{'Markov %':>12}\")\n",
    "        for c in channel_names:\n",
    "            print(f\"{c:<12}{lastc[c]:>13.1f}%{markov[c]:>11.1f}%\")\n",
    "        if warnings:\n",
    "            print(\"\\n[!] Data warnings:\")\n",
    "            for w in warnings:\n",
    "                print(\"  - \" + w)\n",
    "        else:\n",
    "            print(\"\\nNo data-sufficiency warnings.\")\n",
    "    return report\n",
    "\n",
    "\n",
    "print(\"Loader ready. Use attribute_paths(csv_text) or set YOUR_CSV_PATH below.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56689962",
   "metadata": {},
   "source": [
    "### A worked example on a second (independent) synthetic file\n",
    "\n",
    "To show the loader works — and reproduces a sensible answer — here is a small\n",
    "paths CSV built by hand, where journeys mostly *start* at Display or Email and\n",
    "Search does the closing. Expect the same shape of result as the main example:\n",
    "Display gains a little versus last-click, Email's credit collapses because its\n",
    "visitors can still reach a sale through the other channels, and Search dominates.\n",
    "The Email \"removal effect clipped to zero\" note is the honest behaviour from the\n",
    "article, not an error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "82d2abda",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.350611Z",
     "iopub.execute_input": "2026-07-18T11:44:30.350611Z",
     "shell.execute_reply": "2026-07-18T11:44:30.358979Z",
     "iopub.status.idle": "2026-07-18T11:44:30.358979Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Channels: Display, Email, Search\n",
      "Converting journeys: 1,065   Total journeys: 4,045   Single-touch: 33%\n",
      "Baseline P(convert): 0.2633\n",
      "\n",
      "channel       last-click %    Markov %\n",
      "Display               4.2%        5.1%\n",
      "Email                11.3%        0.0%\n",
      "Search               84.5%       94.9%\n",
      "\n",
      "[!] Data warnings:\n",
      "  - Removing Email did not lower the modelled conversion rate (it slightly rose), so their removal effect was clipped to zero and they earn no Markov credit. Their visitors can still reach a sale through the other channels - genuine substitutability if those channels are well represented, or too little data if they are thin. Zero is not proof the channel is worthless.\n"
     ]
    }
   ],
   "source": [
    "EXAMPLE_CSV = \"\"\"path,conversions,non_conversions\n",
    "Display > Search,640,1160\n",
    "Display > Search > Email,80,150\n",
    "Display,45,720\n",
    "Email > Search,190,360\n",
    "Email,40,430\n",
    "Email > Display > Search,45,80\n",
    "Search,25,80\n",
    "\"\"\"\n",
    "\n",
    "report_good = attribute_paths(EXAMPLE_CSV)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50ddbae0",
   "metadata": {},
   "source": [
    "### The loader fails honestly\n",
    "\n",
    "Two things that should *not* silently produce a number: a file with the wrong\n",
    "columns, and a file with far too little data. Both are handled below — a clear\n",
    "message, never a confident-looking but meaningless credit table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "694fa00f",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.358979Z",
     "iopub.execute_input": "2026-07-18T11:44:30.358979Z",
     "iopub.status.idle": "2026-07-18T11:44:30.366799Z",
     "shell.execute_reply": "2026-07-18T11:44:30.366799Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Caught DataProblem (as intended):\n",
      "Missing column(s): path, conversions, non_conversions.\n",
      "Expected exactly these columns: path, conversions, non_conversions.\n",
      "Found instead: channel, clicks.\n"
     ]
    }
   ],
   "source": [
    "# (1) Wrong columns -> a friendly, specific error.\n",
    "BROKEN_CSV = \"\"\"channel,clicks\n",
    "Display,120\n",
    "Search,300\n",
    "\"\"\"\n",
    "try:\n",
    "    attribute_paths(BROKEN_CSV)\n",
    "except DataProblem as e:\n",
    "    print(\"Caught DataProblem (as intended):\\n\" + str(e))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b1d54026",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.370811Z",
     "iopub.execute_input": "2026-07-18T11:44:30.370811Z",
     "shell.execute_reply": "2026-07-18T11:44:30.375573Z",
     "iopub.status.idle": "2026-07-18T11:44:30.375573Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Channels: Display, Email, Search\n",
      "Converting journeys: 11   Total journeys: 55   Single-touch: 53%\n",
      "Baseline P(convert): 0.2000\n",
      "\n",
      "channel       last-click %    Markov %\n",
      "Display               0.0%      100.0%\n",
      "Email                 9.1%        0.0%\n",
      "Search               90.9%        0.0%\n",
      "\n",
      "[!] Data warnings:\n",
      "  - Only 11 converting journeys (rule of thumb: at least 300). Treat every credit share as directional, not exact.\n",
      "  - These channels appear in very few journeys, so their credit is unreliable: Display, Email (fewer than 30 journeys each).\n",
      "  - Removing Email, Search did not lower the modelled conversion rate (it slightly rose), so their removal effect was clipped to zero and they earn no Markov credit. Their visitors can still reach a sale through the other channels - genuine substitutability if those channels are well represented, or too little data if they are thin. Zero is not proof the channel is worthless.\n"
     ]
    }
   ],
   "source": [
    "# (2) Correct shape, but far too thin -> it runs, and it warns loudly.\n",
    "THIN_CSV = \"\"\"path,conversions,non_conversions\n",
    "Display > Search,6,20\n",
    "Search,4,15\n",
    "Email,1,9\n",
    "\"\"\"\n",
    "report_thin = attribute_paths(THIN_CSV)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "raw-to-paths-md",
   "metadata": {},
   "source": [
    "## Getting your own data into this format\n",
    "\n",
    "The loader above wants a **paths** table. Almost nobody has one lying around.\n",
    "What you *can* usually export is a **raw touch log**: one row per marketing\n",
    "touch — who, when, which channel, and whether they eventually bought. Turning\n",
    "that into paths (grouping each person's touches into sessions, ordering them,\n",
    "collapsing repeats, and counting identical journeys) is the tedious step most\n",
    "tutorials skip.\n",
    "\n",
    "So here is a helper that does it. Give it rows of `person_id`, `timestamp`,\n",
    "`channel`, `is_conversion`; it sessionises by 30-minute inactivity, orders the\n",
    "touches, starts a fresh journey after each purchase, collapses consecutive\n",
    "repeats, and returns exactly the paths CSV the loader wants.\n",
    "\n",
    "**What it cannot do is fetch the raw touches for you** — that part is on you, and\n",
    "the section after this shows how. This consolidation is one of the things the\n",
    "platform automates end to end; the version here is deliberately short and\n",
    "readable."
   ]
  },
  {
   "cell_type": "code",
   "id": "consolidate-code",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.378819Z",
     "iopub.execute_input": "2026-07-18T11:44:30.378819Z",
     "shell.execute_reply": "2026-07-18T11:44:30.396622Z",
     "iopub.status.idle": "2026-07-18T11:44:30.396622Z"
    }
   },
   "execution_count": 13,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Consolidator ready. Use consolidate_touchpoints(rows) -> paths CSV.\n"
     ]
    }
   ],
   "source": [
    "# ---- Raw touch log -> paths CSV (the step before attribute_paths) -------\n",
    "# Input rows: dicts with person_id, timestamp (epoch SECONDS or an ISO-8601\n",
    "# string), channel, is_conversion (0/1, set on the touch whose session bought).\n",
    "# Common alternative column names (customer_id, event_time, source_channel,\n",
    "# purchased, ...) are auto-detected, so a non-GA4 export usually just works.\n",
    "TOUCH_COLUMNS = [\"person_id\", \"timestamp\", \"channel\", \"is_conversion\"]\n",
    "\n",
    "_COLUMN_ALIASES = {\n",
    "    \"person_id\":     [\"person_id\", \"user_id\", \"user_pseudo_id\", \"customer_id\", \"client_id\", \"visitor_id\", \"id\"],\n",
    "    \"timestamp\":     [\"timestamp\", \"event_timestamp\", \"event_time\", \"datetime\", \"occurred_at\", \"time\", \"date\", \"ts\"],\n",
    "    \"channel\":       [\"channel\", \"source_channel\", \"default_channel_group\", \"channel_group\", \"source_medium\", \"source\", \"medium\"],\n",
    "    \"is_conversion\": [\"is_conversion\", \"converted\", \"conversion\", \"is_purchase\", \"purchased\", \"purchase\", \"bought\"],\n",
    "}\n",
    "\n",
    "\n",
    "def _resolve_columns(fieldnames):\n",
    "    \"\"\"Map the four fields the consolidator needs onto whatever the file calls them.\"\"\"\n",
    "    have = {str(f).strip().lower(): f for f in fieldnames}\n",
    "    resolved = {}\n",
    "    for want, aliases in _COLUMN_ALIASES.items():\n",
    "        for a in aliases:\n",
    "            if a in have:\n",
    "                resolved[want] = have[a]\n",
    "                break\n",
    "    missing = [w for w in _COLUMN_ALIASES if w not in resolved]\n",
    "    if missing:\n",
    "        raise DataProblem(\n",
    "            \"Could not find a touch-log column for: \" + \", \".join(missing) + \".\\n\"\n",
    "            \"A raw touch log needs one column each for who (a person/customer id), when (a \"\n",
    "            \"timestamp), which channel, and whether that touch's session converted (0/1).\\n\"\n",
    "            f\"Your columns are: {', '.join(map(str, fieldnames))}.\\n\"\n",
    "            \"Rename them to person_id, timestamp, channel, is_conversion - or to names the \"\n",
    "            \"loader recognises, e.g. customer_id, event_time, source_channel, purchased.\")\n",
    "    return resolved\n",
    "\n",
    "\n",
    "def _to_seconds(v):\n",
    "    \"\"\"Accept an epoch value in seconds, or an ISO-8601 timestamp string.\"\"\"\n",
    "    try:\n",
    "        return float(v)\n",
    "    except (TypeError, ValueError):\n",
    "        return datetime.fromisoformat(str(v).replace(\"Z\", \"+00:00\")).timestamp()\n",
    "\n",
    "\n",
    "def _as_flag(v):\n",
    "    \"\"\"A conversion flag as 0/1, tolerant of \"1\"/\"0\", true/false, yes/no.\"\"\"\n",
    "    return 1 if str(v).strip().lower() in (\"1\", \"true\", \"yes\", \"y\", \"t\") else 0\n",
    "\n",
    "\n",
    "def _collapse_repeats(seq):\n",
    "    out = []\n",
    "    for c in seq:\n",
    "        if not out or out[-1] != c:\n",
    "            out.append(c)\n",
    "    return tuple(out)\n",
    "\n",
    "\n",
    "def consolidate_touchpoints(rows, session_gap_minutes=30):\n",
    "    \"\"\"Turn a raw touch log into a paths CSV (path,conversions,non_conversions).\n",
    "\n",
    "    Each person's touches are ordered and split into sessions by >gap inactivity;\n",
    "    one channel per session (its first touch = the acquisition source). A journey\n",
    "    closes at the session that converted -- touches after a purchase begin a new\n",
    "    journey -- and identical paths are counted. Column names are auto-detected, so\n",
    "    common non-GA4 exports work without renaming.\n",
    "    \"\"\"\n",
    "    rows = list(rows)\n",
    "    if not rows:\n",
    "        raise DataProblem(\"The touch log has no rows.\")\n",
    "    col = _resolve_columns(rows[0].keys())\n",
    "    gap = session_gap_minutes * 60\n",
    "    by_person = defaultdict(list)\n",
    "    for r in rows:\n",
    "        by_person[r[col[\"person_id\"]]].append(\n",
    "            (_to_seconds(r[col[\"timestamp\"]]), str(r[col[\"channel\"]]), _as_flag(r[col[\"is_conversion\"]])))\n",
    "\n",
    "    journeys = []\n",
    "    for events in by_person.values():\n",
    "        events.sort(key=lambda e: e[0])\n",
    "        sessions, cur, last_t = [], None, None\n",
    "        for t, channel, conv in events:\n",
    "            if cur is None or (t - last_t) > gap:\n",
    "                cur = {\"channel\": channel, \"conv\": conv}\n",
    "                sessions.append(cur)\n",
    "            else:\n",
    "                cur[\"conv\"] = cur[\"conv\"] or conv\n",
    "            last_t = t\n",
    "        path = []\n",
    "        for s in sessions:\n",
    "            path.append(s[\"channel\"])\n",
    "            if s[\"conv\"]:\n",
    "                journeys.append((_collapse_repeats(path), True))\n",
    "                path = []\n",
    "        if path:\n",
    "            journeys.append((_collapse_repeats(path), False))\n",
    "\n",
    "    agg = defaultdict(lambda: [0, 0])\n",
    "    for channels, converted in journeys:\n",
    "        if channels:\n",
    "            agg[channels][0 if converted else 1] += 1\n",
    "\n",
    "    buf = io.StringIO()\n",
    "    writer = csv.writer(buf)\n",
    "    writer.writerow([\"path\", \"conversions\", \"non_conversions\"])\n",
    "    for channels in sorted(agg, key=lambda k: (-sum(agg[k]), k)):\n",
    "        conv, nonconv = agg[channels]\n",
    "        writer.writerow([\" > \".join(channels), conv, nonconv])\n",
    "    return buf.getvalue()\n",
    "\n",
    "\n",
    "print(\"Consolidator ready. Use consolidate_touchpoints(rows) -> paths CSV.\")"
   ]
  },
  {
   "cell_type": "code",
   "id": "consolidate-demo",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.398369Z",
     "iopub.execute_input": "2026-07-18T11:44:30.398369Z",
     "shell.execute_reply": "2026-07-18T11:44:30.471569Z",
     "iopub.status.idle": "2026-07-18T11:44:30.471569Z"
    }
   },
   "execution_count": 14,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Exploded 4,000 journeys into 5,962 raw touches. First few:\n",
      "   {'person_id': 0, 'timestamp': 9449049.0, 'channel': 'Display', 'is_conversion': 0}\n",
      "   {'person_id': 1, 'timestamp': 6841799.0, 'channel': 'Display', 'is_conversion': 1}\n",
      "   {'person_id': 2, 'timestamp': 5782924.0, 'channel': 'Display', 'is_conversion': 0}\n",
      "   {'person_id': 3, 'timestamp': 8336510.0, 'channel': 'Email', 'is_conversion': 0}\n",
      "\n",
      "Consolidated back into paths, then attributed:\n",
      "\n",
      "Channels: Display, Email, Search\n",
      "Converting journeys: 1,281   Total journeys: 4,000   Single-touch: 54%\n",
      "Baseline P(convert): 0.3202\n",
      "\n",
      "channel       last-click %    Markov %\n",
      "Display               9.8%       17.2%\n",
      "Email                11.6%        0.0%\n",
      "Search               78.6%       82.8%\n",
      "\n",
      "[!] Data warnings:\n",
      "  - Removing Email did not lower the modelled conversion rate (it slightly rose), so their removal effect was clipped to zero and they earn no Markov credit. Their visitors can still reach a sale through the other channels - genuine substitutability if those channels are well represented, or too little data if they are thin. Zero is not proof the channel is worthless.\n"
     ]
    }
   ],
   "source": [
    "# ---- Demo: raw touches -> consolidate -> attribute (reproduces the headline) --\n",
    "# Explode the synthetic journeys from earlier into a raw touch log with\n",
    "# timestamps, then rebuild the paths and attribute. Because the consolidation is\n",
    "# faithful, the credit matches the Display 17.2 / Search 82.8 / Email 0.0 above.\n",
    "def _journeys_to_touch_log(journeys, seed=7):\n",
    "    demo_rng = np.random.default_rng(seed)\n",
    "    rows = []\n",
    "    for person_id, (touches, converted) in enumerate(journeys):\n",
    "        t = float(demo_rng.integers(0, 10_000_000))\n",
    "        for k, channel in enumerate(touches):\n",
    "            bought = 1 if (converted and k == len(touches) - 1) else 0\n",
    "            rows.append({\"person_id\": person_id, \"timestamp\": t,\n",
    "                         \"channel\": channel, \"is_conversion\": bought})\n",
    "            t += int(demo_rng.integers(31, 5000)) * 60   # next touch = a new session\n",
    "    return rows\n",
    "\n",
    "\n",
    "touch_log = _journeys_to_touch_log(journeys)\n",
    "print(f\"Exploded {len(journeys):,} journeys into {len(touch_log):,} raw touches. First few:\")\n",
    "for _row in touch_log[:4]:\n",
    "    print(\"  \", _row)\n",
    "paths_from_touches = consolidate_touchpoints(touch_log)\n",
    "print(\"\\nConsolidated back into paths, then attributed:\\n\")\n",
    "_ = attribute_paths(paths_from_touches)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ga4-sql-md",
   "metadata": {},
   "source": [
    "### Getting the raw touches out of GA4 (BigQuery)\n",
    "\n",
    "The raw touch log has to come from somewhere. **No ad platform can give you the\n",
    "cross-channel sequence** — Google Ads sees only Google touches, Meta only Meta.\n",
    "The joined-up journey lives in your analytics. For most people that is\n",
    "**Google Analytics 4**, and specifically its free, event-level\n",
    "[BigQuery export](https://support.google.com/analytics/answer/9358801) — the GA4\n",
    "interface only shows aggregated path reports, not the per-person sequence.\n",
    "\n",
    "This query turns that export into one row per session, in the exact shape\n",
    "`consolidate_touchpoints` wants. Replace the property id and dates, adapt the\n",
    "channel `CASE` to your channels, and export the result as CSV:\n",
    "\n",
    "```sql\n",
    "-- GA4 -> one row per session, in the consolidate_touchpoints() format.\n",
    "SELECT\n",
    "  person_id,\n",
    "  timestamp,\n",
    "  CASE  -- reduced GA4 Default Channel Group; full logic: support.google.com/analytics/answer/9756891\n",
    "    WHEN REGEXP_CONTAINS(source, r'(?i)email') OR REGEXP_CONTAINS(medium, r'(?i)email') THEN 'Email'\n",
    "    WHEN REGEXP_CONTAINS(medium, r'(?i)^(.*cp.*|ppc|paid.*|retargeting)$')\n",
    "         AND REGEXP_CONTAINS(source, r'(?i)google|bing|yahoo') THEN 'Paid Search'\n",
    "    WHEN REGEXP_CONTAINS(medium, r'(?i)^(display|banner|cpm|interstitial)$') THEN 'Display'\n",
    "    WHEN medium = 'organic' THEN 'Organic Search'\n",
    "    WHEN REGEXP_CONTAINS(medium, r'(?i)^(social|social-network|sm)$') THEN 'Organic Social'\n",
    "    WHEN source = '(direct)' THEN 'Direct'\n",
    "    ELSE COALESCE(NULLIF(medium, ''), '(unknown)')\n",
    "  END AS channel,\n",
    "  is_conversion\n",
    "FROM (\n",
    "  SELECT\n",
    "    COALESCE(user_id, user_pseudo_id) AS person_id,          -- user_id stitches devices; else per-device\n",
    "    MIN(event_timestamp) / 1000000 AS timestamp,             -- micros -> epoch seconds\n",
    "    ANY_VALUE(session_traffic_source_last_click.manual_campaign.source) AS source,\n",
    "    ANY_VALUE(session_traffic_source_last_click.manual_campaign.medium) AS medium,\n",
    "    MAX(IF(event_name = 'purchase', 1, 0)) AS is_conversion  -- your key event(s)\n",
    "  FROM `analytics_<PROPERTY_ID>.events_*`\n",
    "  WHERE _TABLE_SUFFIX BETWEEN '20260101' AND '20260630'\n",
    "    AND _TABLE_SUFFIX NOT LIKE '%intraday%'                  -- exclude staging + Fresh Daily\n",
    "    AND _TABLE_SUFFIX NOT LIKE '%fresh%'\n",
    "  GROUP BY\n",
    "    person_id,\n",
    "    CONCAT(user_pseudo_id, (SELECT ep.value.int_value FROM UNNEST(event_params) ep\n",
    "                            WHERE ep.key = 'ga_session_id'))\n",
    ")\n",
    "ORDER BY person_id, timestamp\n",
    "```\n",
    "\n",
    "**Read these before you trust the output:**\n",
    "\n",
    "- **`user_pseudo_id` is one browser on one device.** It resets on cookie clearing\n",
    "  and Safari/ITP caps, and it never spans devices. Set a logged-in `user_id` to\n",
    "  stitch a person across devices; otherwise long journeys fragment into\n",
    "  single-touch ones (which the loader warns about).\n",
    "- **The export holds only *observed* events.** GA4's consent-modelled conversions\n",
    "  are [not in BigQuery](https://support.google.com/analytics/answer/11161109), so\n",
    "  your counts run lower than the GA4 UI — and this model computes attribution from\n",
    "  scratch, so it will **not** (and should not) reconcile to GA4's own attribution\n",
    "  report.\n",
    "- **Skip the freshest ~3 days** (data keeps arriving for ~72 hours) and, as above,\n",
    "  the `intraday`/`fresh` tables.\n",
    "- Impression-only and parameter-stripped paid clicks land as `Direct` or\n",
    "  `(unknown)`, so a GA4-only model under-credits view-through social and\n",
    "  over-credits Direct. Confirm the winners with an experiment before moving budget."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e36ecb34",
   "metadata": {},
   "source": [
    "### Your turn\n",
    "\n",
    "Two ways in:\n",
    "\n",
    "- **You already have a paths CSV** - columns `path, conversions, non_conversions`.\n",
    "  Set `YOUR_PATHS_CSV` below.\n",
    "- **You have a raw touch log** - one row per touch, with a person id, a time, a\n",
    "  channel, and a converted flag (e.g. the GA4 export above). Set `YOUR_TOUCHES_CSV`\n",
    "  and it is consolidated for you first. Column names can differ from\n",
    "  `person_id, timestamp, channel, is_conversion`; common ones like `customer_id`,\n",
    "  `event_time`, `source_channel`, `purchased` are recognised automatically.\n",
    "\n",
    "Not sure which you have? Open the file's header row: three columns of counts is a\n",
    "paths CSV; one row per touch is a touch log. Either way the file is read from disk\n",
    "locally and never sent anywhere."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "9f8f098f",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2026-07-18T11:44:30.475592Z",
     "iopub.execute_input": "2026-07-18T11:44:30.475592Z",
     "shell.execute_reply": "2026-07-18T11:44:30.482003Z",
     "iopub.status.idle": "2026-07-18T11:44:30.482003Z"
    }
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Set YOUR_PATHS_CSV (paths) or YOUR_TOUCHES_CSV (raw touch log) and re-run.\n",
      "paths columns:   path, conversions, non_conversions\n",
      "touches columns: person_id, timestamp, channel, is_conversion\n"
     ]
    }
   ],
   "source": [
    "YOUR_PATHS_CSV   = None   # already have paths?           e.g. \"my_paths.csv\"\n",
    "YOUR_TOUCHES_CSV = None   # have a raw touch log instead?  e.g. \"my_touches.csv\"\n",
    "\n",
    "if YOUR_TOUCHES_CSV:\n",
    "    raw = list(csv.DictReader(io.StringIO(Path(YOUR_TOUCHES_CSV).read_text(encoding=\"utf-8\"))))\n",
    "    print(f\"Consolidating {len(raw):,} raw touches into paths, then attributing:\\n\")\n",
    "    my_report = attribute_paths(consolidate_touchpoints(raw))\n",
    "elif YOUR_PATHS_CSV:\n",
    "    my_report = attribute_paths(Path(YOUR_PATHS_CSV).read_text(encoding=\"utf-8\"))\n",
    "else:\n",
    "    print(\"Set YOUR_PATHS_CSV (paths) or YOUR_TOUCHES_CSV (raw touch log) and re-run.\")\n",
    "    print(\"paths columns:  \", \", \".join([\"path\", \"conversions\", \"non_conversions\"]))\n",
    "    print(\"touches columns:\", \", \".join(TOUCH_COLUMNS))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5112ef80",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "*Method: Markov chain attribution with the removal effect. The absorbing-chain\n",
    "algebra (Fundamental Matrix `N = (I − Q)⁻¹`) is standard probability theory; the\n",
    "removal effect as an attribution mechanism is established in the marketing-science\n",
    "literature. This notebook uses synthetic data throughout the worked example and\n",
    "runs your own data only on your machine. It is a teaching tool, not financial\n",
    "advice.*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12.8",
   "mimetype": "text/x-python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "file_extension": ".py"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
