{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9c6bf9ba",
   "metadata": {},
   "source": [
    "# A1 · L2 — Look before you fit\n",
    "\n",
    "200 markets. In each one, a budget was split across TV, radio and newspaper, and a\n",
    "sales figure came back. Before fitting anything we ask four questions — what does the\n",
    "target look like, does the obvious predictor move with it, where is the money going\n",
    "today, and what else is worth a look.\n",
    "\n",
    "Units: media budgets are in **$ thousands**, sales in **thousands of units**.\n",
    "\n",
    "Every figure and number this notebook produces is what the lesson prints. Nothing in\n",
    "the lesson is typed by hand."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "017d2aa1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:58.830505Z",
     "iopub.status.busy": "2026-08-19T09:11:58.830243Z",
     "iopub.status.idle": "2026-08-19T09:11:59.117845Z",
     "shell.execute_reply": "2026-08-19T09:11:59.117566Z"
    }
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from _figkit import save_fig, record\n",
    "except ImportError:  # Colab — no repo, no problem\n",
    "    def save_fig(name, plot, **kw):\n",
    "        import matplotlib.pyplot as plt\n",
    "        fig, ax = plt.subplots(figsize=kw.get('figsize', (7, 4.2))); plot(ax); plt.show()\n",
    "    def record(key, value):\n",
    "        print(f'{key} = {value}'); return value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "214717a2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:59.119190Z",
     "iopub.status.busy": "2026-08-19T09:11:59.119086Z",
     "iopub.status.idle": "2026-08-19T09:11:59.305826Z",
     "shell.execute_reply": "2026-08-19T09:11:59.305557Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>tv_spend</th>\n",
       "      <th>radio_spend</th>\n",
       "      <th>newspaper_spend</th>\n",
       "      <th>sales</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>230.1</td>\n",
       "      <td>37.8</td>\n",
       "      <td>69.2</td>\n",
       "      <td>22.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>44.5</td>\n",
       "      <td>39.3</td>\n",
       "      <td>45.1</td>\n",
       "      <td>10.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>17.2</td>\n",
       "      <td>45.9</td>\n",
       "      <td>69.3</td>\n",
       "      <td>9.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>151.5</td>\n",
       "      <td>41.3</td>\n",
       "      <td>58.5</td>\n",
       "      <td>18.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>180.8</td>\n",
       "      <td>10.8</td>\n",
       "      <td>58.4</td>\n",
       "      <td>12.9</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   tv_spend  radio_spend  newspaper_spend  sales\n",
       "0     230.1         37.8             69.2   22.1\n",
       "1      44.5         39.3             45.1   10.4\n",
       "2      17.2         45.9             69.3    9.3\n",
       "3     151.5         41.3             58.5   18.5\n",
       "4     180.8         10.8             58.4   12.9"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "import pandas as pd\n",
    "\n",
    "# Local checkout first, the published copy otherwise — so this cell works\n",
    "# unchanged in Colab, where there is no repo. Searched upwards rather than\n",
    "# written as ../../../ so it does not depend on where jupyter was started.\n",
    "REL = 'public/datasets/a1-regression/advertising-media-mix.csv'\n",
    "here = Path.cwd()\n",
    "LOCAL = next((p / REL for p in [here, *here.parents] if (p / REL).exists()), None)\n",
    "CSV = LOCAL or 'https://raw.githubusercontent.com/Dr-Shashank-S-Sharma/expedify-ai-courses/main/datasets/a1-regression/advertising-media-mix.csv'\n",
    "\n",
    "df = pd.read_csv(CSV)\n",
    "CHANNELS = ['tv_spend', 'radio_spend', 'newspaper_spend']\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3767bd8",
   "metadata": {},
   "source": [
    "## 1. What does the thing we are predicting look like?\n",
    "\n",
    "Always the first question, and the one most often skipped. A regression predicts the\n",
    "*mean* — so the shape of the target decides whether the mean is a fair summary at all."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "43192a08",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:59.307192Z",
     "iopub.status.busy": "2026-08-19T09:11:59.307080Z",
     "iopub.status.idle": "2026-08-19T09:11:59.373699Z",
     "shell.execute_reply": "2026-08-19T09:11:59.373410Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n_markets = 200\n",
      "n_missing = 0\n",
      "median_sales = 12.9\n",
      "mean_sales = 14.0\n",
      "min_sales = 1.6\n",
      "max_sales = 27.0\n",
      "sales_skew = 0.41\n",
      "figure sales-distribution -> look-before-you-fit.sales-distribution.{light,dark}.svg\n"
     ]
    }
   ],
   "source": [
    "sales = df['sales']\n",
    "k = lambda v: f'{v:,.1f}'\n",
    "\n",
    "record('n_markets', len(df))\n",
    "record('n_missing', int(df.isna().sum().sum()))\n",
    "record('median_sales', k(sales.median()))\n",
    "record('mean_sales', k(sales.mean()))\n",
    "record('min_sales', k(sales.min()))\n",
    "record('max_sales', k(sales.max()))\n",
    "record('sales_skew', round(float(sales.skew()), 2))\n",
    "\n",
    "def plot(ax):\n",
    "    ax.hist(sales, bins=18, edgecolor='none')\n",
    "    ax.axvline(sales.mean(), linestyle='--', linewidth=1.4, color='#8a8a94')\n",
    "    ax.annotate('mean', xy=(sales.mean(), 0), xytext=(4, 4),\n",
    "                textcoords='offset points', fontsize=9, color='#8a8a94')\n",
    "    ax.set_xlabel('Sales (thousands of units)')\n",
    "    ax.set_ylabel('Markets')\n",
    "\n",
    "save_fig('sales-distribution', plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d7fe1fa",
   "metadata": {},
   "source": [
    "## 2. Does the obvious predictor move with it?\n",
    "\n",
    "TV takes the largest share of the budget, so it is the first thing anyone would reach\n",
    "for. Plot it before trusting it — a correlation is one number and several very\n",
    "different shapes produce the same one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a6a9131c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:59.374865Z",
     "iopub.status.busy": "2026-08-19T09:11:59.374777Z",
     "iopub.status.idle": "2026-08-19T09:11:59.429503Z",
     "shell.execute_reply": "2026-08-19T09:11:59.429266Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "corr_tv = 0.78\n",
      "corr_radio = 0.58\n",
      "corr_newspaper = 0.23\n",
      "spread_low_tv = 2.68\n",
      "spread_high_tv = 4.52\n",
      "figure sales-vs-tv -> look-before-you-fit.sales-vs-tv.{light,dark}.svg\n"
     ]
    }
   ],
   "source": [
    "#| caption: Does the obvious predictor move with sales?\n",
    "record('corr_tv', round(float(df['tv_spend'].corr(sales)), 2))\n",
    "record('corr_radio', round(float(df['radio_spend'].corr(sales)), 2))\n",
    "record('corr_newspaper', round(float(df['newspaper_spend'].corr(sales)), 2))\n",
    "\n",
    "# Does the cloud fan out, and which way? Claimed in prose often enough that it\n",
    "# is worth measuring: sales spread within the lowest and highest third of TV\n",
    "# budgets. (It grows with budget — the opposite of what a first draft of this\n",
    "# lesson asserted.)\n",
    "thirds = pd.qcut(df['tv_spend'], 3, labels=['low', 'mid', 'high'])\n",
    "spread = sales.groupby(thirds, observed=True).std()\n",
    "record('spread_low_tv', round(float(spread['low']), 2))\n",
    "record('spread_high_tv', round(float(spread['high']), 2))\n",
    "\n",
    "def plot(ax):\n",
    "    ax.scatter(df['tv_spend'], sales, s=22, alpha=0.75)\n",
    "    ax.set_xlabel('TV budget ($ thousands)')\n",
    "    ax.set_ylabel('Sales (thousands of units)')\n",
    "\n",
    "save_fig('sales-vs-tv', plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e3af980",
   "metadata": {},
   "source": [
    "## 3. Where is the money going today?\n",
    "\n",
    "Not a modelling question — a briefing question. Any recommendation this path produces\n",
    "is a change to *this* split, so it is worth knowing what it is before proposing one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "372bbb96",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:59.430617Z",
     "iopub.status.busy": "2026-08-19T09:11:59.430540Z",
     "iopub.status.idle": "2026-08-19T09:11:59.475312Z",
     "shell.execute_reply": "2026-08-19T09:11:59.474996Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mean_tv_spend = 147.0\n",
      "mean_radio_spend = 23.3\n",
      "mean_newspaper_spend = 30.6\n",
      "share_newspaper_budget = 15%\n",
      "share_tv_budget = 73%\n",
      "figure spend-by-channel -> look-before-you-fit.spend-by-channel.{light,dark}.svg\n"
     ]
    }
   ],
   "source": [
    "means = df[CHANNELS].mean()\n",
    "record('mean_tv_spend', k(means['tv_spend']))\n",
    "record('mean_radio_spend', k(means['radio_spend']))\n",
    "record('mean_newspaper_spend', k(means['newspaper_spend']))\n",
    "record('share_newspaper_budget', f\"{100 * means['newspaper_spend'] / means.sum():.0f}%\")\n",
    "record('share_tv_budget', f\"{100 * means['tv_spend'] / means.sum():.0f}%\")\n",
    "\n",
    "def plot(ax):\n",
    "    labels = ['TV', 'Radio', 'Newspaper']\n",
    "    ax.bar(labels, [means[c] for c in CHANNELS])\n",
    "    ax.set_ylabel('Average budget per market ($ thousands)')\n",
    "\n",
    "save_fig('spend-by-channel', plot, figsize=(7, 3.6))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b8fe641",
   "metadata": {},
   "source": [
    "## 4. What else is worth a look?\n",
    "\n",
    "One bar per channel: how strongly its budget moves with sales. A shortlist, never a\n",
    "verdict — and the last line of this cell is the one that matters most later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "288b3273",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-19T09:11:59.476655Z",
     "iopub.status.busy": "2026-08-19T09:11:59.476575Z",
     "iopub.status.idle": "2026-08-19T09:11:59.520334Z",
     "shell.execute_reply": "2026-08-19T09:11:59.520069Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "corr_news_radio = 0.35\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "figure what-correlates -> look-before-you-fit.what-correlates.{light,dark}.svg\n"
     ]
    }
   ],
   "source": [
    "corr = df[CHANNELS].corrwith(sales).sort_values()\n",
    "\n",
    "# The load-bearing number in this notebook. Newspaper and radio budgets move\n",
    "# together, which is why newspaper looks like it works — lesson 5 collects this.\n",
    "record('corr_news_radio', round(float(df['newspaper_spend'].corr(df['radio_spend'])), 2))\n",
    "\n",
    "def plot(ax):\n",
    "    labels = {'tv_spend': 'TV', 'radio_spend': 'Radio', 'newspaper_spend': 'Newspaper'}\n",
    "    ax.barh([labels[c] for c in corr.index], corr.values)\n",
    "    ax.set_xlabel('Correlation with sales')\n",
    "    ax.set_xlim(0, 1)\n",
    "\n",
    "save_fig('what-correlates', plot, figsize=(7, 3.2))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
