Math for Causal Inference

Day 69. Two-Way Fixed Effects & Panel Algebra

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.

Video coming soon
Learning outcomes
  • ●Compute one-way and two-way fixed-effects estimates by demeaning, and relate them to dummy-variable regression via Frisch–Waugh–Lovell.
  • ●Prove that fixed effects remove time-invariant confounders and that with two periods FE equals first differences and DiD.
  • ●State strict exogeneity and show how feedback from past outcomes violates it.
  • ●Explain serial correlation and cluster-robust standard errors in panel DiD.
2-HOUR SESSION PLAN
Theory 25′Warm-up (1–5) 20′Proofs (6–10) 35′Applied (11–15) 25′Python & Lab 15′
Colab notebook (.ipynb) Jump to the simulator

Theory

The model

Yit=αi+λt+βDit+εit,Y_{it} = \alpha_i + \lambda_t + \beta D_{it} + \varepsilon_{it},

where αi\alpha_i captures everything constant within unit ii (ability, location, management quality) and λt\lambda_t everything common to all units in period tt (business cycle, national policy). Both may be correlated with DitD_{it}. This is the two-way fixed effects (TWFE) model.

The within transformation

One way. Subtract unit means: Y~it=Yit−Yˉi\tilde Y_{it} = Y_{it} - \bar Y_i. Then Y~it=βD~it+ε~it\tilde Y_{it} = \beta\tilde D_{it} + \tilde\varepsilon_{it} (plus time effects), and αi\alpha_i is gone.

Two way (balanced panel). Double-demean:

Y¨it=Yit−Yˉi⋅−Yˉ⋅t+Yˉ⋅⋅,\ddot Y_{it} = Y_{it} - \bar Y_{i\cdot} - \bar Y_{\cdot t} + \bar Y_{\cdot\cdot},

which removes both αi\alpha_i and λt\lambda_t. Then

β^TWFE=∑i,tD¨it Yit∑i,tD¨it2.\hat\beta_{TWFE} = \frac{\sum_{i,t}\ddot D_{it}\,Y_{it}}{\sum_{i,t}\ddot D_{it}^2}.

By Frisch–Waugh–Lovell (Day 28), this equals the coefficient on DD in a regression with a dummy for every unit and every period (LSDV).

What identifies β

Only within-unit changes in DD (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.

Assumptions

Strict exogeneity: E[εit∣Di1,…,DiT,αi]=0E[\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).

Special cases

  • Two periods: FE = first differences: regress ΔYi\Delta Y_i on ΔDi\Delta D_i.
  • Two groups × two periods with treatment in the last cell: TWFE = the 2×2 DiD (Day 67).

Inference

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).

Part 1 · Warm-up

Exercise 1 · One-way demeaning

A worker's wages over three years are 10, 13, 14 and union status 0, 1, 1. Compute the demeaned values.

Show solution

Step 1. Means: Yˉ=37/3≈12.33\bar Y = 37/3 \approx 12.33, Dˉ=2/3\bar D = 2/3.

Step 2. Y~=(−2.33,0.67,1.67)\tilde Y = (-2.33, 0.67, 1.67); D~=(−0.67,0.33,0.33)\tilde D = (-0.67, 0.33, 0.33).

Exercise 2 · Two-way demeaning

A 2 × 2 panel: unit A has Y=(1,3)Y = (1, 3), unit B has Y=(2,6)Y = (2, 6) in periods 1 and 2. Compute Y¨\ddot Y.

Show solution

Step 1. Unit means: A 2, B 4. Period means: 1.5 and 4.5. Grand mean 3.

Step 2. A1: 1−2−1.5+3=0.51 - 2 - 1.5 + 3 = 0.5. A2: 3−2−4.5+3=−0.53 - 2 - 4.5 + 3 = -0.5.

Step 3. B1: 2−4−1.5+3=−0.52 - 4 - 1.5 + 3 = -0.5. B2: 6−4−4.5+3=0.56 - 4 - 4.5 + 3 = 0.5.

Answer: Y¨=(0.5,−0.5;−0.5,0.5)\ddot Y = (0.5, -0.5; -0.5, 0.5): rows and columns sum to zero.

Exercise 3 · What fixed effects remove

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.

Show solution

Step 1. (a) and (c) are constant within worker: removed by αi\alpha_i.

Step 2. (d) is common to all workers in a year: removed by λt\lambda_t (if time effects are included).

Step 3. (b) varies within worker over time: not removed.

Exercise 4 · Counting parameters

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?

Show solution

Step 1. 3,000+9+1=3,0103{,}000 + 9 + 1 = 3{,}010 (one year dummy omitted, the constant absorbed).

Step 2. Demeaning gives the same β^\hat\beta by FWL without inverting a 3,010 × 3,010 matrix.

Exercise 5 · Two periods

With T=2T = 2, what regression gives the FE estimate?

Show solution

Step 1. Regress ΔYi=Yi2−Yi1\Delta Y_i = Y_{i2} - Y_{i1} on ΔDi\Delta D_i (and a constant for the time effect).

Answer: first differences (Exercise 8).

Part 2 · Proofs

Exercise 6 · Demeaning removes unit effects

Prove Y~it=βD~it+(λt−λˉ)+ε~it\tilde Y_{it} = \beta\tilde D_{it} + (\lambda_t - \bar\lambda) + \tilde\varepsilon_{it}, so αi\alpha_i disappears.

Show solution

Step 1. Average the model over tt for unit ii: Yˉi=αi+λˉ+βDˉi+εˉi\bar Y_i = \alpha_i + \bar\lambda + \beta\bar D_i + \bar\varepsilon_i.

Step 2. Subtract from the model: αi\alpha_i cancels. ■\blacksquare

Exercise 7 · Double demeaning

In a balanced panel, prove that Y¨it\ddot Y_{it} removes both αi\alpha_i and λt\lambda_t.

Show solution

Step 1. αi\alpha_i appears in YitY_{it} and Yˉi⋅\bar Y_{i\cdot} (as αi\alpha_i), and in Yˉ⋅t\bar Y_{\cdot t} and Yˉ⋅⋅\bar Y_{\cdot\cdot} (as αˉ\bar\alpha): αi−αi−αˉ+αˉ=0\alpha_i - \alpha_i - \bar\alpha + \bar\alpha = 0.

Step 2. λt\lambda_t appears in YitY_{it} and Yˉ⋅t\bar Y_{\cdot t} (as λt\lambda_t), and in Yˉi⋅\bar Y_{i\cdot} and Yˉ⋅⋅\bar Y_{\cdot\cdot} (as λˉ\bar\lambda): λt−λˉ−λt+λˉ=0\lambda_t - \bar\lambda - \lambda_t + \bar\lambda = 0. ■\blacksquare

Exercise 8 · FE equals FD with two periods

With T=2T = 2, prove that the one-way FE estimator equals the first-difference estimator (no time effects).

Show solution

Step 1. Demeaned values: Y~i1=−ΔYi/2\tilde Y_{i1} = -\Delta Y_i/2, Y~i2=ΔYi/2\tilde Y_{i2} = \Delta Y_i/2; similarly for DD.

Step 2. ∑tD~itY~it=ΔDiΔYi/2\sum_t\tilde D_{it}\tilde Y_{it} = \Delta D_i\Delta Y_i/2 and ∑tD~it2=(ΔDi)2/2\sum_t\tilde D_{it}^2 = (\Delta D_i)^2/2.

Step 3. β^FE=∑iΔDiΔYi/∑i(ΔDi)2\hat\beta_{FE} = \sum_i\Delta D_i\Delta Y_i/\sum_i(\Delta D_i)^2, the FD estimator (through the origin). ■\blacksquare

Exercise 9 · TWFE in the 2×2 case

Two groups, two periods, Dit=Gi×PosttD_{it} = G_i \times \text{Post}_t. Prove β^TWFE\hat\beta_{TWFE} is the DiD of means.

Show solution

Step 1. With unit and period dummies plus DD, 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).

Step 2. By Day 67, Exercise 7, the coefficient on G×PostG \times \text{Post} is the DiD of means; averaging units within groups does not change it in a balanced panel. ■\blacksquare

Exercise 10 · Feedback violates strict exogeneity

Suppose Dit=ρ εi,t−1+uitD_{it} = \rho\,\varepsilon_{i,t-1} + u_{it} (last period's shock affects this period's treatment). Show E[D~itε~it]≠0E[\tilde D_{it}\tilde\varepsilon_{it}] \ne 0 in general, so FE is biased.

Show solution

Step 1. ε~it=εit−εˉi\tilde\varepsilon_{it} = \varepsilon_{it} - \bar\varepsilon_i, and εˉi\bar\varepsilon_i contains εi,t−1\varepsilon_{i,t-1} for every t≥2t \ge 2.

Step 2. D~it\tilde D_{it} contains ρεi,t−1\rho\varepsilon_{i,t-1} and, through Dˉi\bar D_i, all past shocks.

Step 3. So E[D~itε~it]E[\tilde D_{it}\tilde\varepsilon_{it}] includes terms like −ρVar⁡(ε)/T-\rho\operatorname{Var}(\varepsilon)/T that do not cancel.

Answer: feedback biases FE (by order 1/T1/T); first differences with lagged instruments (Arellano–Bond) are one remedy. ■\blacksquare

Part 3 · Applied

Exercise 11 · The union wage premium

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.

Show solution

Step 1. Cross-section: compares union and non-union workers, mixing the union effect with permanent differences (skills, sector).

Step 2. FE: compares the same worker's wages in union and non-union years, removing time-invariant differences.

Step 3. Identification comes from switchers; it assumes switching is not driven by wage shocks (strict exogeneity) and that switchers' premium represents others'.

Answer: about 10 points of the cross-sectional gap reflect selection on permanent characteristics. (Measurement error in union status also attenuates FE estimates.)

Exercise 12 · A fixed-effects estimate by hand

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.

Show solution

Step 1. A: D~=(−2/3,1/3,1/3)\tilde D = (-2/3, 1/3, 1/3), Y~=(−7/3,2/3,5/3)\tilde Y = (-7/3, 2/3, 5/3). B: D~=(−1/3,−1/3,2/3)\tilde D = (-1/3, -1/3, 2/3), Y~=(−5/3,−2/3,7/3)\tilde Y = (-5/3, -2/3, 7/3).

Step 2. ∑D~Y~\sum\tilde D\tilde Y: A: 14/9+2/9+5/9=21/914/9 + 2/9 + 5/9 = 21/9; B: 5/9+2/9+14/9=21/95/9 + 2/9 + 14/9 = 21/9. Total 42/942/9.

Step 3. ∑D~2\sum\tilde D^2: A: 4/9+1/9+1/9=6/94/9 + 1/9 + 1/9 = 6/9; B: 1/9+1/9+4/9=6/91/9 + 1/9 + 4/9 = 6/9. Total 12/912/9.

Step 4. β^=(42/9)/(12/9)=3.5\hat\beta = (42/9)/(12/9) = 3.5.

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.

Exercise 13 · Clustering

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.

Show solution

Step 1. Conventional t=8t = 8; clustered t≈2.3t \approx 2.3.

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.

Answer: cluster by state; the effect is still significant, but much less precisely estimated than it seemed.

Exercise 14 · Seat-belt laws and traffic deaths

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.

Show solution

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.

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).

Exercise 15 · Health insurance and health with feedback

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?

Show solution

Step 1. A health shock this year can make people buy insurance next year (feedback from εit\varepsilon_{it} to Di,t+1D_{i,t+1}).

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.

Answer: the sign of the bias depends on shock persistence; event-study plots around insurance changes and lagged-instrument methods help.

Verify in Python

import numpy as np

def twfe(Y, D, time_effects=True):
    """Fixed-effects coefficient from a balanced N x T panel by (double) demeaning."""
    dm = lambda M: M - M.mean(1, keepdims=True) - (M.mean(0, keepdims=True) - M.mean() if time_effects else 0)
    Dd = dm(D)
    return (Dd * Y).sum() / (Dd ** 2).sum()

D = np.array([[0, 1, 1], [0, 0, 1.]]); Y = np.array([[10, 13, 14], [20, 21, 24.]])
print("Ex 12: one-way FE", round(twfe(Y, D, False), 3), " two-way FE", round(twfe(Y, D, True), 3))
Y2 = np.array([[1, 3], [2, 6.]])
print("Ex 2:", (Y2 - Y2.mean(1, keepdims=True) - Y2.mean(0, keepdims=True) + Y2.mean()).tolist())

# Union premium: ability drives membership; FE removes it. Clustered vs naive SEs.
rng = np.random.default_rng(69); N, T = 2000, 6
A = rng.normal(size=N)
D = (A[:, None] + rng.normal(0, 1.5, (N, T)) > 0.5).astype(float)
e = 0.7 * rng.normal(size=(N, 1)) + 0.5 * rng.normal(size=(N, T))          # persistent shocks
Y = 2 + 0.08 * D + 0.25 * A[:, None] + 0.02 * np.arange(T) + e
X = np.column_stack([np.ones(N * T), D.ravel()])
print(f"pooled OLS {np.linalg.lstsq(X, Y.ravel(), rcond=None)[0][1]:.3f}  TWFE {twfe(Y, D):.3f}  (truth 0.08)")

# SEs for a state-level policy: conventional vs clustered (one-way FE on a policy constant for years)
S, Tn = 50, 20
reps = []
for _ in range(300):
    P = np.zeros((S, Tn)); start = rng.integers(5, 15, S); P[np.arange(Tn)[None, :] >= start[:, None]] = 1
    P[rng.random(S) < .5] = 0
    u = np.zeros((S, Tn)); u[:, 0] = rng.normal(size=S)
    for t in range(1, Tn): u[:, t] = .8 * u[:, t - 1] + rng.normal(size=S)
    reps.append(twfe(u, P))
print(f"true sampling SD of the placebo estimate: {np.std(reps):.3f}")
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()
b = (Pd * ud).sum() / (Pd ** 2).sum(); r = ud - b * Pd
conv = np.sqrt((r ** 2).sum() / (S * Tn) / (Pd ** 2).sum())
clus = np.sqrt(sum(((Pd[s] * r[s]).sum()) ** 2 for s in range(S))) / (Pd ** 2).sum()
print(f"one draw: conventional SE {conv:.3f}, clustered SE {clus:.3f}")

Key takeaways

  • TWFE removes unit-constant and period-common confounders by double demeaning; identification comes from within-unit changes in treatment.
  • With two periods FE = first differences, and in the 2×2 design TWFE = DiD.
  • It needs strict exogeneity (no feedback, no time-varying confounders) and cluster-robust standard errors.

Simulation Lab: Two-Way Fixed Effects

Open full screen →

Raw, unit-demeaned and two-way-demeaned panels: pooled OLS, one-way FE and TWFE compared.

101502468yearlog wage (×10)
pooled OLS
4.66
unit FE only
3.11
two-way FE
1.41
truth
1.00
join the union in year 5never join

Eight workers over ten years. The four with the highest permanent wage level (unit effect) join a union in year 5. Pooled OLS confuses permanent differences with the union effect; removing unit means (one-way FE) leaves the common time trend tangled with the treatment timing; removing unit and year means (TWFE) isolates within-unit changes.

Day 68Day 70