{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "provenance": []
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day 69: Two-Way Fixed Effects & Panel Algebra\n",
    "\n",
    "*Beginner to Ninja: Math for Causal Inference*\n",
    "\n",
    "With repeated observations on the same units, fixed effects remove everything that is constant within a unit or within a period. The within transformation, its equivalence to dummy-variable regression, first differences, the strict exogeneity it needs, and why standard errors must be clustered.\n",
    "\n",
    "**Learning outcomes**\n",
    "\n",
    "- Compute one-way and two-way fixed-effects estimates by demeaning, and relate them to dummy-variable regression via Frisch–Waugh–Lovell.\n",
    "- Prove that fixed effects remove time-invariant confounders and that with two periods FE equals first differences and DiD.\n",
    "- State strict exogeneity and show how feedback from past outcomes violates it.\n",
    "- Explain serial correlation and cluster-robust standard errors in panel DiD."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Theory\n",
    "\n",
    "### The model\n",
    "\n",
    "$$\n",
    "Y_{it} = \\alpha_i + \\lambda_t + \\beta D_{it} + \\varepsilon_{it},\n",
    "$$\n",
    "\n",
    "where $\\alpha_i$ captures everything constant within unit $i$ (ability, location, management quality) and $\\lambda_t$ everything common to all units in period $t$ (business cycle, national policy). Both may be correlated with $D_{it}$. This is the **two-way fixed effects (TWFE)** model.\n",
    "\n",
    "### The within transformation\n",
    "\n",
    "**One way.** Subtract unit means: $\\tilde Y_{it} = Y_{it} - \\bar Y_i$. Then $\\tilde Y_{it} = \\beta\\tilde D_{it} + \\tilde\\varepsilon_{it}$ (plus time effects), and $\\alpha_i$ is gone.\n",
    "\n",
    "**Two way** (balanced panel). Double-demean:\n",
    "\n",
    "$$\n",
    "\\ddot Y_{it} = Y_{it} - \\bar Y_{i\\cdot} - \\bar Y_{\\cdot t} + \\bar Y_{\\cdot\\cdot},\n",
    "$$\n",
    "\n",
    "which removes both $\\alpha_i$ and $\\lambda_t$. Then\n",
    "\n",
    "$$\n",
    "\\hat\\beta_{TWFE} = \\frac{\\sum_{i,t}\\ddot D_{it}\\,Y_{it}}{\\sum_{i,t}\\ddot D_{it}^2}.\n",
    "$$\n",
    "\n",
    "By Frisch–Waugh–Lovell (Day 28), this equals the coefficient on $D$ in a regression with a dummy for every unit and every period (LSDV).\n",
    "\n",
    "### What identifies β\n",
    "\n",
    "Only **within-unit changes** in $D$ (relative to other units' changes in the same period) identify $\\beta$. Units whose treatment never changes contribute to estimating time effects but not to the comparison of their own treated and untreated periods.\n",
    "\n",
    "### Assumptions\n",
    "\n",
    "**Strict exogeneity:** $E[\\varepsilon_{it} \\mid D_{i1}, \\dots, D_{iT}, \\alpha_i] = 0$: past, present and future treatment are unrelated to the shocks. It fails with **feedback** (a bad health shock today changes insurance tomorrow), and with time-varying confounders. Fixed effects remove only what is constant within units (or common within periods).\n",
    "\n",
    "### Special cases\n",
    "\n",
    "- Two periods: FE = first differences: regress $\\Delta Y_i$ on $\\Delta D_i$.\n",
    "- Two groups × two periods with treatment in the last cell: TWFE = the 2×2 DiD (Day 67).\n",
    "\n",
    "### Inference\n",
    "\n",
    "Outcomes within a unit are correlated over time. Conventional standard errors treating unit-periods as independent can be far too small (Bertrand, Duflo and Mullainathan, 2004). Cluster standard errors at the unit level (or the level of treatment assignment, such as the state).\n",
    "\n",
    "## Part 1 · Warm-up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1 · One-way demeaning\n",
    "\n",
    "A worker's wages over three years are 10, 13, 14 and union status 0, 1, 1. Compute the demeaned values.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Means: $\\bar Y = 37/3 \\approx 12.33$, $\\bar D = 2/3$.\n",
    "\n",
    "**Step 2.** $\\tilde Y = (-2.33, 0.67, 1.67)$; $\\tilde D = (-0.67, 0.33, 0.33)$.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2 · Two-way demeaning\n",
    "\n",
    "A 2 × 2 panel: unit A has $Y = (1, 3)$, unit B has $Y = (2, 6)$ in periods 1 and 2. Compute $\\ddot Y$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Unit means: A 2, B 4. Period means: 1.5 and 4.5. Grand mean 3.\n",
    "\n",
    "**Step 2.** A1: $1 - 2 - 1.5 + 3 = 0.5$. A2: $3 - 2 - 4.5 + 3 = -0.5$.\n",
    "\n",
    "**Step 3.** B1: $2 - 4 - 1.5 + 3 = -0.5$. B2: $6 - 4 - 4.5 + 3 = 0.5$.\n",
    "\n",
    "**Answer:** $\\ddot Y = (0.5, -0.5; -0.5, 0.5)$: rows and columns sum to zero.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3 · What fixed effects remove\n",
    "\n",
    "In a regression of wages on union membership with worker fixed effects, which of these confounders are removed? (a) Innate ability. (b) A promotion that also leads the worker to join the union. (c) Region of birth. (d) Economy-wide wage growth.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** (a) and (c) are constant within worker: removed by $\\alpha_i$.\n",
    "\n",
    "**Step 2.** (d) is common to all workers in a year: removed by $\\lambda_t$ (if time effects are included).\n",
    "\n",
    "**Step 3.** (b) varies within worker over time: not removed.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4 · Counting parameters\n",
    "\n",
    "A panel of 3,000 firms over 10 years is estimated with firm and year dummies plus one treatment variable. How many parameters, and why is the within transformation convenient?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $3{,}000 + 9 + 1 = 3{,}010$ (one year dummy omitted, the constant absorbed).\n",
    "\n",
    "**Step 2.** Demeaning gives the same $\\hat\\beta$ by FWL without inverting a 3,010 × 3,010 matrix.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5 · Two periods\n",
    "\n",
    "With $T = 2$, what regression gives the FE estimate?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Regress $\\Delta Y_i = Y_{i2} - Y_{i1}$ on $\\Delta D_i$ (and a constant for the time effect).\n",
    "\n",
    "**Answer:** first differences (Exercise 8).\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 2 · Proofs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6 · Demeaning removes unit effects\n",
    "\n",
    "Prove $\\tilde Y_{it} = \\beta\\tilde D_{it} + (\\lambda_t - \\bar\\lambda) + \\tilde\\varepsilon_{it}$, so $\\alpha_i$ disappears.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Average the model over $t$ for unit $i$: $\\bar Y_i = \\alpha_i + \\bar\\lambda + \\beta\\bar D_i + \\bar\\varepsilon_i$.\n",
    "\n",
    "**Step 2.** Subtract from the model: $\\alpha_i$ cancels. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 7 · Double demeaning\n",
    "\n",
    "In a balanced panel, prove that $\\ddot Y_{it}$ removes both $\\alpha_i$ and $\\lambda_t$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $\\alpha_i$ appears in $Y_{it}$ and $\\bar Y_{i\\cdot}$ (as $\\alpha_i$), and in $\\bar Y_{\\cdot t}$ and $\\bar Y_{\\cdot\\cdot}$ (as $\\bar\\alpha$): $\\alpha_i - \\alpha_i - \\bar\\alpha + \\bar\\alpha = 0$.\n",
    "\n",
    "**Step 2.** $\\lambda_t$ appears in $Y_{it}$ and $\\bar Y_{\\cdot t}$ (as $\\lambda_t$), and in $\\bar Y_{i\\cdot}$ and $\\bar Y_{\\cdot\\cdot}$ (as $\\bar\\lambda$): $\\lambda_t - \\bar\\lambda - \\lambda_t + \\bar\\lambda = 0$. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 8 · FE equals FD with two periods\n",
    "\n",
    "With $T = 2$, prove that the one-way FE estimator equals the first-difference estimator (no time effects).\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Demeaned values: $\\tilde Y_{i1} = -\\Delta Y_i/2$, $\\tilde Y_{i2} = \\Delta Y_i/2$; similarly for $D$.\n",
    "\n",
    "**Step 2.** $\\sum_t\\tilde D_{it}\\tilde Y_{it} = \\Delta D_i\\Delta Y_i/2$ and $\\sum_t\\tilde D_{it}^2 = (\\Delta D_i)^2/2$.\n",
    "\n",
    "**Step 3.** $\\hat\\beta_{FE} = \\sum_i\\Delta D_i\\Delta Y_i/\\sum_i(\\Delta D_i)^2$, the FD estimator (through the origin). $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 9 · TWFE in the 2×2 case\n",
    "\n",
    "Two groups, two periods, $D_{it} = G_i \\times \\text{Post}_t$. Prove $\\hat\\beta_{TWFE}$ is the DiD of means.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** With unit and period dummies plus $D$, the model has as many parameters as needed to fit each unit's level, each period's level and the treated cell: it is the saturated DiD regression at the group level (unit dummies nest the group dummy).\n",
    "\n",
    "**Step 2.** By Day 67, Exercise 7, the coefficient on $G \\times \\text{Post}$ is the DiD of means; averaging units within groups does not change it in a balanced panel. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 10 · Feedback violates strict exogeneity\n",
    "\n",
    "Suppose $D_{it} = \\rho\\,\\varepsilon_{i,t-1} + u_{it}$ (last period's shock affects this period's treatment). Show $E[\\tilde D_{it}\\tilde\\varepsilon_{it}] \\ne 0$ in general, so FE is biased.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $\\tilde\\varepsilon_{it} = \\varepsilon_{it} - \\bar\\varepsilon_i$, and $\\bar\\varepsilon_i$ contains $\\varepsilon_{i,t-1}$ for every $t \\ge 2$.\n",
    "\n",
    "**Step 2.** $\\tilde D_{it}$ contains $\\rho\\varepsilon_{i,t-1}$ and, through $\\bar D_i$, all past shocks.\n",
    "\n",
    "**Step 3.** So $E[\\tilde D_{it}\\tilde\\varepsilon_{it}]$ includes terms like $-\\rho\\operatorname{Var}(\\varepsilon)/T$ that do not cancel.\n",
    "\n",
    "**Answer:** feedback biases FE (by order $1/T$); first differences with lagged instruments (Arellano–Bond) are one remedy. $\\blacksquare$\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 3 · Applied"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 11 · The union wage premium\n",
    "\n",
    "A cross-sectional regression of log wages on union membership gives 0.18; with worker fixed effects, 0.08. Interpret both and the identifying variation of the second.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Cross-section: compares union and non-union workers, mixing the union effect with permanent differences (skills, sector).\n",
    "\n",
    "**Step 2.** FE: compares the same worker's wages in union and non-union years, removing time-invariant differences.\n",
    "\n",
    "**Step 3.** Identification comes from switchers; it assumes switching is not driven by wage shocks (strict exogeneity) and that switchers' premium represents others'.\n",
    "\n",
    "**Answer:** about 10 points of the cross-sectional gap reflect selection on permanent characteristics. (Measurement error in union status also attenuates FE estimates.)\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 12 · A fixed-effects estimate by hand\n",
    "\n",
    "Worker A: union 0, 1, 1; wage 10, 13, 14. Worker B: union 0, 0, 1; wage 20, 21, 24. Compute the one-way FE estimate.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** A: $\\tilde D = (-2/3, 1/3, 1/3)$, $\\tilde Y = (-7/3, 2/3, 5/3)$. B: $\\tilde D = (-1/3, -1/3, 2/3)$, $\\tilde Y = (-5/3, -2/3, 7/3)$.\n",
    "\n",
    "**Step 2.** $\\sum\\tilde D\\tilde Y$: A: $14/9 + 2/9 + 5/9 = 21/9$; B: $5/9 + 2/9 + 14/9 = 21/9$. Total $42/9$.\n",
    "\n",
    "**Step 3.** $\\sum\\tilde D^2$: A: $4/9 + 1/9 + 1/9 = 6/9$; B: $1/9 + 1/9 + 4/9 = 6/9$. Total $12/9$.\n",
    "\n",
    "**Step 4.** $\\hat\\beta = (42/9)/(12/9) = 3.5$.\n",
    "\n",
    "**Answer:** 3.5. Adding year effects changes the answer (the code computes 2.0), because part of each worker's wage growth is common to both.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 13 · Clustering\n",
    "\n",
    "A state-level policy is studied with 50 states over 20 years. Conventional SE: 0.01; clustered by state: 0.035. The estimate is 0.08. Compare the t-statistics and explain the difference.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Conventional $t = 8$; clustered $t \\approx 2.3$.\n",
    "\n",
    "**Step 2.** Outcomes within a state are serially correlated, and the policy is constant for years at a time, so the 1,000 observations carry much less independent information.\n",
    "\n",
    "**Answer:** cluster by state; the effect is still significant, but much less precisely estimated than it seemed.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 14 · Seat-belt laws and traffic deaths\n",
    "\n",
    "A TWFE regression of state traffic deaths on a seat-belt law indicator, with state and year effects, finds a 6% reduction. Name a confounder that fixed effects do not remove, and suggest a check.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Time-varying state conditions: a recession reduces driving and deaths, and may coincide with law changes; other safety laws (speed limits, drunk-driving laws) adopted at the same time.\n",
    "\n",
    "**Step 2.** Checks: control for vehicle miles travelled and other laws; event study for pre-trends (Day 68); placebo outcomes (deaths of pedestrians, unaffected by seat belts).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 15 · Health insurance and health with feedback\n",
    "\n",
    "A panel regresses self-rated health on insurance status with person fixed effects. Why may strict exogeneity fail, and in which direction might the bias go?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** A health shock this year can make people buy insurance next year (feedback from $\\varepsilon_{it}$ to $D_{i,t+1}$).\n",
    "\n",
    "**Step 2.** Then insured years follow bad shocks; regression to the mean makes health improve after insurance starts, or, if shocks persist, insured years have worse health.\n",
    "\n",
    "**Answer:** the sign of the bias depends on shock persistence; event-study plots around insurance changes and lagged-instrument methods help.\n",
    "\n",
    "</details>\n",
    "\n",
    "## Verify in Python"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def twfe(Y, D, time_effects=True):\n",
    "    \"\"\"Fixed-effects coefficient from a balanced N x T panel by (double) demeaning.\"\"\"\n",
    "    dm = lambda M: M - M.mean(1, keepdims=True) - (M.mean(0, keepdims=True) - M.mean() if time_effects else 0)\n",
    "    Dd = dm(D)\n",
    "    return (Dd * Y).sum() / (Dd ** 2).sum()\n",
    "\n",
    "D = np.array([[0, 1, 1], [0, 0, 1.]]); Y = np.array([[10, 13, 14], [20, 21, 24.]])\n",
    "print(\"Ex 12: one-way FE\", round(twfe(Y, D, False), 3), \" two-way FE\", round(twfe(Y, D, True), 3))\n",
    "Y2 = np.array([[1, 3], [2, 6.]])\n",
    "print(\"Ex 2:\", (Y2 - Y2.mean(1, keepdims=True) - Y2.mean(0, keepdims=True) + Y2.mean()).tolist())\n",
    "\n",
    "# Union premium: ability drives membership; FE removes it. Clustered vs naive SEs.\n",
    "rng = np.random.default_rng(69); N, T = 2000, 6\n",
    "A = rng.normal(size=N)\n",
    "D = (A[:, None] + rng.normal(0, 1.5, (N, T)) > 0.5).astype(float)\n",
    "e = 0.7 * rng.normal(size=(N, 1)) + 0.5 * rng.normal(size=(N, T))          # persistent shocks\n",
    "Y = 2 + 0.08 * D + 0.25 * A[:, None] + 0.02 * np.arange(T) + e\n",
    "X = np.column_stack([np.ones(N * T), D.ravel()])\n",
    "print(f\"pooled OLS {np.linalg.lstsq(X, Y.ravel(), rcond=None)[0][1]:.3f}  TWFE {twfe(Y, D):.3f}  (truth 0.08)\")\n",
    "\n",
    "# SEs for a state-level policy: conventional vs clustered (one-way FE on a policy constant for years)\n",
    "S, Tn = 50, 20\n",
    "reps = []\n",
    "for _ in range(300):\n",
    "    P = np.zeros((S, Tn)); start = rng.integers(5, 15, S); P[np.arange(Tn)[None, :] >= start[:, None]] = 1\n",
    "    P[rng.random(S) < .5] = 0\n",
    "    u = np.zeros((S, Tn)); u[:, 0] = rng.normal(size=S)\n",
    "    for t in range(1, Tn): u[:, t] = .8 * u[:, t - 1] + rng.normal(size=S)\n",
    "    reps.append(twfe(u, P))\n",
    "print(f\"true sampling SD of the placebo estimate: {np.std(reps):.3f}\")\n",
    "Pd = P - P.mean(1, keepdims=True) - P.mean(0, keepdims=True) + P.mean(); ud = u - u.mean(1, keepdims=True) - u.mean(0, keepdims=True) + u.mean()\n",
    "b = (Pd * ud).sum() / (Pd ** 2).sum(); r = ud - b * Pd\n",
    "conv = np.sqrt((r ** 2).sum() / (S * Tn) / (Pd ** 2).sum())\n",
    "clus = np.sqrt(sum(((Pd[s] * r[s]).sum()) ** 2 for s in range(S))) / (Pd ** 2).sum()\n",
    "print(f\"one draw: conventional SE {conv:.3f}, clustered SE {clus:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Key takeaways\n",
    "\n",
    "- TWFE removes unit-constant and period-common confounders by double demeaning; identification comes from within-unit changes in treatment.\n",
    "- With two periods FE = first differences, and in the 2×2 design TWFE = DiD.\n",
    "- It needs strict exogeneity (no feedback, no time-varying confounders) and cluster-robust standard errors."
   ]
  }
 ]
}