{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Lesson 01: Synthetic Demand Data Generation and Validation\n",
        "\n",
        "**CS230 inspiration:** Full-cycle project thinking, data-centric AI, and practical ML strategy before model training.\n",
        "\n",
        "**Demand forecasting focus:** Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice.\n",
        "\n",
        "**Learning objectives**\n",
        "\n",
        "- Describe the components of a demand data-generating process.\n",
        "- Compare weak toy synthetic data with richer synthetic panel data.\n",
        "- Audit realism with distribution, seasonality, leakage, and adversarial-validation checks.\n",
        "- Document limits so synthetic data supports learning without creating false confidence.\n",
        "\n",
        "**Senior-readiness checkpoint:** You can design, validate, and critique synthetic data before trusting it for model development or portfolio evidence.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 1 Explanation\n",
        "\n",
        "**What this cell does:** This setup cell imports the shared scientific Python stack and the curriculum helpers used by later cells. It also establishes the deterministic seed convention so repeated runs are comparable.\n",
        "\n",
        "**Why it matters:** A restart-and-run notebook should make its dependencies explicit before any modeling work begins. For forecasting, reproducibility is especially important because small data or seed changes can change validation conclusions.\n",
        "\n",
        "**Key operations to notice:** confirm that shared helper imports match the lesson's code path; check that `SEED` is defined before any generated data or model training\n",
        "\n",
        "**What to inspect:** Check that imports are grouped by purpose: general analysis packages, forecasting data helpers, metrics, plotting utilities, and any lesson-specific libraries.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "\n",
        "from demand_forecasting_curriculum.data import (\n",
        "    DEFAULT_FEATURE_COLUMNS,\n",
        "    generate_daily_demand,\n",
        "    make_sequence_dataset,\n",
        "    make_supervised_table,\n",
        "    time_series_split,\n",
        ")\n",
        "from demand_forecasting_curriculum.metrics import (\n",
        "    interval_coverage,\n",
        "    mase,\n",
        "    pinball_loss,\n",
        "    smape,\n",
        "    wmape,\n",
        ")\n",
        "from demand_forecasting_curriculum.plotting import plot_predictions, plot_series\n",
        "\n",
        "SEED = 42\n",
        "np.random.seed(SEED)\n",
        "\n",
        "from sklearn.ensemble import HistGradientBoostingClassifier\n",
        "from sklearn.metrics import roc_auc_score\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Why This Lesson Exists\n",
        "\n",
        "Deep learning is data-hungry, and real demand data is often private, sparse, censored, or slow to collect. High-quality synthetic data is useful for curriculum design, prototype testing, privacy-safe demos, stress testing, and debugging training loops. Low-quality synthetic data is dangerous because it can make a model look impressive on patterns that would not survive contact with real operations.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 2 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on the lesson workflow. It creates or updates `dgp_checklist` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** creates an inspectable table for assumptions, metrics, or decisions\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "dgp_checklist = pd.DataFrame(\n",
        "    [\n",
        "        (\"entity heterogeneity\", \"stores/items differ in scale, price sensitivity, and seasonality\"),\n",
        "        (\"calendar structure\", \"weekly cycles, holidays, month effects, and annual seasonality\"),\n",
        "        (\"known future covariates\", \"planned price, promotion, and calendar features\"),\n",
        "        (\"latent vs observed demand\", \"stockouts can censor demand, so sales may understate need\"),\n",
        "        (\"noise and shocks\", \"random variation, local events, supply issues, and regime changes\"),\n",
        "        (\"hierarchy\", \"SKU-store forecasts must aggregate to item, store, and total views\"),\n",
        "        (\"validation hooks\", \"known ground truth, scenario labels, and reproducible seeds\"),\n",
        "    ],\n",
        "    columns=[\"component\", \"what good synthetic data should include\"],\n",
        ")\n",
        "dgp_checklist\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 3 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on synthetic demand generation. It creates or updates `reference`, `rich_candidate`, `weak_candidate` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** creates a synthetic retail panel with known demand drivers; creates an inspectable table for assumptions, metrics, or decisions\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def generate_bad_iid_demand(periods=420, n_stores=4, n_items=6, seed=SEED):\n",
        "    rng = np.random.default_rng(seed)\n",
        "    dates = pd.date_range(\"2021-01-01\", periods=periods, freq=\"D\")\n",
        "    rows = []\n",
        "    for store_id in range(n_stores):\n",
        "        for item_id in range(n_items):\n",
        "            demand = rng.poisson(lam=45, size=periods)\n",
        "            rows.append(\n",
        "                pd.DataFrame(\n",
        "                    {\n",
        "                        \"date\": dates,\n",
        "                        \"store_id\": store_id,\n",
        "                        \"item_id\": item_id,\n",
        "                        \"demand\": demand,\n",
        "                        \"true_demand\": demand,\n",
        "                        \"price\": 20.0,\n",
        "                        \"promo\": 0,\n",
        "                        \"stockout\": 0,\n",
        "                        \"holiday\": 0,\n",
        "                        \"day_of_week\": dates.dayofweek,\n",
        "                        \"month\": dates.month,\n",
        "                        \"time_idx\": np.arange(periods),\n",
        "                    }\n",
        "                )\n",
        "            )\n",
        "    return pd.concat(rows, ignore_index=True)\n",
        "\n",
        "reference = generate_daily_demand(n_stores=4, n_items=6, periods=420, seed=SEED)\n",
        "rich_candidate = generate_daily_demand(n_stores=4, n_items=6, periods=420, seed=SEED + 1)\n",
        "weak_candidate = generate_bad_iid_demand()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 4 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on the lesson workflow. It creates or updates `reports` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** creates an inspectable table for assumptions, metrics, or decisions\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def quality_report(frame):\n",
        "    grain = [\"date\", \"store_id\", \"item_id\"]\n",
        "    series_lengths = frame.groupby([\"store_id\", \"item_id\"]).size()\n",
        "    daily_total = frame.groupby(\"date\")[\"demand\"].sum()\n",
        "    weekly_mean = frame.groupby(\"day_of_week\")[\"demand\"].mean()\n",
        "\n",
        "    def safe_corr(left, right):\n",
        "        if left.std() == 0 or right.std() == 0:\n",
        "            return np.nan\n",
        "        return left.corr(right)\n",
        "\n",
        "    return pd.Series(\n",
        "        {\n",
        "            \"rows\": len(frame),\n",
        "            \"series\": frame[[\"store_id\", \"item_id\"]].drop_duplicates().shape[0],\n",
        "            \"duplicate_grain_rows\": int(frame.duplicated(grain).sum()),\n",
        "            \"min_series_length\": int(series_lengths.min()),\n",
        "            \"max_series_length\": int(series_lengths.max()),\n",
        "            \"zero_demand_rate\": float((frame[\"demand\"] == 0).mean()),\n",
        "            \"promo_rate\": float(frame[\"promo\"].mean()),\n",
        "            \"stockout_rate\": float(frame[\"stockout\"].mean()),\n",
        "            \"mean_demand\": float(frame[\"demand\"].mean()),\n",
        "            \"demand_cv\": float(frame[\"demand\"].std() / frame[\"demand\"].mean()),\n",
        "            \"weekly_seasonality_ratio\": float(weekly_mean.max() / weekly_mean.min()),\n",
        "            \"daily_total_cv\": float(daily_total.std() / daily_total.mean()),\n",
        "            \"price_demand_corr\": safe_corr(frame[\"price\"], frame[\"demand\"]),\n",
        "        }\n",
        "    )\n",
        "\n",
        "reports = pd.DataFrame(\n",
        "    {\n",
        "        \"reference\": quality_report(reference),\n",
        "        \"rich_candidate\": quality_report(rich_candidate),\n",
        "        \"weak_candidate\": quality_report(weak_candidate),\n",
        "    }\n",
        ").T\n",
        "reports\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 5 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on visualization. It creates or updates `fig`, `axes` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** visualizes model behavior or data quality\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "fig, axes = plt.subplots(1, 2, figsize=(11, 3))\n",
        "reference.groupby(\"date\")[\"demand\"].sum().plot(ax=axes[0], label=\"reference\")\n",
        "rich_candidate.groupby(\"date\")[\"demand\"].sum().plot(ax=axes[0], label=\"rich\")\n",
        "weak_candidate.groupby(\"date\")[\"demand\"].sum().plot(ax=axes[0], label=\"weak\")\n",
        "axes[0].set_title(\"Daily total demand\")\n",
        "axes[0].legend()\n",
        "\n",
        "reports[[\"promo_rate\", \"stockout_rate\", \"weekly_seasonality_ratio\"]].plot.bar(ax=axes[1])\n",
        "axes[1].set_title(\"Synthetic quality signals\")\n",
        "axes[1].tick_params(axis=\"x\", rotation=20)\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 6 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on the lesson workflow. It creates or updates `auc_report` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** measures adversarial-validation separability; creates an inspectable table for assumptions, metrics, or decisions\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def adversarial_auc(reference_frame, candidate_frame):\n",
        "    cols = [\"demand\", \"price\", \"promo\", \"holiday\", \"day_of_week\", \"month\", \"stockout\"]\n",
        "    ref = reference_frame[cols].assign(is_candidate=0)\n",
        "    cand = candidate_frame[cols].assign(is_candidate=1)\n",
        "    data = pd.concat([ref, cand], ignore_index=True).sample(frac=1, random_state=SEED)\n",
        "    split = int(len(data) * 0.7)\n",
        "    train, test = data.iloc[:split], data.iloc[split:]\n",
        "\n",
        "    classifier = HistGradientBoostingClassifier(max_iter=80, random_state=SEED)\n",
        "    classifier.fit(train[cols], train[\"is_candidate\"])\n",
        "    score = classifier.predict_proba(test[cols])[:, 1]\n",
        "    return roc_auc_score(test[\"is_candidate\"], score)\n",
        "\n",
        "auc_report = pd.DataFrame(\n",
        "    [\n",
        "        (\"rich_candidate\", adversarial_auc(reference, rich_candidate)),\n",
        "        (\"weak_candidate\", adversarial_auc(reference, weak_candidate)),\n",
        "    ],\n",
        "    columns=[\"candidate\", \"adversarial_auc\"],\n",
        ")\n",
        "auc_report\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 7 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on time-based splitting, supervised feature construction, forecast metrics. It creates or updates intermediate values and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** turns time series rows into forecast-origin training examples; keeps train, validation, and test windows ordered by date; evaluates scale-weighted forecast error; evaluates symmetric percentage forecast error; creates an inspectable table for assumptions, metrics, or decisions\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def seasonal_naive_score(frame, horizon=7):\n",
        "    supervised = make_supervised_table(frame, horizon=horizon)\n",
        "    train, valid, _ = time_series_split(supervised, valid_days=60, test_days=45)\n",
        "    return {\n",
        "        \"valid_wmape\": wmape(valid[\"y\"], valid[\"lag_7\"]),\n",
        "        \"valid_smape\": smape(valid[\"y\"], valid[\"lag_7\"]),\n",
        "        \"train_rows\": len(train),\n",
        "    }\n",
        "\n",
        "pd.DataFrame(\n",
        "    {\n",
        "        \"reference\": seasonal_naive_score(reference),\n",
        "        \"rich_candidate\": seasonal_naive_score(rich_candidate),\n",
        "        \"weak_candidate\": seasonal_naive_score(weak_candidate),\n",
        "    }\n",
        ").T\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 8 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Synthetic Demand Data Generation and Validation** by working on the lesson workflow. It creates or updates `synthetic_data_card` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** follow the assignments, final displayed object, and any checks in the cell because those are the evidence the next cell depends on\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "synthetic_data_card = {\n",
        "    \"purpose\": \"Teach and test demand forecasting workflows without private business data.\",\n",
        "    \"known_ground_truth\": [\"true_demand\", \"stockout\", \"promo\", \"holiday\"],\n",
        "    \"realism_checks\": [\n",
        "        \"grain uniqueness\",\n",
        "        \"series length consistency\",\n",
        "        \"seasonality strength\",\n",
        "        \"promotion and stockout rates\",\n",
        "        \"adversarial validation against a reference distribution\",\n",
        "        \"baseline forecast difficulty\",\n",
        "    ],\n",
        "    \"known_limits\": [\n",
        "        \"No real customer substitution behavior\",\n",
        "        \"Simplified price elasticity\",\n",
        "        \"No supply-chain lead-time simulation\",\n",
        "        \"No privacy guarantees because this is simulation, not anonymization\",\n",
        "    ],\n",
        "    \"safe_uses\": [\n",
        "        \"curriculum examples\",\n",
        "        \"debugging training loops\",\n",
        "        \"stress-testing feature and metric code\",\n",
        "        \"portfolio demonstrations with clear disclosure\",\n",
        "    ],\n",
        "}\n",
        "synthetic_data_card\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Lesson Recap\n",
        "\n",
        "In this lesson, we used 8 runnable code cells to work through **Synthetic Demand Data Generation and Validation**. The practical focus was: Design a realistic demand data-generating process and audit whether synthetic data is useful for deep learning practice.\n",
        "\n",
        "We did this because deep learning for demand forecasting is not only about choosing an architecture. The learner needs to understand the data contract, the target and horizon, the validation path, the metric, and the operational decision supported by the forecast.\n",
        "\n",
        "The core takeaways were:\n",
        "\n",
        "- Describe the components of a demand data-generating process.\n",
        "- Compare weak toy synthetic data with richer synthetic panel data.\n",
        "- Audit realism with distribution, seasonality, leakage, and adversarial-validation checks.\n",
        "- Document limits so synthetic data supports learning without creating false confidence.\n",
        "\n",
        "By the end, you should be able to explain not just what the code produced, but why each step was necessary and what could go wrong if the same step were skipped or performed with leakage.\n",
        "\n",
        "**Senior-readiness reminder:** You can design, validate, and critique synthetic data before trusting it for model development or portfolio evidence.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Exercises\n",
        "\n",
        "- Modify `generate_daily_demand` to expose promo intensity or stockout severity as parameters.\n",
        "- Add a demand shock scenario and verify that monitoring lessons can detect it.\n",
        "- Create a synthetic data card for a real portfolio project and list what it cannot prove.\n",
        "- Use adversarial validation to compare two generator versions after changing the DGP.\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python (deep-learning-demand)",
      "language": "python",
      "name": "deep-learning-demand"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
