The whole course in two sentences
Lectures 1–3 teach you how to fit a model to data and say how sure you are. Lectures 4–7 teach you how to tell whether X actually causes Y, or just happens to move with it.
How to use this guide
- One lecture per tab. Only one is on screen at a time. Every lecture follows the same five steps (below), so you always know where you are.
- Yellow box first. Each lecture opens with the 3 things to remember, then what you'll be able to do afterwards, each marked Foundational, Intermediate or Advanced.
- "Just the gist" (top right) hides the sections marked deeper. Use it for a first pass, then switch to Full.
- Explain buttons. Tricky ideas have Simple / Intuitive / Technical switches, so you can change the depth of one concept without changing the whole page.
- Play with the demos. Each lecture has an interactive simulation: move a slider and see why a result changes.
- Practise, don't just read. "Apply" and "Challenge" questions give feedback on the exact answer you picked. Exam mode mixes questions from all lectures, with a timer if you want one.
- Final test when everything is studied: thirty new questions, graded together, then a score per lecture and a study plan that links to the exact sections to revisit.
- Search (top right, or press /) looks through everything: definitions, explanations, questions and R code.
- Optional R labs show each method on real-looking data, with the exact output you should get and downloadable scripts.
- Your progress stays in this browser only.
The five steps in every lecture
- UnderstandPlain-language explanation and a worked example.
- See itAn interactive demo: change something, watch what happens.
- ApplyShort calculations and output-reading questions.
- ChallengeSpot the broken assumption or the wrong conclusion.
- ReviewQuick recall questions, then exam mode later.
Your progress
Studied = you ticked it. Practised = you answered every Apply and Challenge question. Mastered = at least 80 % right in exam mode on most of that lecture's questions, or at least 80 % on that lecture's part of the final test. Mastered lectures show "review due" after a week, because recalling something a few days later is what makes it stick.
The map
1 · Regression basics
Draw the best line through data, read its numbers, know what can go wrong.
2 · Maximum likelihood
Pick the parameter that makes your data least surprising.
3 · Bootstrap
Measure uncertainty by resampling your own data, over and over.
4 · Intro to causality
Why correlation isn't causation, and why randomness is the cure.
5 · Instrumental variables
Use an outside "knob" that nudges the treatment to get a clean effect.
6 · Regression discontinuity
Compare people just above and just below a cut-off.
7 · Difference-in-differences
Compare how much two groups changed, before vs after a policy.
Basics refresher
Plain definitions of the simple stuff: mean, variance, SE, log, derivative, Greek letters.
Cheat sheet
All methods side by side, how they connect, and "choose the design" practice.
Exam mode
Mixed questions from any lectures, timed or untimed, with results by topic.
Final test
Thirty new questions from everything, graded at the end, with a study plan that links back to what to revisit.
Econometrics Learnbook for Dummies
The printable learnbook (third edition, PDF): worked derivations, R labs, exercises, two practice exam papers and answers.
Download the PDFFive words you'll see everywhere
| Word | Plain meaning |
|---|---|
| Y (outcome) | The thing you care about: wage, test score, cholera deaths. |
| X or D (regressor / treatment) | The thing you think affects Y: education, going to a charter school, clean water. |
| β ("beta") | The true size of the effect. Unknown. We try to guess it. |
| β̂ ("beta hat") | Our guess of β from the data. The hat always means "estimated". |
| ε (error) | Everything else that affects Y that we didn't put in the model. |
- Mastering 'Metrics (Angrist & Pischke): friendly, story-driven, perfect for lectures 4–7. Chapters map almost 1:1 to the causal lectures.
- Linear Models with R (Faraway): more technical, with R code. Use it for lecture 1, one topic at a time.
The simple stuff, defined
Every "obvious" word the lectures assume you know, in one or two lines each. Look things up here whenever a word feels fuzzy.
Data and variables
- Observation
- One row of data: one person, firm, country or store at one time.
- Variable
- One column of data, something measured for each observation (wage, age).
- Population vs sample
- Population = everyone you care about. Sample = the part you actually observed. We use the sample to learn about the population.
- Parameter
- A fixed but unknown number describing the population, like the true mean μ or true slope β.
- Dummy (indicator) variable
- A variable that is 1 or 0: treated or not, female or not, NJ or PA.
- Continuous vs discrete
- Continuous can take any value in a range (height). Discrete takes separate values (number of kids).
- Cross-section / time series / panel
- Many units at one time / one unit over many times / many units over many times.
- Observational data
- Data you just collected from the world, with no experiment. Treatment wasn't randomised, so causality is hard.
Describing data
- Mean (average), x̄
- Add everything up, divide by how many.
x̄ = Σxᵢ / n. - Median
- The middle value when sorted. Not pulled around by extreme values like the mean is.
- Mode
- The most common value, or a peak in a distribution. "Unimodal" = one peak.
- Variance
- Average squared distance from the mean: how spread out the data are.
Var(x) = Σ(xᵢ − x̄)² / n(or n − 1 for the sample version). - Standard deviation (SD, σ)
- Square root of the variance. Spread in the original units. "0.48 SD" = 0.48 of a standard deviation.
- Covariance
- Do two variables move together?
Cov(x,y) = Σ(xᵢ − x̄)(yᵢ − ȳ) / n. Positive = up together, negative = opposite. Units are awkward. - Correlation
- Covariance rescaled to lie between −1 and 1. ±1 = perfect straight line; 0 = no linear relationship.
- Percentile
- The value below which that % of data falls. 75th percentile: 75 % of values are lower.
- Outlier
- A point far from the rest. Can drag means and regression lines around.
- Demeaning
- Subtracting the mean from a variable, so the new version averages zero:
x̃ᵢ = xᵢ − x̄.
Probability
- Random variable
- A quantity whose value depends on chance, like a die roll or the wage of a randomly picked person.
- Distribution
- The full list of possible values and how likely each one is.
- PDF (density)
- Probability density function: the curve showing where values are likely. Area under it = probability.
- CDF
- Cumulative distribution function: F(x) = P(X ≤ x), the probability of being at most x. Goes from 0 up to 1.
- Expectation, E[X]
- The long-run average value of a random variable, its "population mean".
- Conditional expectation, E[Y | X]
- The average of Y among those with a given X. E[wage | educ = 12] = average wage of people with 12 years of school.
- Independence (⫫)
- Knowing one variable tells you nothing about the other.
- i.i.d.
- Independent and identically distributed: each observation is a separate draw from the same distribution.
- Normal distribution, N(μ, σ²)
- The bell curve. Mean μ, variance σ². About 95 % of values lie within ±1.96 SD of the mean.
- Bernoulli / Binomial
- Bernoulli = one yes/no trial with probability p. Binomial = number of successes in n such trials.
- Poisson
- Distribution for counts (0, 1, 2…) of events.
- Exponential
- Distribution for waiting times between random events.
- t, χ², F distributions
- Distributions of test statistics. t ≈ normal but fatter tails for small samples; χ² and F are for tests of several things at once.
The two big theorems
- Law of Large Numbers (LLN)
- With more and more data, the sample mean gets closer and closer to the true mean.
- Central Limit Theorem (CLT)
- With enough independent observations from a distribution with a finite variance, the sample mean is approximately normally distributed, even if the data themselves aren't. "Enough" depends on how skewed the data are. This is why normal-based tests work so often.
Estimation
- Estimator vs estimate
- Estimator = the recipe (e.g. "take the sample mean"). Estimate = the number you get from your particular sample. The hat (β̂) marks both.
- Sampling variation
- A different sample would give a different estimate. The estimator is itself a random variable.
- Bias
- E[β̂] − β: how far off the estimator is on average. Unbiased = right on average.
- Standard error (SE)
- The standard deviation of an estimator across hypothetical repeated samples: how much it would wobble. Smaller = more precise.
- SD vs SE
- SD describes the spread of the data. SE describes the uncertainty of an estimate. SE shrinks as n grows (roughly SD/√n for a mean); SD doesn't.
- Consistent
- The estimator converges to the true value as the sample size grows.
- Efficient
- Has the smallest variance among a class of estimators.
- Asymptotic
- "In very large samples." Asymptotic results are approximations for big n.
- Identification
- Whether the data could in principle pin down the parameter, even with infinite data. Comes before estimation.
- Residual vs error
- Error ε = true unobserved deviation from the true line. Residual ε̂ = observed deviation from your fitted line.
- Fitted value, ŷ
- The value the model predicts for an observation.
- Intercept / slope
- Intercept: predicted y when all x's are 0. Slope: change in y per one-unit change in x.
- Control variable
- A variable added to a regression so comparisons are made between people similar in that variable.
- Ceteris paribus
- Latin for "other things equal": the effect of x holding everything else fixed.
Testing
- Hypothesis
- A claim about a parameter you want to check, e.g. "β = 0".
- Null hypothesis (H₀)
- The default "nothing going on" claim you test against (usually: no effect).
- Alternative (H₁)
- What you accept if you reject H₀ (usually: there is an effect).
- Test statistic
- A number computed from data to judge H₀, like t = β̂ / SE.
- Significance level (α)
- The cut-off for rejecting, usually 5 %. "Significant at 5 %" = p < 0.05.
- p-value
- If the model and H₀ were true, the chance of a test statistic at least as extreme as yours. Small = data look incompatible with H₀. It is not the probability that H₀ is true.
- Type I / Type II error
- Type I: rejecting a true H₀ (false alarm). Type II: not rejecting a false H₀ (missed effect).
- Power
- Chance of correctly rejecting a false H₀. More data = more power.
- Confidence interval
- A range built by a method that captures the true value in, say, 95 % of repeated samples. Roughly estimate ± 2 × SE.
- Degrees of freedom
- Number of observations minus number of estimated parameters (n − p). Used to pick the right t or F distribution.
- Stars (*, **, ***)
- Shorthand in tables for significance at 10 %, 5 %, 1 %. Your lecturer warns against reading too much into them.
Math tools
- Σ (sum)
Σᵢ xᵢ= x₁ + x₂ + … + xₙ.- Π (product)
Πᵢ xᵢ= x₁ · x₂ · … · xₙ.- log (natural log)
- The inverse of eˣ. Key property: log(a·b) = log a + log b. A small change in log x ≈ the percentage change in x (Δlog x = 0.05 ≈ 5 %).
- exp, e
- e ≈ 2.718. exp(x) = eˣ. exp(log x) = x.
- Derivative
- The slope of a function at a point: how fast it changes. At a maximum or minimum the derivative is 0. That's why we "set the derivative to zero" (first-order condition, FOC).
- Second derivative
- How fast the slope changes, i.e. the curvature. Negative at a maximum.
- argmax
- "The value of θ that makes this biggest." The max is the height; the argmax is where it happens.
- Limit (lim x↓c)
- The value a function approaches as x gets closer and closer to c. ↓ = from above, ↑ = from below.
- Indicator 1{…}
- Equals 1 if the condition is true, 0 otherwise.
- Matrix
- A table of numbers. X in regression has one row per observation and one column per variable.
- Transpose, Xᵀ
- Flip a matrix so rows become columns.
- Inverse, A⁻¹
- The matrix version of dividing: A⁻¹A = I. Not every matrix has one (e.g. perfectly collinear X).
- Identity matrix, I
- 1s on the diagonal, 0s elsewhere. The matrix version of the number 1.
- Orthogonal (⊥)
- Perpendicular. For data vectors: their products sum to zero, i.e. they're uncorrelated in that sense.
Greek letters you'll see
- α alpha
- intercept, or significance level
- β beta
- regression coefficients
- γ gamma
- more coefficients (often on controls or interactions)
- δ delta
- treatment effect (IV, DiD), or a small change
- ε epsilon, η eta
- error terms
- θ theta
- a generic parameter (MLE)
- λ lambda
- a parameter (exponential mean), or time fixed effects
- μ mu
- a mean
- π pi
- first-stage coefficients in IV (not 3.14 there!)
- ρ rho, φ phi
- reduced form and first stage in the LATE formula
- σ sigma, Σ
- σ = standard deviation, σ² = variance. Capital Σ = a sum, or a covariance matrix.
- τ tau
- treatment effect (RDD)
No word matches that. Try a shorter search.
Regression basics
Regression draws the line that best fits a cloud of points, then tells you how steep that line is and how sure you can be about it.
Gist mode is on: deeper sections are hidden. Switch to Full at the top when you're ready for more.
- Variance
- How spread out something is: the average squared distance from its mean.
- Covariance
- Whether two variables move together (positive) or in opposite directions (negative).
- Residual
- Actual y minus the y your line predicts: the leftover mistake for one data point.
- Standard error
- How much an estimate would wobble if you redid the study with a new sample.
- log
- Natural logarithm. A change of 0.05 in log y ≈ a 5 % change in y.
If you remember only 3 things
- OLS picks the line that makes the squared mistakes as small as possible. The slope β̂ tells you how much Y changes when X goes up by 1, holding the other X's fixed.
- A regression shows association, not causation. If you leave out something that affects Y and is linked to X (like "ability"), your slope is biased.
- p-values and confidence intervals assume your model is right. They measure uncertainty from sampling, not whether the model is true.
After this lecture you can…
- FoundationalRead a slope correctly, including log forms and "holding the others fixed".
- FoundationalSay what R² measures, and when it can mislead (or go negative).
- IntermediateUse the omitted-variable-bias formula to predict the sign and size of a bias.
- IntermediateChoose between usual, robust and clustered standard errors.
- IntermediateInterpret p-values and confidence intervals without the classic mistakes.
- AdvancedDerive β̂ = (XᵀX)⁻¹Xᵀy and read OLS as a projection.
On this page
What is regression for?
Two very different jobs:
| Prediction | Explanation |
|---|---|
| "What will happen?" | "Why did it happen?" |
| Easier, especially for things that repeat. | Hard. Needs real knowledge of the subject. |
| A big R² matters a lot. | A small R² is fine, if the slope is unbiased. |
Keep asking yourself which job you're doing. Many rules in this lecture change depending on the answer.
The model, in words
- y
- outcome (also "dependent variable")
- x₁ … xp
- things that might explain y ("regressors", "features")
- β₀
- intercept: predicted y when all x's are 0
- β₁
- slope: change in y when x₁ goes up by 1 and the other x's stay the same
- ε
- error: everything else we didn't measure
In matrix form the same thing is written y = Xβ + ε. X is just a table: one row per person, one column per variable, plus a column of 1s for the intercept.
A map. A map is useful because it leaves things out. Your lecturer's line: "The purpose of the model is to be useful. It is not to be correct." It should be right about the part you care about.
What the line is aiming at: the conditional expectation
E[Y | X = x] is the average of Y among everyone with that value of x: the average wage of people with 12 years of school, of people with 13 years, and so on. Plot those averages and you get the conditional expectation function (CEF).
- If the CEF is a straight line, regression recovers it exactly (in large samples).
- If it's curved, OLS gives the best straight-line approximation to it. That's still useful, but check with a plot.
- The error ε is "Y minus its conditional average". The assumption
E[ε | X] = 0says the line gets the averages right for every x.
This is a statement about averages among people who differ in X. Whether changing someone's X would change their Y is a different, causal question (lecture 4).
Least squares: how the line is chosen
- Xᵀy
- how much each x moves together with y
- (XᵀX)⁻¹
- divides by how much the x's vary (and overlap with each other)
With one x this collapses to the formula you can remember: β̂₁ = Cov(x, y) / Var(x). You'll see this shape again in IV.
Geometrydeeper
Fitting OLS = dropping a perpendicular from y onto the space spanned by the columns of X. The fitted values ŷ = Py are the "shadow" of y (P is the projection matrix). The residuals ε̂ = My are what's left (M = I − P is the residual maker). Both P and M are symmetric and idempotent (doing them twice changes nothing), and ŷ is perpendicular to ε̂.
Why OLS is a good choice: the Gauss–Markov theorem says that if the model is linear in β, X has full rank (no perfect collinearity), errors average zero given X (exogeneity), and errors have constant variance and are uncorrelated with each other, then OLS has the smallest variance of all linear unbiased estimators. Short name: BLUE (Best Linear Unbiased Estimator). Normal errors are not needed for BLUE; they're only needed for exact small-sample t and F tests.
"Linear" doesn't mean "straight"
Linear means linear in the β's. You can transform the x's and y however you like:
log(y) = β₀ + β₁x + β₂x² + εis still a linear model (a curve!).y = β₀ · x^β₁ · εbecomes linear after taking logs:log y = log β₀ + β₁ log x + log ε.
R²: how much variation the line explains
- RSS
- residual sum of squares: total squared mistakes left over
- TSS
- total sum of squares: how spread out y is around its mean
For ordinary OLS with an intercept, measured on the data used to fit it, R² lies between 0 (line explains nothing) and 1 (perfect fit), and it equals the squared correlation between ŷ and y.
The 0-to-1 range is not a law of nature. Without an intercept, or when a model is judged on new data (out of sample), RSS can exceed TSS and the formula gives a negative R². That simply means the model predicts worse than the plain mean ȳ. Some software also reports a different "uncentred" R² for models without an intercept, so the number isn't comparable.
Very different datasets can have the same R² (Faraway shows four with R² = 0.65). Always plot your data. For explanation, a low R² is not automatically a problem.
Reading the coefficients (the log table)
This shows up on exams all the time. Learn this table:
| Model | x goes up by… | y changes by… |
|---|---|---|
y = β₁x (level–level) | 1 unit | β₁ units |
log y = β₁x (log–level) | 1 unit | about β₁·100 % |
y = β₁ log x (level–log) | 1 % | about β₁/100 units |
log y = β₁ log x (log–log) | 1 % | about β₁ % (an elasticity) |
log(wage) = β₀ + β₁·education + β₂·age + ε, and say β̂₁ = 0.07.
Correct sentence: "For people of the same age, one more year of education is associated with about 7 % higher wages."
Note the words same age (holding others fixed) and associated with (not "causes"). Your lecturer stresses both.
Interaction termsdeeperlog(wage) = β₀ + β₁educ + β₂south + β₃south·educ + ε. Outside the South the education slope is β₁. In the South it is β₁ + β₃ and the intercept shifts by β₂. So β₃ answers "is the return to education different in the South?"
Inference: t-tests, F-tests, intervals deeper
If errors are normal, β̂ is normal too: β̂ ~ N(β, σ²(XᵀX)⁻¹). That lets us test things.
- se(β̂ᵢ)
- standard error: how much β̂ᵢ would jump around across samples
- n − p
- degrees of freedom: observations minus parameters
Tests H₀: βᵢ = 0. Big |t| means the estimate is many standard errors away from zero.
F-test compares a big model Ω with a smaller one ω that drops some variables: F = [(RSSω − RSSΩ)/q] / [RSSΩ/(n−p)], where q is the number of dropped variables. "Do these variables together add anything?"
Confidence interval: β̂ᵢ ± t · se(β̂ᵢ). The parameter is fixed. The interval is the random thing.
Predicting a new point x₀: the interval for the average y at x₀ uses √(x₀ᵀ(XᵀX)⁻¹x₀). The interval for one future y uses √(1 + x₀ᵀ(XᵀX)⁻¹x₀). The extra "1+" is the new person's own error ε₀, so prediction intervals are always wider.
t-test = regression, if you code it right. Regress y on an intercept and one dummy (1 = group B, 0 = group A). The intercept is group A's mean, the dummy's coefficient is the difference in means, and its ordinary t-statistic is exactly the pooled-variance (equal-variance) two-sample t-test. (Two dummies and an intercept would be perfectly collinear; two dummies without an intercept just give the two group means.)
If the two groups have different spreads, the matching test is Welch's t-test. The regression version is the same dummy regression with heteroskedasticity-robust standard errors (HC2 gives Welch's standard error exactly; the degrees of freedom differ slightly).
What can go wrong
1 · Omitted variable bias (the big one)
True model includes ability, but we can't measure it:
- β₁*
- true effect of education
- β₃*
- effect of ability on wage
- γ₁
- how ability moves with education
The bias disappears only if ability doesn't affect wages (β₃* = 0) or isn't related to education (γ₁ = 0). Otherwise your slope picks up part of ability's effect.
Related: Simpson's paradox. Adding one variable can flip the sign of another coefficient. So two models can give completely different β̂₁.
2 · Collinearity
When x's are strongly correlated with each other, OLS can't tell them apart. Estimates get high variance and become unstable.
- Spot it: correlations near ±1; regressing xⱼ on the other x's gives Rⱼ² near 1; condition number √(λ₁/λp) ≥ 30.
- Measure it: variance of β̂ⱼ is multiplied by
1/(1 − Rⱼ²), the variance inflation factor.
3 · Heteroskedasticity
"Hetero" = different, "skedastic" = spread. The errors are bigger for some x's than others (e.g. y = 1 + 3x + x·ε: bigger x, bigger noise). The usual standard-error formula assumed constant spread σ².
Heteroskedasticity does not bias β̂ (as long as errors still average zero given X). What breaks is the conventional standard errors, and with them t-tests and confidence intervals.
- Fix A, weighted least squares: give noisy points less weight, wᵢ = 1/var(yᵢ). Example: if yᵢ is an average over nᵢ people, use wᵢ = nᵢ.
- Fix B, robust standard errors (HC0–HC3): keep OLS β̂, fix only the standard errors. Angrist & Pischke prefer this one.
The noise around the line is bigger in some places than in others.
Food spending against income: low-income households all spend similar amounts on food, while high-income households vary a lot (some eat out every day, some don't). The cloud of points fans out as income grows.
Var(εᵢ | xᵢ) = σᵢ² is not constant. If E[ε | X] = 0, OLS stays unbiased, but Var(β̂ | X) = (XᵀX)⁻¹XᵀΩX(XᵀX)⁻¹ ≠ σ²(XᵀX)⁻¹, so the usual SEs are wrong. HC estimators plug ε̂ᵢ² into Ω. OLS is no longer BLUE; GLS/WLS with the right weights is.
4 · Clustered errors
People in the same school or town share shocks. Cluster your standard errors when treatment is assigned at group level, or when you sampled groups first and people second.
See it: the regression playground
Fifty made-up people. The dashed orange line is the true relationship used to create the data. The solid blue line is what OLS estimates from this one sample. Change one thing at a time and watch what moves.
- Raise the noise. The blue line wobbles more from sample to sample (press New sample), and the SE grows. On average it is still right.
- Turn on heteroskedasticity, then press "Repeat 500 samples". The average slope stays at the truth (no bias), but the usual SE understates the real spread, so its "95 %" CI covers the truth only around 90 % of the time. The robust SE is much closer (robust SEs are themselves slightly optimistic with only 50 points).
- Raise the ability correlation. The blue line gets steeper than the truth in every sample: omitted variable bias. Repeating samples doesn't fix it, and the CIs miss the truth almost every time.
- Add the outlier. One far-right point with a big residual drags the whole line. Points far from the mean of x have high leverage.
Choosing a model deeper
- Occam's razor: among similar models, prefer the simpler one (especially for explanation). For prediction, a bigger model can be fine.
- Automatic selection by p-values is risky. After many tests the p-values are no longer valid and results look better than reality. Freedman (1983): with p = 50 variables and n = 100, you find "patterns" in pure noise.
- Information criteria = fit minus a penalty for complexity.
AIC = −2·logL + 2pandBIC = −2·logL + p·log n, where p is the number of parameters. Because log n > 2 once n ≥ 8, BIC punishes extra parameters harder and prefers smaller models. Lower is better. - Note on the slides: one slide writes the BIC penalty as 2p·log n. That is not the standard definition; treat it as a typo or a non-standard scaling, and use
p·log nunless your lecturer says otherwise. Model rankings between AIC and BIC can differ either way. - If models that fit equally well give very different answers, the honest conclusion may be that the data can't answer the question.
What p-values and CIs actually mean
Your lecturer spends several slides on this, so expect it in an exam.
- "p = 0.08 means an 8 % chance that chance alone produced the result."
- "p = 0.01 means the null hypothesis has a 1 % chance of being true."
- "p ≤ 0.05 means the null is false."
- "This 95 % CI has a 95 % chance of containing the true value."
- "If two CIs overlap, the difference isn't significant."
p-value: If the model is right and the null hypothesis is true, then in repeated samples you'd see a test statistic at least this extreme about p·100 % of the time. It measures how compatible your data are with the model + null.
95 % CI: a recipe that, in repeated samples, produces intervals that cover the true value about 95 % of the time. Any single interval either covers it or doesn't.
Statistical ≠ practical significance. A tiny effect can be "significant" with lots of data. Always ask whether the size matters economically.
A standard error tells you how much your estimate would change if you collected a different sample of people.
Imagine surveying 100 students to estimate their average study time. If you repeatedly picked different groups of 100, their averages would differ a little each time. The standard error describes how much those averages typically vary.
The standard error is the estimated standard deviation of an estimator's sampling distribution. For an i.i.d. sample mean it is s/√n. For OLS, se(β̂ⱼ) = √[σ̂²(XᵀX)⁻¹]ⱼⱼ under constant variance, or the square root of the sandwich (XᵀX)⁻¹(Σ ε̂ᵢ² xᵢxᵢᵀ)(XᵀX)⁻¹ for robust SEs.
How surprising your result would be if there were really no effect, assuming your model is right. Small p = surprising.
A friend says a coin is fair. You flip it 20 times and get 16 heads. If the coin really were fair, a result at least that lopsided (16+ heads or 16+ tails) happens about 1.2 % of the time. So p ≈ 0.012: hard to square with "fair", but not impossible.
p = P(|T| ≥ |tobs| ; H₀ and the model), using T's null distribution (e.g. tn−p). If H₀ and the model hold, p is uniformly distributed on [0, 1]. It is not P(H₀ | data), and it says nothing about effect size.
Other bits from the slides deeper
- Logistic regression: for 0/1 outcomes (got a job, passed a test). Models log(p/(1−p)), so predicted probabilities stay between 0 and 1.
- Poisson regression: for counts (0, 1, 2…). Models log(mean).
- GLMs are the family that includes normal, logistic and Poisson: a linear predictor η = Xβ plus a "link" that connects it to the mean.
- PCA: squeeze many correlated x's into a few uncorrelated "components" that keep most of the variance.
- Missing values: deletion, imputation, matching. First ask why they're missing.
- Practical habits: always plot the data, ask how it was collected, never edit the raw file, comment your code.
OLS regression in five questions
- What problem are we solving?Describe how the average of Y changes with X, either to predict or to compare people who differ in X but are alike in the other regressors.
- How does the method solve it?Pick the line (or plane) that makes the sum of squared residuals as small as possible: β̂ = (XᵀX)⁻¹Xᵀy.
- What must hold?
- Linear in the β's, no perfect collinearity.
- Exogeneity, E[ε | X] = 0: nothing left in ε is related to X. Needed for unbiased (and causal) slopes.
- Constant variance and independent errors: needed for the usual standard errors.
- Normal errors: only for exact small-sample t and F tests.
- What happens when it fails?
- Omitted variable or reverse causality: β̂ is biased, and more data doesn't help.
- Heteroskedasticity or clustering: β̂ is fine, the usual SEs are wrong.
- Collinearity: β̂ is unbiased but very noisy.
- How can we check?
- Plot residuals against fitted values and each x.
- See whether the coefficient moves when plausible confounders are added.
- Use robust (or clustered) SEs by default.
- Ask what is missing from the model, and in which direction it would push β̂.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 1 · Wages, education and the missing "ability" about 15 min
# Lab 1: wages, education and the omitted "ability"
library(sandwich); library(lmtest); library(ggplot2)
set.seed(1)
n <- 500
ability <- rnorm(n) # unobserved in real life
educ <- round(12 + 1.5 * ability + rnorm(n, 0, 2)) # able people study longer
exper <- round(runif(n, 0, 30))
e <- rnorm(n, 0, 0.15 + 0.01 * exper) # noise grows with experience
logwage <- 1 + 0.08 * educ + 0.02 * exper + 0.10 * ability + e
d <- data.frame(logwage, educ, exper, ability)
long <- lm(logwage ~ educ + exper + ability, data = d) # if ability were measured
short <- lm(logwage ~ educ + exper, data = d) # what we can actually run
round(rbind(long = coef(long)[2:3], short = coef(short)[2:3]), 3)
# Omitted variable bias formula: beta3 * gamma1
gamma1 <- coef(lm(ability ~ educ + exper, data = d))["educ"]
round(c(gap = unname(coef(short)["educ"] - coef(long)["educ"]),
formula = unname(coef(long)["ability"] * gamma1)), 4)
# Robust (heteroskedasticity-consistent) standard errors
coeftest(short, vcov = vcovHC(short, type = "HC1"))
# Frisch-Waugh-Lovell: remove exper from educ, then a simple regression
educ_resid <- resid(lm(educ ~ exper, data = d))
round(coef(lm(logwage ~ educ_resid, data = d))[2], 4)
# Residual plot: look for curves or a fan shape
ggplot(data.frame(fitted = fitted(short), resid = resid(short), exper = d$exper),
aes(exper, resid)) +
geom_point(alpha = 0.5) + geom_hline(yintercept = 0, linetype = 2) +
labs(x = "Experience (years)", y = "Residual") + theme_minimal()
What you should see
educ exper
long 0.086 0.019
short 0.108 0.019
gap formula
0.0219 0.0219
t test of coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.68394 0.07470 9.16 <2e-16 ***
educ 0.10809 0.00593 18.24 <2e-16 ***
exper 0.01886 0.00172 10.95 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
educ_resid
0.1081
How to read it
longvsshort: leaving out ability raises the education slope from 0.086 to 0.108. The truth used to simulate the data is 0.08.gap=formula= 0.0219: the omitted-variable-bias formula β₃·γ₁ explains the difference exactly.- The robust SE for educ (0.00593) is almost the usual one (0.00594): here the noise grows with experience, not education, so it hardly matters for this slope. Always check rather than assume.
- Frisch–Waugh–Lovell: regressing log wage on the part of education not explained by experience gives 0.1081, the same as the multiple regression.
Common mistakes
- Calling
summary(m)and forgetting that its SEs assume constant variance. Usecoeftest(m, vcov = vcovHC(m, type = "HC1")). - Reading 0.108 as "education raises wages by 10.8 %". With ability missing, it is an association that overstates the effect.
- Reading log coefficients as levels. In
log(wage) ~ educ, 0.108 ≈ 10.8 % per year (exactly 100·(e^0.108 − 1) = 11.4 %).
Warm-up: reading a regression table line by line
Call:
lm(formula = logwage ~ educ + exper)
Residuals:
Min 1Q Median 3Q Max
-1.2850 -0.1863 0.0006 0.2086 1.3007
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.68394 0.07723 8.86 <2e-16 ***
educ 0.10809 0.00594 18.20 <2e-16 ***
exper 0.01886 0.00173 10.93 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.341 on 497 degrees of freedom
Multiple R-squared: 0.471, Adjusted R-squared: 0.469
F-statistic: 221 on 2 and 497 DF, p-value: <2e-16
2.5 % 97.5 %
(Intercept) 0.53220 0.83568
educ 0.09642 0.11975
exper 0.01547 0.02225
- Estimate: 0.108 for educ, holding exper fixed. Std. Error: 0.00594. t value = 0.10809 / 0.00594 = 18.2. Pr(>|t|): p-value for H₀: β = 0.
- Residual standard error 0.341 on 497 degrees of freedom = n − 3 parameters (500 − 3).
- Multiple R-squared 0.471: the model explains 47 % of the variation in log wages (in sample, with an intercept).
- F-statistic 221: tests whether all slopes are zero at once.
confint(m): educ [0.0964, 0.1198] = estimate ± about 1.96 × SE.
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
In log(wage) = 0.5 + 0.08·educ, what does 0.08 mean?
One more year of education is associated with about 8 % higher wage (log–level ⇒ β·100 %).
When does leaving out "ability" NOT bias the education coefficient?
When ability has no effect on wages, or when ability is uncorrelated with education. Either one is enough.
Is y = β₀ + β₁x + β₂x² a linear model?
Yes. It's linear in the β's, even though the curve isn't straight.
Why is a prediction interval wider than a confidence interval for the mean?
It adds the new observation's own error ε₀, the "1 +" inside the square root.
Your errors get bigger as x grows. Name two fixes.
Weighted least squares (weights = 1/variance), or keep OLS and use heteroskedasticity-robust standard errors.
- Faraway: ch. 2 (estimation, Gauss–Markov, R²), ch. 3 (tests and CIs), ch. 4 (diagnostics), §5.3 (collinearity), §6.1–6.2 (GLS/WLS), ch. 8 (variable selection), §9.1 (PCA), ch. 12 (missing data).
- Mastering 'Metrics: ch. 2 "Regression", especially the omitted variables bias part. Very readable.
Maximum likelihood
Maximum likelihood asks: which parameter value would make the data I actually saw most probable? That value is your estimate.
Gist mode is on: deeper sections are hidden.
- Probability distribution
- A rule saying how likely each possible value is (e.g. binomial for coin flips).
- i.i.d.
- Independent, identically distributed: separate draws from the same distribution.
- Derivative
- The slope of a curve. It's 0 at the top of a hill, which is how we find maximums.
- log(a·b)
- = log a + log b. Logs turn products into sums.
- Π and Σ
- Π = multiply them all; Σ = add them all.
If you remember only 3 things
- Likelihood = probability of your data, viewed as a function of the parameter. The data are fixed; you slide the parameter around.
- Take logs, take the derivative, set it to zero. Logs turn a product into a sum, which is much easier.
- A sharp peak means a precise estimate. The curvature at the top (Fisher information) gives you the standard error.
After this lecture you can…
- FoundationalExplain the difference between a probability and a likelihood.
- FoundationalFind a simple MLE: write ℓ(θ), take the derivative, set it to zero.
- IntermediateGet a standard error from the curvature of the log-likelihood.
- IntermediateInterpret logistic-regression coefficients as changes in log-odds.
- AdvancedUse Fisher information, the Cramér–Rao bound and the information matrix equality.
On this page
The coin example
You flip a coin 500 times and get 97 heads and 403 tails. Is it fair?
If p = 0.5, seeing exactly 97 heads has probability about 9.3 × 10⁻⁴⁶. Basically impossible. So which p fits best?
- Probability of the data:
L(p) = C · p⁹⁷ (1−p)⁴⁰³(C is a constant that doesn't depend on p). - Take logs:
ℓ(p) = 97·log p + 403·log(1−p) + const. - Derivative = 0:
97/p − 403/(1−p) = 0. - Solve:
p̂ = 97/500 = 0.194.
The MLE is just the share of heads. Most simple MLEs match your intuition like this.
See it: the likelihood explorer
Choose how many flips and heads you saw. The curve shows how well each p explains those data (log-likelihood, with the top set to 0). Drag your guess and read the slope (the score): it points towards the top.
The shaded band marks the p values within 1.92 log-points of the top: a likelihood-based 95 % interval. Keep the share of heads fixed and raise n: the hill gets sharper and the band and SE shrink. That sharpness is the Fisher information.
How well a particular parameter value explains the data you actually saw.
You saw 15 heads in 50 flips. If p were 0.5, that result would be fairly unlikely. If p were 0.3, it would be as likely as it can be. The likelihood gives every candidate p a score; the MLE is the top scorer.
L(θ) = ∏ f(xᵢ | θ), read as a function of θ with the data fixed. It is not a probability distribution over θ (it needn't integrate to 1). θ̂ = argmax ℓ(θ); the observed information −ℓ″(θ̂) gives SE(θ̂) ≈ 1/√(−ℓ″(θ̂)).
The recipe
- Pick a distribution for your data with an unknown parameter θ:
f(x | θ). - Write the likelihood. With independent (i.i.d.) observations it's a product:
L(θ) = f(X₁|θ) · f(X₂|θ) · … · f(Xₙ|θ). Independence is what lets you multiply. - Take logs:
ℓ(θ) = Σ log f(Xᵢ|θ). Same maximum, numerically more stable. - Set the derivative (the score) to zero and solve. If there's no formula, a computer climbs the hill numerically.
A detective with fixed evidence (the data) trying out different suspects (parameter values). The best suspect is the one under whom this evidence is least surprising.
Waiting-time example (exponential)
100 waiting times between insurance claims. Model: exponential with mean λ, f(x|λ) = e^(−x/λ) / λ.
ℓ(λ) = −n·log λ − n·X̄/λ- Score:
−n/λ + n·X̄/λ² = 0 - λ̂ = X̄, the sample mean.
Challenger: why logistic regression needs ML
Each shuttle launch had 6 O-rings; some failed. We want the failure probability pᵢ as a function of temperature.
- A straight line
pᵢ = β₀ + β₁·tempcan predict probabilities below 0 or above 1. Bad. - Fix: model the log-odds,
log(pᵢ/(1−pᵢ)) = β₀ + β₁·tempᵢ. Any β gives a p between 0 and 1. - Randomness: failures yᵢ ~ Binomial(6, pᵢ). Now the model is fully described except for β₀, β₁.
- Likelihood: multiply
C(6,yᵢ)·pᵢ^yᵢ·(1−pᵢ)^(6−yᵢ)across launches, take logs, and let the computer find the β's that maximise it.
The vocabulary of the hill deeper
| Term | Formula | Plain meaning |
|---|---|---|
| Score | Sₙ(θ) = ∂ℓₙ/∂θ | Slope of the log-likelihood. Zero at the top. |
| Hessian | ∂²ℓₙ/∂θ∂θᵀ | Second derivative: how curved the hill is. At a maximum it is negative (the hill curves down). |
| Observed information | Hₙ(θ) = −∂²ℓₙ/∂θ∂θᵀ | Minus the Hessian, so it's positive. Sharper hill, more information, smaller SE ≈ 1/√Hₙ(θ̂). (The slides call this Hₙ.) |
| Efficient score | S = ∂ log f(X|θ₀)/∂θ | Score of one observation at the true θ. Its average is 0. |
| Fisher information | J = E[SSᵀ] | Variance of the score: how much one observation tells you about θ. |
| Expected Hessian | H = −E[∂² log f/∂θ∂θᵀ] | Average curvature for one observation. |
Information matrix equality: if the model is correctly specified, J = H. Exponential example: both equal 1/λ₀².
Why the MLE is approximately normal: a Taylor expansion of the score around θ₀ gives √n(θ̂ − θ₀) → N(0, H⁻¹JH⁻¹) = N(0, J⁻¹). So for large n, Var(θ̂) ≈ 1/(n·J). For the exponential: Var(λ̂) ≈ λ²/n, so SE ≈ λ̂/√n.
Why people love MLE
- Consistent: θ̂ → true θ as n grows (needs i.i.d. data, identification and some regularity conditions).
- Asymptotically normal: you can build t-tests and confidence intervals.
- Asymptotically efficient: it hits the Cramér–Rao lower bound, the smallest variance any unbiased estimator can have:
Var(θ̃) ≥ (nJ)⁻¹. - Invariant: if θ̂ is the MLE of θ, then h(θ̂) is the MLE of h(θ). Example: the MLE of p² is p̂².
Link back to lecture 1
If y = Xβ + ε with normal errors, the log-likelihood is a constant minus (1/2σ²)·Σ(yᵢ − xᵢβ)². Maximising it is the same as minimising the sum of squared residuals.
So OLS = MLE when errors are normal. (Small difference: MLE estimates σ² = ε̂ᵀε̂/n, dividing by n instead of n − p.)
Maximum likelihood in five questions
- What problem are we solving?Estimate the parameters of a model for the whole distribution of the data (coin flips, waiting times, 0/1 outcomes).
- How does the method solve it?Choose the θ that makes the observed data most probable: maximise ℓ(θ) = Σ log f(xᵢ | θ). The curvature at the top gives the SE.
- What must hold?
- The assumed distribution is (close to) right.
- Independent observations, so the likelihood is a product.
- The parameter is identified and not on the edge of its range.
- Large n for the normal approximation and SE ≈ 1/√information.
- What happens when it fails?
- Wrong distribution: θ̂ targets a "pseudo-true" value and Hessian-based SEs are wrong (use the sandwich H⁻¹JH⁻¹).
- Small n or a boundary: the normal approximation is poor.
- How can we check?
- Compare fitted and observed distributions.
- Compare the observed information with the outer product of scores (information matrix equality).
- Try alternative distributions and report robust SEs.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 2 · Maximum likelihood by hand, then with glm() about 15 min
# Lab 2: maximum likelihood by hand, and with glm()
set.seed(2)
wait <- rexp(100, rate = 1/5) # 100 waiting times, true mean 5 minutes
# Negative log-likelihood of the exponential with mean lambda
nll <- function(lambda) -sum(dexp(wait, rate = 1/lambda, log = TRUE))
fit <- optim(4, nll, method = "Brent", lower = 0.1, upper = 50, hessian = TRUE)
c(mle = fit$par, sample_mean = mean(wait))
# Standard error from the curvature (Hessian of the negative log-likelihood)
c(se_hessian = sqrt(1 / fit$hessian[1, 1]), se_formula = fit$par / sqrt(100))
# Logistic regression is also fitted by maximum likelihood
launch <- data.frame(temp = round(runif(23, 12, 27), 1))
p <- plogis(5 - 0.35 * launch$temp)
launch$fail <- rbinom(23, size = 6, prob = p) # failed O-rings out of 6
m <- glm(cbind(fail, 6 - fail) ~ temp, family = binomial, data = launch)
round(summary(m)$coefficients, 3)
round(exp(coef(m)["temp"]), 3) # odds multiplier per +1 degree
round(predict(m, data.frame(temp = c(10, 20)), type = "response"), 3)
logLik(m)
What you should see
mle sample_mean
5.034 5.034
se_hessian se_formula
0.5034 0.5034
Estimate Std. Error z value Pr(>|z|)
(Intercept) 4.569 1.089 4.195 0
temp -0.327 0.065 -5.021 0
temp
0.721
1 2
0.785 0.122
'log Lik.' -24.65 (df=2)
How to read it
- The numerical MLE of the exponential mean (5.034) equals the sample mean, as the algebra says.
- The SE from the Hessian of the negative log-likelihood (0.5034) equals the formula λ̂/√n.
- Logistic regression: the temperature coefficient is −0.327 on the log-odds scale; exp(−0.327) = 0.721, so each extra degree multiplies the odds of an O-ring failure by about 0.72.
- Predicted failure probability: 0.785 at 10 °C, 0.122 at 20 °C.
logLik= −24.65 with 2 parameters.
Common mistakes
optim()minimises, so give it the negative log-likelihood. Its Hessian is then the observed information (no minus sign needed).- Forgetting
log = TRUEindexp(): multiplying many small densities underflows to 0. - Mixing up parameterisations:
dexpuses the rate 1/λ, not the mean λ. - Reading logit coefficients as changes in probability.
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
What's the difference between a density and a likelihood?
Same formula. A density is a function of the data with the parameter fixed. A likelihood is a function of the parameter with the data fixed.
Why do we take logs?
A product of many small numbers becomes a sum, which is easier to differentiate and numerically stable. The maximiser doesn't change because log is increasing.
You flip 200 times and get 50 heads. MLE of p?
50/200 = 0.25.
Two log-likelihoods peak at the same θ̂, but one is much more sharply curved. Which estimate is more precise?
The sharply curved one. More curvature = more Fisher information = smaller variance (≈ 1/(nJ)).
When is OLS the same as MLE?
When the errors are normally distributed (with constant variance).
- Neither of your two books covers MLE in depth. The lecture recommends: Lanot, "Maximum likelihood and economic modeling", IZA World of Labor 326 (very non-technical, start here), and Hansen's Probability, ch. 10.
- Faraway ch. 2 is useful for the OLS = MLE connection.
The bootstrap
You only have one sample, but you want to know how much your estimate would change with a different sample. So you pretend your sample is the population and resample from it, many times.
Gist mode is on: deeper sections are hidden.
- Sampling distribution
- How an estimate (like a mean) would be spread out over many different samples.
- Standard error
- The standard deviation of that sampling distribution.
- Percentile
- The value below which that % of numbers fall (2.5th percentile: 2.5 % are lower).
- With replacement
- After picking a value you put it back, so it can be picked again.
If you remember only 3 things
- Resample n observations from your data, with replacement. Some values appear twice, some not at all.
- Recompute your estimate on each resample, thousands of times. The spread of those estimates is your standard error. Their 2.5th and 97.5th percentiles give a 95 % CI.
- It needs few assumptions but can fail, e.g. when the parameter sits on the edge of what's allowed, or for the sample maximum.
After this lecture you can…
- FoundationalRun the bootstrap algorithm by hand: resample, recompute, repeat.
- FoundationalRead a bootstrap SE and a percentile confidence interval.
- IntermediateJudge when the bootstrap helps and when it fails (tiny n, the maximum, boundaries).
- AdvancedExplain bootstrap consistency, pivotal statistics and subsampling.
On this page
Try it yourself
Here are 9 waiting times (days between insurance claims). The estimate is the mean. Press the buttons and watch the bootstrap distribution build up.
Notice: the histogram of means is much narrower than the data itself, and it's lopsided because two big values (13.95, 18.03) drag some resamples up. A normal approximation would miss that lopsidedness. But with only 9 values, the whole picture rests on those 9 numbers: the bootstrap can't tell you about values you never observed.
Use the menu to swap in a fresh sample of 9, 30 or 100 from a skewed population whose true mean is 5. Then press Repeat the whole study: it checks how often the "95 %" interval really contains 5. With 9 values it falls clearly short; with 100 it gets close.
The algorithm
- Draw a sample of size n from your data (the empirical distribution F̂ₙ), with replacement.
- Compute your statistic on it: T*.
- Repeat B times (say 1,000–10,000).
- Use the B values of T*: their standard deviation = SE, their percentiles = CI, their mean minus the original = bias estimate.
You can't go back and survey the country again. But your 1,000 respondents are a mini-version of the country. Drawing from them with replacement is like running the survey again in that mini-country.
How much your estimate jumps around when you recompute it on reshuffled copies of your own data.
Your 9 claim times are a mini-population. Draw 9 of them with replacement, compute the mean, and do that 1,000 times. The spread of those 1,000 means mimics how the mean would vary if you could collect new samples.
SEboot = SD of T*₁, …, T*B, where each T* is computed on a sample from the empirical distribution F̂ₙ. It is consistent when Gₙ(·, F̂ₙ) → Gₙ(·, F₀): smooth statistics of i.i.d. data. B only controls Monte Carlo error.
The key logic: F̂ₙ is close to the true F, so how estimates vary when sampling from F̂ₙ is close to how they vary when sampling from F. More resamples B only make the simulation more accurate; they don't add real information.
When to reach for it
- Moderately small samples where "large n" formulas are shaky (e.g. n = 30–50 with skewed data), provided the sample still represents the population reasonably well.
- Complicated estimator with no known formula for its variance (e.g. the output of a numerical optimisation).
- Formula depends on unknowns. The sample median's variance is 1/(4n·f(m)²), which needs the unknown density f at the median.
- Dubious assumptions, e.g. stock returns with fat tails, where normality is wrong.
It resamples the data you have; it can't create information that isn't there. With very few observations (like the 9 in the demo above), the sample is a crude stand-in for the population, the bootstrap distribution is lumpy, and 95 % intervals often cover the truth less than 95 % of the time. Its guarantees are themselves large-sample results. A small sample stays a small sample.
The stamp example deeper
Question: were stamps printed on one type of paper or several? If one, the distribution of thickness should have one mode (one peak).
- H₀: number of modes = 1. Find ĥ₁ = 0.0068, the smallest smoothing level at which the estimated density has one peak.
- Draw bootstrap samples from a smoothed version of the data that has exactly one mode (a smooth bootstrap, adjusted to keep the right variance).
- For each, compute ĥ₁*. Count how often ĥ₁* ≥ ĥ₁: that share is the achieved significance level.
- With B = 5,000 it came out at 0.0002 < 5 %, so reject "one paper type".
Lesson: choosing a clever test statistic and null hypothesis improves the power of the test.
When the bootstrap fails
- Parameter on the boundary. E.g. X ~ N(μ, 1) but μ is only allowed to be ≥ 0, and the true μ = 0 (Andrews, 2000).
- Sample maximum. If data are uniform on [0, θ], the bootstrap max equals the sample max with probability 1 − (1 − 1/n)ⁿ → 1 − 1/e ≈ 63 %. The real sampling distribution has no such spike. So the bootstrap gets it wrong.
- The paradox: we want the bootstrap in complicated situations, but those are exactly where it's hardest to prove it works.
Fix: subsampling. Draw smaller samples (size m < n) without replacement. Works more generally, but is less efficient when the normal bootstrap works, and you have to choose m.
The theory words deeper
- Gₙ(τ, F₀): true distribution (CDF) of your test statistic. Unknown because F₀ is unknown.
- Bootstrap estimate: Gₙ(τ, F̂ₙ). Plug in the data's distribution instead of the true one.
- Pivotal: the statistic's distribution doesn't depend on F at all. Asymptotically pivotal: true in large samples (e.g. a t-statistic).
- Bootstrap consistency: Gₙ(·, F̂ₙ) → Gₙ(·, F₀) as n → ∞. Beran & Ducharme's sufficient conditions: F̂ₙ is a good estimate of F₀, the limit distribution is continuous in τ, and it changes smoothly with F.
The bootstrap in five questions
- What problem are we solving?Get a standard error or confidence interval for an estimator that has no simple formula, or whose formula needs unknowns.
- How does the method solve it?Treat the sample as the population: draw n observations with replacement, recompute the statistic, repeat B times, and use the spread.
- What must hold?
- The sample represents the population well (n not tiny).
- Observations are independent (or the resampling copies the dependence, e.g. block bootstrap).
- The statistic depends smoothly on the data (not a maximum, not a boundary parameter).
- What happens when it fails?
- Tiny n: intervals cover the truth less often than they claim.
- Maximum or boundary: the bootstrap distribution is wrong (spike at the sample maximum).
- Dependent data resampled as if independent: SEs too small.
- How can we check?
- Is the statistic smooth? Is the data independent?
- Compare with an analytic SE where one exists.
- Try subsampling (m < n, without replacement) when in doubt.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 3 · Bootstrapping a median with the boot package about 10 min
# Lab 3: bootstrapping a median with the boot package
library(boot); library(ggplot2)
set.seed(3)
basket <- round(rlnorm(20, meanlog = 3.5, sdlog = 0.8), 1) # 20 skewed basket values (EUR)
c(mean = mean(basket), median = median(basket))
med_fun <- function(data, idx) median(data[idx]) # statistic on a resample
b <- boot(basket, med_fun, R = 2000)
b # original, bias, std. error
boot.ci(b, type = c("norm", "perc", "bca"))
# Histogram of the 2,000 bootstrap medians
ggplot(data.frame(med = b$t[, 1]), aes(med)) +
geom_histogram(bins = 30, fill = "grey60", colour = "white") +
geom_vline(xintercept = b$t0, linetype = 2) +
labs(x = "Bootstrap median (EUR)", y = "Count") + theme_minimal()
# Where the bootstrap fails: the sample maximum
x <- runif(50, 0, 10)
bmax <- boot(x, function(d, i) max(d[i]), R = 2000)
mean(bmax$t[, 1] == max(x)) # share of resamples that repeat the sample max
What you should see
mean median
35.23 30.05
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = basket, statistic = med_fun, R = 2000)
Bootstrap Statistics :
original bias std. error
t1* 30.05 -0.4944 6.562
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 2000 bootstrap replicates
CALL :
boot.ci(boot.out = b, type = c("norm", "perc", "bca"))
Intervals :
Level Normal Percentile BCa
95% (17.68, 43.41 ) (18.50, 38.80 ) (18.50, 38.70 )
Calculations and Intervals on Original Scale
[1] 0.653
How to read it
original30.05 is the sample median;std. error6.56 is the SD of the 2,000 bootstrap medians.- The normal interval (17.68, 43.41) is symmetric by construction. The percentile and BCa intervals (about 18.5 to 38.8) follow the skewed bootstrap distribution, so prefer those here.
- Last line: 65 % of bootstrap maxima equal the sample maximum (theory: about 63 %). The bootstrap fails for the maximum.
Common mistakes
- The statistic function must take
(data, idx)and usedata[idx]. - Reporting
biasas if it were the uncertainty. The uncertainty is thestd. error. - Forgetting
set.seed(): results change slightly on every run. - Resampling time-series or clustered data as if observations were independent (use block or cluster bootstrap).
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
Why "with replacement"?
Without replacement, a sample of size n from n values is just the original data shuffled. The statistic would never change.
Does increasing B (number of resamples) make your estimate more precise?
No. It only reduces simulation noise in the bootstrap distribution. Precision depends on the real sample size n.
How do you get a 95 % percentile confidence interval?
Sort the B bootstrap estimates and take the 2.5th and 97.5th percentiles.
Name one case where the bootstrap fails, and a fix.
Sample maximum, or a parameter on the boundary. Fix: subsampling (smaller samples without replacement).
- Not in your two books. The lecture recommends Efron & Tibshirani, An Introduction to the Bootstrap, and these animations: Wild's bootstrap animations.
- Short blog explanation: Wasserman, bootstrapping and subsampling (part I).
- Faraway §3.3 (permutation tests) uses the same "simulate from your own data" idea.
Intro to causality
To know if something causes an outcome, you need to compare people who are alike in every way except the treatment. Randomness is what makes that comparison fair.
Gist mode is on: deeper sections are hidden.
- Treatment
- Whatever's being tested: a policy, a drug, a school, clean water. D = 1 if you got it.
- Independent (⫫)
- Knowing one thing tells you nothing about the other.
- E[Y | D = 1]
- Average Y among people with D = 1.
- Confounder
- A hidden factor that affects both treatment and outcome, so it fakes or hides an effect.
If you remember only 3 things
- Everyone has two potential outcomes, Y(1) with treatment and Y(0) without, but we only ever see one. That's the fundamental problem of causal inference.
- Random assignment makes the treated and untreated groups comparable, so a simple difference in means gives the causal effect.
- When you can't experiment, find a natural source of randomness: a lottery, nature, a law change, a cut-off. Lectures 5–7 are three ways to use one.
After this lecture you can…
- FoundationalDefine potential outcomes and the fundamental problem of causal inference.
- FoundationalExplain why random assignment turns a difference in means into a causal effect.
- IntermediateSplit a naive comparison into ATT + selection bias.
- IntermediateDecide what to control for: confounders yes, mediators and colliders no.
- AdvancedCompute a randomisation-inference p-value.
On this page
Correlation ≠ causation
The 2021 Economics Nobel (Card, Angrist, Imbens) went to the "credibility revolution": methods for getting causal answers from real-world data. This lecture is the trailer for the rest of the course.
Potential outcomes
Imagine we could see both versions of each person:
| Y(1) treated | Y(0) untreated | Effect Y(1) − Y(0) | |
|---|---|---|---|
| Adam | 4 | 6 | −2 |
| Boris | 5 | 7 | −2 |
| Cyril | 8 | 6 | +2 |
| Diana | 3 | 7 | −4 |
| Ema | 3 | 4 | −1 |
| Filip | 1 | 3 | −2 |
In reality, half the cells are missing. If Adam is treated, we see his 4 and never his 6. If Cyril isn't, we see his 6 and never his 8. Individual effects are never observed.
The six effects are −2, −2, +2, −4, −1, −2. They sum to −9, so the true average effect (ATE) is −9 / 6 = −1.5.
- A coin puts Adam, Boris and Ema in the treated group. We see their Y(1): (4 + 5 + 3)/3 = 4.0.
- We see the others' Y(0): Cyril, Diana, Filip = (6 + 7 + 3)/3 = 5.33.
- Estimate = 4.0 − 5.33 = −1.33. A different coin (Cyril, Diana, Filip treated) gives 4.0 − 5.67 = −1.67.
Neither single draw hits −1.5 exactly, but averaged over all 20 ways to split six people into three and three, the estimate is exactly −1.5. Randomisation makes the estimate right on average (unbiased), not right every time.
(An earlier version of this guide said the truth was "about −1.6". The table's own numbers give exactly −1.5.)
- ⫫
- "is independent of"
- D
- treatment indicator (1 = treated, 0 = not)
Who gets treated has nothing to do with how they would do either way. That's what a coin flip guarantees.
Why a naive comparison misleads: selection bias
Without randomisation, comparing the treated with the untreated mixes two things:
- ATT
- average treatment effect on the treated: E[Y(1) − Y(0) | D = 1]
- second term
- selection bias: how different the treated would have been even without treatment
Step by step: subtract and add E[Y(0)|D=1]. The first pair is the ATT; the second pair compares the two groups' untreated outcomes. Randomisation makes that second pair zero.
People with high Y(0) were more likely to choose treatment. The naive difference was 16.78. Split it: ATT = 5.18, selection bias = 11.60. Check: 5.18 + 11.60 = 16.78. Two-thirds of the "effect" was just who chose to be treated.
See it: coin flip vs choosing for yourself
Two hundred simulated people, each with a Y(0) and a Y(1). The true average effect is about 5. Assign treatment by coin flip, or let people choose: those with a high Y(0) are more likely to sign up.
The treated group would have been different even without the treatment.
People who join a gym are already healthier than people who don't. Comparing their health a year later mixes the gym's effect with who chose to join.
E[Y | D=1] − E[Y | D=0] = ATT + {E[Y(0) | D=1] − E[Y(0) | D=0]}. Random assignment makes D independent of Y(0), so the second term is zero.
Statistical vs causal inference
| Statistical inference | Causal inference | |
|---|---|---|
| Question | How sure are we about this number, given sampling noise? | Is this number the effect of changing D? |
| Tools | Standard errors, confidence intervals, p-values (lectures 1–3) | Randomisation, IV, RDD, DiD (lectures 4–7) |
| More data helps? | Yes: SEs shrink | No: a biased comparison stays biased with a million rows |
A very precise estimate (tiny SE) can still be the wrong kind of number. Precision and credibility are separate questions; always answer both.
What to control for: confounders, mediators, colliders
"Add more controls" is not always safer. It depends on where a variable sits in the causal picture (a DAG, directed acyclic graph: arrows mean "causes").
| Variable | Example | Control for it? |
|---|---|---|
| Confounder (causes D and Y) | Ability, when D = education, Y = wage | Yes. Leaving it out causes omitted variable bias. |
| Mediator (on the path D → M → Y) | Occupation, if education raises wages partly by getting you a better job | No, if you want the total effect. Controlling for it removes part of the effect you're measuring. |
| Collider (caused by D and Y) | Admission to a selective university, caused by both test scores and motivation | No. Conditioning on it creates a fake link between D and Y. |
Among admitted students only, high test scores and high motivation look negatively related: anyone admitted with low scores must have had high motivation, and vice versa. In the whole population there may be no link at all. Studying only the admitted (selecting on a collider) invents the correlation.
Randomisation inference deeper
In an experiment you can get a p-value without any normal approximation. Take the sharp null: treatment has no effect on anyone, so Y(1) = Y(0) for every person and all outcomes are known.
- Compute your observed difference in means.
- Re-run the random assignment many times on paper (all splits, or a few thousand), keeping outcomes fixed, and recompute the difference each time.
- The p-value is the share of re-assignments with a difference at least as extreme as the one you observed.
The uncertainty here comes from which units were assigned to treatment, not from sampling from a population. It's exact in small experiments and is the logic behind permutation tests (Faraway §3.3). Same "simulate the world under H₀" idea as the bootstrap, with a different source of randomness.
The problem: we usually can't randomise
Questions where a proper experiment is impossible or unethical:
- Do veterans earn less because of the war?
- Does education increase wages?
- How does the minimum wage affect unemployment?
- Does class size affect learning? Does alcohol cause car crashes?
Option 1: compare similar people, Y(1), Y(0) ⫫ D | X ("random once we control for X"). Works only if you've measured everything that matters.
Option 2: find a source of randomness, a "quasi-experiment".
The quasi-experiment idea
Some Z (a lottery, a birth date, the weather) pushes the treatment D, and touches Y only through D:
Then: β̂ = effect(Z→Y) / effect(Z→D) = Cov(Y,Z)/Cov(D,Z). With a 0/1 Z: (E[Y|Z=1] − E[Y|Z=0]) / (E[D|Z=1] − E[D|Z=0]). This is exactly the IV estimator of lecture 5.
·
Famous examples (you'll meet them again)
| Study | Source of randomness | Method |
|---|---|---|
| Vietnam veterans' earnings (Angrist 1990) | Draft lottery by birth date | IV |
| Returns to schooling (Angrist & Krueger 1991) | Quarter of birth affects years of compulsory school. Result: about 7 % per year | IV |
| Demand for fish (Angrist, Graddy & Imbens 2000) | Weather at sea shifts supply | IV |
| Class size (Angrist & Lavy 1999) | Maimonides' rule: 40 pupils → 1 class, 41 → 2 classes | RDD / IV |
| Minimum wage (Card & Krueger 1994) | New Jersey raised it, Pennsylvania didn't | DiD |
| Mobile phones & fish markets (Jensen 2007) | Phone coverage arrived at different times in Kerala | DiD-style |
| Chernobyl fallout (Almond et al.) | Rainfall decided where fallout landed | Natural experiment |
Keep a critical eye deeper
- Generalisation: does the result apply beyond this sample?
- Heterogeneity: the effect may differ across people.
- Is the "natural" experiment really random? Is quarter of birth truly random? Its link to schooling is also weak.
- Even real experiments break: people don't respond, measurement error, a too-specific sample, conditions change.
- Qualitative support for causality: the effect is strong, consistent, specific, stable over time, monotone (more dose, more effect), plausible, and confirmed by experiments.
The lecture's conclusion: results are based on models, so be critical. But some questions matter so much that an imperfect answer beats none.
Randomised experiments in five questions
- What problem are we solving?Measure the effect of D on Y when the people who get D might differ from those who don't.
- How does the method solve it?Assign D at random, then compare average outcomes. Randomisation makes D independent of the potential outcomes, so selection bias is zero on average.
- What must hold?
- Random assignment (independence).
- No spillovers between units (SUTVA).
- People take the treatment they are assigned (or you analyse by assignment).
- No differential drop-out.
- What happens when it fails?
- Selection into treatment: the difference in means mixes the effect with who chose it.
- Non-compliance: comparing by assignment gives the ITT; use IV for the effect of treatment itself.
- Attrition that differs by group: biased comparison.
- How can we check?
- Balance table on pre-treatment characteristics.
- Compliance and attrition rates by group.
- Randomisation inference for small experiments.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 4 · Randomisation and selection bias, simulated about 10 min
# Lab 4: potential outcomes, randomisation and selection bias
set.seed(4)
n <- 1000
y0 <- rnorm(n, 50, 10) # outcome without treatment
effect <- rnorm(n, 5, 4) # individual effects differ
y1 <- y0 + effect
c(ATE = mean(y1 - y0))
# (a) Random assignment: compare means
d <- sample(rep(0:1, n / 2))
y <- ifelse(d == 1, y1, y0) # we only see one outcome per person
c(estimate = mean(y[d == 1]) - mean(y[d == 0]))
# Repeat the random assignment 1,000 times
est <- replicate(1000, {d <- sample(rep(0:1, n / 2))
mean(y1[d == 1]) - mean(y0[d == 0])})
round(c(mean_of_estimates = mean(est), sd = sd(est),
min = min(est), max = max(est)), 2)
# (b) Self-selection: people with high y0 choose treatment more often
d_self <- rbinom(n, 1, plogis((y0 - 50) / 5))
y_self <- ifelse(d_self == 1, y1, y0)
c(naive = mean(y_self[d_self == 1]) - mean(y_self[d_self == 0]),
ATT = mean((y1 - y0)[d_self == 1]),
selection_bias = mean(y0[d_self == 1]) - mean(y0[d_self == 0]))
What you should see
ATE
5.098
estimate
4.585
mean_of_estimates sd min max
5.11 0.64 3.14 7.21
naive ATT selection_bias
16.780 5.179 11.601
How to read it
- The true ATE in this population is 5.10. One random assignment gives 4.59: close, not exact.
- Across 1,000 random assignments the estimates average 5.11 (SD 0.64): unbiased, with chance variation.
- Self-selection: the naive difference is 16.78 = ATT 5.18 + selection bias 11.60. Two-thirds of the "effect" is who chose treatment.
Common mistakes
- Expecting one randomised estimate to equal the ATE exactly.
- Forgetting that the selection-bias split needs Y(0) for the treated, which only a simulation gives you.
- Comparing self-selected groups and calling the difference an effect.
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
What is the "fundamental problem of causal inference"?
For each person we observe only one potential outcome, Y(1) or Y(0), never both. So individual effects can't be observed.
Why does randomisation make a simple difference in means causal?
It makes treatment independent of potential outcomes, so the two groups would have had the same average outcomes without treatment.
A good "Z" must do two things. What?
Move the treatment D, and affect Y only through D (no direct path, no link to hidden factors).
- Mastering 'Metrics ch. 1 "Randomized Trials" (health insurance examples). The intro explains the "furious five" methods. This is the best match for this lecture.
Instrumental variables
When the treatment is tangled up with hidden factors, find an outside "knob" Z that pushes the treatment around at random. Then use only the part of the treatment that the knob moved.
Gist mode is on: deeper sections are hidden.
- Covariance
- Do two variables move together? Positive = up together, negative = opposite, 0 = no linear link.
- Endogenous
- Correlated with the error term (tangled with hidden stuff). Opposite: exogenous, as good as random.
- Bias
- Your estimator is off on average, in a systematic direction, even with lots of data.
- Fitted value (D̂)
- The value a regression predicts for each observation.
If you remember only 3 things
- A valid instrument is relevant (moves D), as good as random, and excluded (affects Y only through D). Only relevance can be tested.
- IV = reduced form ÷ first stage = (effect of Z on Y) / (effect of Z on D). In practice it's run as two-stage least squares (2SLS).
- With effects that differ between people, IV gives a LATE: the effect for compliers, people whose treatment was changed by the instrument.
After this lecture you can…
- FoundationalState the IV conditions: relevance, independence, exclusion, monotonicity.
- FoundationalCompute the Wald estimator: reduced form ÷ first stage.
- IntermediateIdentify compliers, always-takers and never-takers, and say who the LATE is about.
- IntermediateRecognise a weak instrument and know what to report instead.
- AdvancedDerive IV by the method of moments; read an overidentification test.
On this page
The problem: endogeneity
D is endogenous (correlated with the error) when there is:
- an omitted variable U,
- reverse causality (Y also affects D),
- or measurement error in D.
Then the OLS condition E[D·η] = 0 fails and OLS is biased.
Z is a clean knob that turns D up and down without touching the noise in Y. The first stage finds how much D moved because of the knob. The second stage asks how much Y moved along with that clean movement.
The three (four) conditions
| Condition | Plain meaning | Testable? |
|---|---|---|
| Relevance | Z actually moves D: Cov(Z, D) ≠ 0 | Yes: look at the first stage |
| Independence | Z is as good as random, unrelated to U | No (argue it) |
| Exclusion | Z affects Y only through D | No (argue it) |
| Monotonicity (for LATE) | Z pushes everyone in the same direction. No "defiers". | No |
Independence + exclusion together = validity: E[Z·η] = 0.
The formulas
Same shape. OLS uses D's own variation (contaminated). IV uses only the variation that comes through Z.
- top
- reduced form: how much Y differs between Z groups
- bottom
- first stage: how much D differs between Z groups
The version for a 0/1 instrument (like a lottery). This is the one to use in exam calculations.
Where the formulas come from: method of momentsdeeper
Write down conditions that hold at the true value, then solve their sample versions. For a mean: E[Y − μ] = 0 ⇒ μ̂ = Ȳ. For OLS: residuals average zero and are orthogonal to D, giving Cov(D,Y)/Var(D). For IV: keep "residuals average zero", but replace the second condition with residuals orthogonal to Z, Σ Zᵢ(Yᵢ − α − δDᵢ) = 0. Demean and solve: δ̂ = Cov(Z,Y)/Cov(Z,D). We weight by Z because E[Zη] = 0 is believable, while E[Dη] = 0 is exactly what fails.
Two-stage least squares (2SLS)
- First stage: regress D on Z (and controls X). Save the fitted values D̂.
- Second stage: regress Y on D̂ (and the same X, but not Z).
The slope on D̂ equals the IV ratio exactly.
If you run the two stages by hand, δ̂ is right but the standard errors are wrong: the second-stage residual uses D̂ instead of the real D. Always use an IV command (ivreg in R, ivregress 2sls in Stata).
Controlsdeeper
If X affects both Z and Y, Z is only valid given X, so X goes in both stages. Equivalent trick (Frisch–Waugh–Lovell): regress Y, D and Z each on X, keep the residuals, then apply the simple formula Cov(Z⊥, Y⊥)/Cov(Z⊥, D⊥). "Controlling for X" = removing X from everything, then using what's left.
Weak instruments
Rewrite: δ̂_IV − δ = Cov(Z, η) / Cov(Z, D). If Z barely moves D, the bottom is near zero, so tiny noise on top gets blown up. The estimate becomes erratic, and conventional standard errors and t-tests stop being trustworthy.
Which way does it go wrong? In the textbook setting (one endogenous D, conventional 2SLS, Z truly valid), the finite-sample distribution of 2SLS tends to be centred towards the OLS estimate, because with little clean variation the estimate is dominated by the same contaminated variation that biases OLS. This is a common pattern, not a universal law: with many instruments it gets worse, with just-identified IV the estimator may not even have a finite mean, and if Z is also slightly invalid, a weak first stage magnifies that bias in any direction.
Old rule of thumb: first-stage F-statistic of at least 10. Newer work (Lee, McCrary, Moreira & Porter 2022) shows the usual ±1.96 t-test needs F around 104 to be reliable. With a weaker first stage, use weak-instrument-robust inference such as Anderson–Rubin confidence intervals.
See it: strong, weak and invalid instruments
Each press runs 500 imaginary studies with 200 people. A hidden U raises both D and Y, so OLS is biased. The true effect of D is 0.5. Make the instrument weaker, or let it affect Y directly (breaking exclusion), and watch the IV estimates.
- Strong instrument (0.5 or more): IV estimates centre on 0.5; OLS sits far to the right.
- Slide strength down to about 0.1: the median F drops below 10, IV estimates spread out widely and their median drifts towards OLS, so fewer studies land near the truth. The usual 95 % CIs become so wide they say little.
- Set strength to 1 (F around 100), then add a direct effect of 0.2: IV now centres on about 0.7, not 0.5, despite the big F. F checks relevance, never validity. Then weaken the instrument: the bias grows, because the violation is divided by a smaller first stage.
LATE: who does IV tell us about?
In real experiments people don't always follow their assignment (imperfect compliance). Comparing by assignment gives the ITT (intention-to-treat): the effect of being offered treatment.
| Type | Treated if Z = 1? | Treated if Z = 0? |
|---|---|---|
| Compliers | yes | no |
| Always-takers | yes | yes |
| Never-takers | no | no |
| Defiers (assumed none) | no | yes |
The effect for the people whose treatment was changed by the instrument.
In the KIPP lottery, some families would send their child to KIPP whatever happened (always-takers) and some never would (never-takers). The lottery only changes what the "compliers" do, so it can only tell us about them.
Under relevance, independence, exclusion and monotonicity, (E[Y | Z=1] − E[Y | Z=0]) / (E[D | Z=1] − E[D | Z=0]) = E[Y(1) − Y(0) | D(1) > D(0)] (Imbens & Angrist 1994). Different instruments have different compliers, so different LATEs.
Always- and never-takers do the same thing whatever Z is, so they add nothing to the reduced form. All of it comes from compliers. So reduced form ÷ first stage = average effect for compliers, the LATE.
- Attendance: 78.7 % of winners and 4.6 % of losers attended KIPP. First stage φ = 0.787 − 0.046 = 0.741.
- Math scores (in standard deviations): winners −0.003, losers −0.358. Reduced form ρ = 0.355. This is the ITT.
- LATE = ρ/φ = 0.355 / 0.741 = 0.479.
- Shares: compliers 0.741, always-takers 0.046 (losers who attended anyway), never-takers 1 − 0.787 = 0.213.
Attending KIPP raised math scores by about 0.48 SD for children who attended only because they won the lottery.
Two words for non-compliance: treatment migration (controls get treated anyway) and treatment dilution (assigned people don't take it).
Case study: kids and mothers' work deeper
Angrist & Evans (1998). Does a third child reduce mothers' labour supply?
- Instrument: first two kids are the same sex. Parents like a mix, so they're more likely to try for a third. Sex is as good as random.
- First stage: 43.2 % of same-sex families had another child vs 37.2 % of mixed: +6.0 points.
- Result (worked for pay): OLS −0.176, 2SLS −0.120. A third child lowers compliers' chance of working by 12 points. OLS overstated it.
- Compliers are only about 6 % of families. Says nothing about always-takers. A different instrument (e.g. twins) has different compliers, so a different LATE.
- Husbands: effect ≈ 0.
Case study: institutions and growth deeper
Acemoglu, Johnson & Robinson (2001). Do good institutions make countries rich?
- Problem: rich countries can afford good institutions (reverse causality), plus omitted factors.
- Instrument: settler mortality. Where Europeans died a lot, they set up extractive states. Where they could live, they built settler institutions. Institutions persisted.
- Story: mortality → settlements → early institutions → today's institutions → today's income.
- Results: OLS 0.52 vs IV 0.94. Nigeria vs Chile: OLS explains a 2-fold income gap, IV a 7-fold gap (the real gap is 11-fold).
- Robust to latitude, dropping Neo-Europes or Africa, continent dummies, colonial origin, religion, climate, malaria.
More instruments than you need deeper
With 2 instruments for 1 treatment, each gives its own estimate. If both are valid, those should be similar. The overidentification test (Sargan / J-test) checks this: J ~ χ²q with q = (number of instruments) − (number of endogenous variables). Caveat: it can only check instruments against each other, never prove they're valid.
Other classic instruments: Vietnam draft lottery, distance to college (Card), distance to hospital (heart attacks), rainfall (civil conflict), weather (fish demand).
Instrumental variables in five questions
- What problem are we solving?D is endogenous (omitted variables, reverse causality, measurement error), so OLS is biased.
- How does the method solve it?Use only the variation in D that comes from an outside "knob" Z: effect of Z on Y divided by effect of Z on D (2SLS).
- What must hold?
- Relevance: Z moves D.
- Independence: Z is as good as random.
- Exclusion: Z affects Y only through D.
- Monotonicity (for LATE): no defiers.
- What happens when it fails?
- Weak first stage: erratic estimates and unreliable usual SEs; in the textbook case they drift towards OLS.
- Invalid Z: biased, and a weak first stage magnifies the bias.
- Effects differ between people: you get a LATE for compliers, not an ATE.
- How can we check?
- First-stage F (relevance; ≥ 10 classic, ~104 for plain t-tests).
- Balance of covariates across Z (partial check of independence).
- Argue exclusion; try placebo outcomes Z shouldn't affect.
- With several instruments, an overidentification test.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 5 · A charter-school lottery as an instrument about 15 min
# Lab 5: a charter-school lottery as an instrument
library(AER); library(ggplot2)
set.seed(5)
n <- 2000
motivation <- rnorm(n) # unobserved: affects attending AND scores
win <- rbinom(n, 1, 0.5) # Z: lottery offer, random
# D: winners attend unless very unmotivated; losers attend only if very motivated
attend <- ifelse(win == 1, motivation > -1, motivation > 1.5) * 1
score <- 0.5 * attend + 0.6 * motivation + rnorm(n) # true effect = 0.5 SD
d <- data.frame(score, attend, win)
coef(lm(score ~ attend, data = d))["attend"] # OLS: mixes effect with motivation
fs <- mean(d$attend[d$win == 1]) - mean(d$attend[d$win == 0]) # first stage
rf <- mean(d$score[d$win == 1]) - mean(d$score[d$win == 0]) # reduced form
round(c(first_stage = fs, reduced_form = rf, wald = rf / fs), 3)
iv <- ivreg(score ~ attend | win, data = d) # 2SLS with correct SEs
summary(iv, diagnostics = TRUE)
# Weak vs strong instrument: 1,000 simulated studies each
sim <- function(strength) t(replicate(1000, {
u <- rnorm(200); z <- rnorm(200)
x <- strength * z + u + rnorm(200) # u makes x endogenous
y <- 0.5 * x + u + rnorm(200)
c(est = cov(z, y) / cov(z, x),
F = summary(lm(x ~ z))$fstatistic[[1]])}))
strong <- sim(1); weak <- sim(0.1)
round(rbind(strong = c(median_F = median(strong[, "F"]), median_est = median(strong[, "est"])),
weak = c(median_F = median(weak[, "F"]), median_est = median(weak[, "est"]))), 2)
sims <- data.frame(est = c(strong[, "est"], weak[, "est"]),
type = rep(c("Strong instrument", "Weak instrument"), each = 1000))
ggplot(subset(sims, abs(est) < 4), aes(est)) +
geom_histogram(bins = 60, fill = "grey60", colour = "white") +
geom_vline(xintercept = 0.5, linetype = 2) + facet_wrap(~type) +
labs(x = "IV estimate (true effect = 0.5)", y = "Count") + theme_minimal()
What you should see
attend
0.9456
first_stage reduced_form wald
0.768 0.332 0.432
Call:
ivreg(formula = score ~ attend | win, data = d)
Residuals:
Min 1Q Median 3Q Max
-4.77483 -0.79300 0.00443 0.81692 3.93551
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.0706 0.0418 1.69 0.091 .
attend 0.4319 0.0698 6.19 7.4e-10 ***
Diagnostic tests:
df1 df2 statistic p-value
Weak instruments 1 1998 2916 <2e-16 ***
Wu-Hausman 1 1997 150 <2e-16 ***
Sargan 0 NA NA NA
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.2 on 1998 degrees of freedom
Multiple R-Squared: 0.0984, Adjusted R-squared: 0.0979
Wald test: 38.3 on 1 and 1998 DF, p-value: 7.4e-10
median_F median_est
strong 99.62 0.51
weak 1.08 0.70
How to read it
- OLS gives 0.946: inflated, because motivated students both attend and score higher. The true effect is 0.5.
- First stage 0.768, reduced form 0.332, Wald = 0.332 / 0.768 = 0.432, the same number
ivregreports. Weak instrumentsF = 2,916: a very strong first stage.Wu-Hausmancompares OLS and IV (assuming the instrument is valid).Sarganis NA because there is one instrument for one treatment.- Simulation: with a strong instrument the median estimate is 0.51; with a weak one (median F ≈ 1) it is 0.70, pulled towards OLS.
Common mistakes
- Formula syntax:
ivreg(y ~ D + X | Z + X). Controls go on both sides of the bar. - Reading the Wu-Hausman or weak-instrument test as proof that the instrument is valid. Neither tests exclusion.
- Running the two stages by hand with
lm()and reporting its SEs.
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
Which IV condition can you test, and how?
Relevance. Look at the first-stage regression of D on Z. F ≥ 10 is the classic rule of thumb; for reliable conventional t-tests, F well above that (around 104) is needed, otherwise use weak-IV-robust intervals.
Lottery winners: 60 % attend, losers 10 %. Scores differ by 0.2. LATE?
First stage = 0.6 − 0.1 = 0.5. LATE = 0.2/0.5 = 0.4.
Why are always-takers left out of LATE?
Their treatment doesn't change with Z, so (by exclusion) their outcomes don't differ between Z groups. They add nothing to the reduced form.
What goes wrong with a weak instrument?
You divide by a near-zero first stage, so the estimate becomes unstable and conventional SEs are unreliable. In the usual 2SLS setting its distribution also tends to drift towards the OLS estimate.
Why not just run the second stage by hand?
The coefficient is right but the standard errors are wrong. Use an IV command.
- Mastering 'Metrics ch. 3 "Instrumental Variables". §3.1 "The Charter Conundrum" is literally the KIPP example, and §3.3 "The Population Bomb" covers kids and labour supply. Read this first.
- Mastering 'Metrics ch. 6.3 covers quarter-of-birth instruments for schooling.
Regression discontinuity
Rules often switch a policy on at a sharp cut-off. If nothing else changes at the cut-off and people can't precisely place themselves on one side, then the jump in the outcome right at the cut-off is the effect.
Gist mode is on: deeper sections are hidden.
- Cut-off (threshold)
- The exact value where a rule switches, e.g. "born on or after 1 July 1990".
- Density
- How many observations sit at each value of a variable (a smoothed histogram).
- Bias–variance trade-off
- Using more data lowers noise (variance) but can add systematic error (bias), and the other way round.
- Placebo test
- Run the analysis where there should be no effect. If you find one, something's off.
If you remember only 3 things
- Treatment depends on a running variable X crossing a cut-off c. Compare outcomes just below and just above c.
- Key assumption: nothing else jumps at c, and people can't precisely sort themselves to one side. Check with a density test, covariate balance and placebo cut-offs.
- Use local linear regression near c, not high-order polynomials over all the data. The effect is local: it's the effect for people at the cut-off.
After this lecture you can…
- FoundationalExplain the running variable, the cut-off and the sharp/fuzzy distinction.
- IntermediateInterpret the jump as a local effect, valid under continuity.
- IntermediateExplain the bias–variance trade-off in choosing a bandwidth.
- IntermediateRecognise threats (sorting, other policies at c) and run the standard checks.
- AdvancedRead rdrobust output, including robust bias-corrected intervals.
On this page
The picture is the method
Imbens & Lemieux: if the basic plot shows no jump, fancy regressions are unlikely to find a credible one. Always start with the plot.
Sharp RD
- Xᵢ
- running variable (or forcing variable): score, age, date of birth
- c
- cut-off
- 1{…}
- equals 1 if true, 0 if false
Knowing X tells you D exactly. That's what "sharp" means.
Identifying assumption, continuity: the average potential outcomes E[Y(0)|X] and E[Y(1)|X] change smoothly through c. Without treatment, there would be no jump. So:
Average just above minus average just below = average effect at the cut-off. Away from c the effect could be different, and the data can't tell you by how much.
A student who scored 79 and one who scored 80 may be almost the same student. If only the 80 gets a scholarship, comparing them can be close to a coin flip.
Being near the cut-off does not automatically make people comparable. The comparison is valid only if (1) the average potential outcomes change smoothly through c, so nothing else jumps at c (no other policy, benefit or label uses the same cut-off), and (2) people can't precisely control which side they land on. Lee (2008) shows that under (2), RD behaves like a local randomised experiment. Both are assumptions to argue and check (density test, covariate balance, placebo cut-offs), never facts that follow from proximity.
Threats: when people game the cut-off
| Threat | What it looks like | What to do |
|---|---|---|
| Bunching / manipulation | Too many people just above c, too few just below (e.g. births shifted after Australia's baby bonus) | Density test (McCrary; now rddensity) |
| Donut hole | People avoid being right at c | "Donut RD": drop points closest to c, check the estimate stays stable |
| Heaping | X is rounded or piles up at round numbers | Careful: units at heaps may differ; you can't get arbitrarily close to c |
| Mass point at c | Many units exactly at c (a default or target) | Count them; re-estimate without them |
- Density of X: no jump at c.
- Covariate balance: things decided before treatment (age, past earnings) don't jump at c.
- Placebo cut-offs: no jumps at fake cut-offs.
- Donut RD: estimate doesn't change much without the closest points.
- Bandwidth sensitivity: estimate stable across reasonable bandwidths.
Estimating it
- τ
- the jump at the cut-off: your effect
- β
- slope on the left side
- β + γ
- slope on the right side (the interaction lets slopes differ)
- Xᵢ − c
- centred running variable, so τ is measured exactly at c
- Global polynomials (fit the whole range with x, x², x³…): avoid high orders. Far-away points get big, erratic weight. In the slide's simulation (true τ = 1), estimates ranged from 0.55 to 2.2 depending on the polynomial order (Gelman & Imbens 2019).
- Local linear (recommended): use only points with |X − c| ≤ h (the bandwidth), fit a line on each side, and weight closer points more (triangular kernel).
See it: the bandwidth simulator
Six hundred simulated people. Treatment switches on at X = 0 and the true jump there is 0.4. The relationship curves, and differently on each side, so wide windows can mislead. The shaded window is the bandwidth; blue points are the ones used.
Narrow the bandwidth to 0.08. Why did your estimate become less precise?
Fewer points fall inside the window, so each side's line is fitted on little data and its intercept at the cut-off is noisy. A wider window uses more data (smaller SE) but compares people further from the cut-off, where the curve bends, so the straight lines start to miss the true jump (bias). Try h = 1: the SE is small but the estimate is pushed well away from 0.4. That is the bias–variance trade-off that rdrobust's bandwidth selector balances.
Move "where you test" away from 0. What should you find, and why is this useful?
No jump (truth = 0): nothing changes at a fake cut-off. This is a placebo test. Finding jumps at fake cut-offs would suggest your method is picking up curvature or noise rather than a treatment effect. (For placebo cut-offs the simulator uses only data on the same side of the real cut-off.)
How far from the cut-off you look.
Comparing students who scored 79 with those who scored 80 compares very similar people, but there are few of them. Comparing 70–79 with 80–89 gives far more people, but they are less alike. The bandwidth is the size of that window.
Local polynomial fit with kernel weights K((Xᵢ − c)/h). For local linear, bias is of order h² and variance of order 1/(nh), so the MSE-optimal h shrinks like n−1/5. At that h, conventional CIs under-cover; robust bias-corrected CIs (Calonico, Cattaneo & Titiunik 2014) fix this.
Bandwidth and inference deeper
- Small h: less bias (only very similar people) but more variance (fewer points).
- Large h: more data, less variance, but more bias.
- MSE = bias² + variance. Data-driven selectors pick the h that minimises it (in the slides' simulation h* ≈ 0.78). Report results at 0.5×h and 1.5×h too.
- Kernel matters much less than bandwidth. Triangular is standard; uniform = plain OLS inside the window.
- Robust bias-corrected CIs (Calonico, Cattaneo & Titiunik 2014). At the MSE-optimal h, ordinary CIs are too narrow. Software:
rdrobust. - Report τ̂, its SE, h, the kernel and the polynomial order. Cluster where treatment is assigned.
Fuzzy RD = IV at the cut-off
Sometimes crossing c only raises the chance of treatment. E.g. students scoring below 80 are eligible for free tutoring, but some don't go and some above 80 find a way in.
The Wald ratio from lecture 5: reduced form ÷ first stage. Eligibility Z = 1{X < 80} is the instrument. You get a LATE for compliers at the cut-off. Estimate with 2SLS in the same window.
Assumptions: relevance (eligibility shifts take-up, testable), exclusion (crossing 80 matters only through tutoring; it fails if being labelled "below 80" discourages students), monotonicity.
Case study: Austrian parental leave
Lalive & Zweimüller (2009). From 1 July 1990, parental leave was extended from 1 to 2 years. Running variable = child's date of birth. Compare June 1990 mothers (control) with July 1990 mothers (treated).
- Year-2 leave taken: +9.3 months. The treatment really changed.
- Another child within 36 months: +4.5 percentage points (13–16 % above the mean of 0.345).
- Returned to work within 36 months: −10.9 points. After 10 years: −3 points.
- Earnings in years 0–3: about −€2.7 per day. Years 4–10: no significant difference.
Checks: no spike in births on 1 July; dropping births within a week of the cut-off gives 0.056; placebo with June/July 1987 gives 0.008 (≈ 0). Balance table: only one of many characteristics differs.
Regression discontinuity in five questions
- What problem are we solving?Treatment is assigned by a rule at a cut-off of a score, age or date, and treated people differ from untreated ones overall.
- How does the method solve it?Compare outcomes just above and just below the cut-off, with a local linear fit on each side within a bandwidth.
- What must hold?
- Continuity: average potential outcomes change smoothly through c (nothing else jumps there).
- No precise sorting: people can't choose their side.
- The local linear fit is a good approximation inside the bandwidth.
- What happens when it fails?
- Another policy at the same cut-off: the jump mixes both.
- Manipulation: people just above differ from those just below.
- Too wide a bandwidth or high-order polynomials: biased; too narrow: noisy.
- How can we check?
- Density test (rddensity).
- Covariates shouldn't jump at c.
- Placebo cut-offs and donut RD.
- Estimates across bandwidths; robust bias-corrected CIs (rdrobust).
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 6 · A parental-leave reform as a regression discontinuity about 15 min
# Lab 6: a parental-leave reform as a regression discontinuity
library(rdrobust); library(rddensity); library(ggplot2)
set.seed(6)
n <- 40000 # births in one year
days <- sample(-180:180, n, replace = TRUE) # birth date relative to 1 July (cut-off = 0)
long_leave <- as.numeric(days >= 0) # sharp: born on/after cut-off -> 2-year leave
p <- 0.32 + 0.00015 * days + 0.045 * long_leave # true jump = 4.5 percentage points
birth36 <- rbinom(n, 1, p) # another child within 36 months?
d <- data.frame(days, long_leave, birth36)
rd <- rdrobust(d$birth36, d$days, c = 0) # local linear, MSE-optimal bandwidth
summary(rd)
# Manipulation check: is there a jump in the density of birth dates?
round(rddensity(d$days, c = 0)$test$p_jk, 3) # p-value of the density test
# Bandwidth sensitivity: same model, different windows (in days)
suppressWarnings(t(sapply(c(30, 60, 90, 150), function(h) {
r <- rdrobust(d$birth36, d$days, c = 0, h = h)
round(c(h = h, estimate = r$coef[1], ci_low = r$ci[3, 1], ci_high = r$ci[3, 2]), 3)})))
# Fuzzy version: eligibility only raises take-up (90 % vs 10 %)
took_leave <- rbinom(n, 1, ifelse(long_leave == 1, 0.9, 0.1))
birth36_f <- rbinom(n, 1, 0.32 + 0.00015 * days + 0.05 * took_leave) # true effect 0.05
fz <- suppressWarnings(rdrobust(birth36_f, days, c = 0, fuzzy = took_leave))
round(c(fuzzy_estimate = fz$coef[1], ci_low = fz$ci[3, 1], ci_high = fz$ci[3, 2]), 3)
# RD plot: monthly bins, separate lines on each side
bins <- aggregate(birth36 ~ cut(days, seq(-180, 180, 15), right = FALSE), data = d, mean)
bins$mid <- seq(-172.5, 172.5, 15)
ggplot(bins, aes(mid, birth36)) + geom_point() +
geom_smooth(data = d, aes(days, birth36, group = long_leave), method = "lm",
formula = y ~ x, se = FALSE, colour = "black") +
geom_vline(xintercept = 0, linetype = 2) +
labs(x = "Birth date (days from 1 July)", y = "Share with another child") + theme_minimal()
What you should see
Call: rdrobust
Sharp RD estimates using local polynomial regression.
Number of Obs. 40000
BW type mserd
Kernel Triangular
VCE method NN
Left Right
Number of Obs. 19821 20179
Eff. Number of Obs. 5870 6039
Order est. (p) 1 1
Order bias (q) 2 2
BW est. (h) 54.902 54.902
BW bias (b) 94.358 94.358
rho (h/b) 0.582 0.582
Unique Obs. 180 181
=====================================================================
Point Robust Inference
Estimate z P>|z| [ 95% C.I. ]
---------------------------------------------------------------------
RD Effect 0.061 3.083 0.002 [0.025 , 0.111]
=====================================================================
[1] 0.927
h estimate ci_low ci_high
[1,] 30 0.074 0.043 0.190
[2,] 60 0.059 0.025 0.129
[3,] 90 0.048 0.025 0.111
[4,] 150 0.043 0.017 0.083
fuzzy_estimate ci_low ci_high
0.064 0.010 0.119
Warning message:
In rdrobust(d$birth36, d$days, c = 0) :
Mass points detected in the running variable.
How to read it
BW est. (h)54.9 days: only births within 55 days of 1 July are used (Eff. Number of Obs.5,870 + 6,039 of 40,000).RD Effect0.061 with a robust 95 % CI [0.025, 0.111]: about 6 pp more mothers had another child within 36 months. The simulated truth is 4.5 pp, inside the CI.- Density test p = 0.927: no sign of sorting around the cut-off (here by construction).
- Bandwidth table: estimates move from 0.074 (h = 30) to 0.043 (h = 150), and the CI narrows as h grows: the bias–variance trade-off in action.
- Fuzzy version: 0.064, CI [0.010, 0.119], close to the true 0.05.
Common mistakes
- Reporting the conventional CI at the MSE-optimal bandwidth. Use the robust bias-corrected one.
- Worrying about the "Mass points" warning: birth dates are whole days, so many births share each value. It is expected here; rdrobust adjusts for it.
- Treating a non-significant density test as proof that nobody sorted.
- Forgetting that the result applies to mothers giving birth near the cut-off.
Reusable template for your own data
# install.packages(c("rdrobust", "rddensity")) # once
library(rdrobust); library(rddensity)
# data must contain an outcome and a running variable (here: outcome, score); cut-off 80
result <- rdrobust(y = data$outcome, x = data$score, c = 80)
summary(result) # jump, bandwidth, robust CI
rdplot(y = data$outcome, x = data$score, c = 80) # the picture first!
summary(rddensity(X = data$score, c = 80)) # sorting check
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
What's the difference between sharp and fuzzy RD?
Sharp: treatment jumps from 0 to 1 at c. Fuzzy: only the probability of treatment jumps, so you divide by the jump in take-up (IV).
Why worry if lots of people sit just above the cut-off?
It suggests manipulation. People who sort themselves across c are probably different, so above-vs-below is no longer like random.
Why not fit a 5th-order polynomial to all the data?
Far-away points get large erratic weights, so the estimate at c is sensitive to the polynomial order and can overfit. Use local linear near c.
Effect at the cut-off is +5. Can you say the policy raises outcomes by 5 for everyone?
No. RD identifies the effect at X = c. Extending it further needs extra assumptions.
- Mastering 'Metrics ch. 4 "Regression Discontinuity Designs": §4.1 minimum drinking age and deaths (sharp RD), §4.2 Boston exam schools (fuzzy RD).
- Further: Lee & Lemieux (2010), Cattaneo, Idrobo & Titiunik (2020) practical intro.
Difference-in-differences
Don't just compare before and after: other things change over time too. Compare the treated group's change with a control group's change. The difference between the two changes is the effect.
Gist mode is on: deeper sections are hidden.
- Panel data
- The same units (people, stores, states) observed at several points in time.
- Fixed effect
- A separate intercept for each unit (or each period) that soaks up everything constant about it.
- Counterfactual
- What would have happened without the treatment. Never observed; it has to be estimated.
- Percentage point (pp)
- Plain difference between two percentages: 40 % → 45 % is +5 pp (but +12.5 %).
If you remember only 3 things
- DiD = (treated after − treated before) − (control after − control before).
- Key assumption: parallel trends. Without treatment, both groups would have changed by the same amount. Levels can differ; trends can't. It can't be tested directly; pre-trends in an event study are a partial check.
- With staggered timing and changing effects, plain two-way fixed effects (TWFE) can be badly wrong, because it uses already-treated units as controls. Use Callaway & Sant'Anna-style estimators.
After this lecture you can…
- FoundationalCompute a 2 × 2 DiD from four means and as a regression.
- FoundationalState parallel trends, no anticipation and no spillovers.
- IntermediateRead an event-study plot: what leads can and can't show.
- IntermediateCluster standard errors at the right level.
- AdvancedExplain why TWFE fails with staggered timing and compute ATT(g, t).
On this page
John Snow and the cholera "Grand Experiment"
London, 1850s. In 1852 the Lambeth water company moved its intake upriver, above the sewage. Southwark & Vauxhall (S&V) kept taking dirty water. Both companies' pipes ran down the same streets, so neighbours breathed the same air but drank different water.
| Cholera deaths per 10,000 households | 1849 | 1854 | Change |
|---|---|---|---|
| Lambeth (treated: clean water) | 62 | 14 | −48 |
| S&V (control: dirty water) | 565 | 614 | +49 |
- Treated change: 14 − 62 = −48.
- Control change: 614 − 565 = +49. The epidemic got worse overall.
- DiD = (−48) − (+49) = −97.
Clean water cut cholera deaths by about 97 per 10,000 households. Just looking at Lambeth before/after (−48) would have understated it.
The picture
See it: when parallel trends hold, and when they don't
Six periods, policy from period 0. The dashed orange line is the counterfactual DiD assumes: the treated group continuing on the control group's path. Make the treated group drift on its own and see the estimate go wrong.
- Change the level gap: the estimate doesn't move. Levels can differ.
- Change the common trend: it doesn't move either. DiD subtracts it.
- Give the treated group an extra trend of 0.5: the estimate is off by 1.5, and you can already see the lines diverging before the policy. Event-study leads would show that, if the data are precise enough to detect it.
The 2 × 2 setup and its assumptions
- μg,t
- average outcome for group g (1 = treated, 0 = control) in period t (1 = after, 0 = before)
- β
- baseline gap between groups
- γ
- common time change
- δ
- the DiD effect (extra change for the treated after)
The same number as the four-means formula, just written as a regression.
The target is the ATT: average effect on the treated group after treatment. It's identified under three assumptions:
- Consistency / SUTVA: observed outcome = potential outcome under the treatment received. Treatment is well defined, and there are no spillovers onto the control group.
- No anticipation: the treated don't react before treatment starts (e.g. to an announced policy).
- Parallel trends:
E[Y₁(0) − Y₀(0) | treated] = E[Y₁(0) − Y₀(0) | control]. The only assumption about the unseen counterfactual.
The derivation in three stepsdeeper
(1) Add and subtract the treated group's "before" outcome, so ATT = (actual change of treated) − (untreated change of treated, unknown). (2) Parallel trends replaces that unknown change with the control group's change. (3) Consistency + no anticipation turn every potential-outcome term into an observed mean. Result: ATT = (μ₁,₁ − μ₁,₀) − (μ₀,₁ − μ₀,₀).
Without the policy, both groups would have changed by the same amount.
Two cities' café sales both grow about 3 % a year. City A introduces a sugar tax, and its sales then grow only 1 % while city B's still grow 3 %. That suggests the tax cost about 2 %, but only if A would otherwise have kept growing 3 % too.
E[Ypost(0) − Ypre(0) | treated] = E[Ypost(0) − Ypre(0) | control]. It concerns an unobserved post-period counterfactual, so it can't be tested directly, and it depends on the scale (parallel in levels ≠ parallel in logs).
Parallel trends does not require equal levels. Lambeth and S&V had wildly different levels (62 vs 565). Only the changes need to match without treatment.
Adding covariates deeper
Conditional parallel trends: trends are parallel within groups with the same X. Compute DiD in each X-cell, then average using the treated group's mix.
Cell X=0: DiD = 1. Cell X=1: DiD = 2. Treated are 20 % / 80 % in these cells, so ATT = 0.2·1 + 0.8·2 = 1.8. Ignoring X gives 3.0, a bias of 1.2, because most treated units are in the fast-trend cell.
In a regression, covariates must be allowed to shift the trend: add Xᵢ × Postₜ. Adding Xᵢ alone does nothing (DiD removes level differences anyway). Better tools: regression adjustment, inverse probability weighting, or doubly robust DiD (Sant'Anna & Zhao 2020).
Event studies: checking pre-trends
Replace the single "treated × after" dummy with one dummy per period relative to treatment, k = t − (adoption time). Leave out k = −1 as the reference.
- Leads (k < 0): should be near zero. If not, trends weren't parallel before.
- Lags (k ≥ 0): how the effect evolves. The slides' fracture example shows income dropping by over €500/month at month 2 and slowly recovering. One average number would hide that.
- Parallel trends is a claim about the post-treatment counterfactual, which is never observed. Leads only show the groups moved together before.
- Pre-trend tests often have low power: with noisy data, a real pre-trend big enough to bias your estimate can easily be "insignificant" (Roth 2022).
- Picking a specification because it passes the pre-trend test distorts the final estimate and its confidence interval.
- Better practice: show the event-study plot, report how big a violation would have to be to change your conclusion (sensitivity analysis, Rambachan & Roth 2023), and argue why trends would stay parallel.
Staggered adoption: why TWFE breaks
Many groups, treated at different times. The usual regression is two-way fixed effects: Yᵢₜ = αᵢ + λₜ + β·Dᵢₜ + uᵢₜ (unit FE + time FE).
Goodman-Bacon (2021): β_TWFE is a weighted average of every possible 2×2 DiD in the data:
- early-treated vs never-treated ✓
- late-treated vs never-treated ✓
- early vs late (late still untreated) ✓
- late vs early, using the already-treated early group as control ✗
If effects grow over time, the early group keeps changing after treatment. Using it as a "control" subtracts that growth. The estimate can even flip sign.
| Comparison | Weight | 2×2 estimate |
|---|---|---|
| Earlier T vs later C | 0.191 | 60.4 |
| Later T vs earlier C | 0.284 | −98.4 |
| T vs never treated | 0.525 | 6.5 |
| TWFE (weighted sum) | −13.0 |
Modern methods on the same data: +68.1. TWFE got the wrong sign.
The fix: Callaway & Sant'Anna
Rule: never use already-treated units as controls. For each cohort g (first treated in period g) and each later period t, compute a clean 2×2 DiD called ATT(g, t):
- Treated change: cohort g from s = g − 1 (last pre-period) to t.
- Control change: units not yet treated at t (or never treated), over the same s → t.
| Mean Y | t=1 | t=2 | t=3 | t=4 |
|---|---|---|---|---|
| A (treated from t=2) | 10 | 16 | 21 | 24 |
| B (treated from t=3) | 20 | 22 | 27 | 29 |
| N (never treated) | 30 | 32 | 35 | 36 |
- Cohort A, base period s = 1, target t = 3.
- Treated change: 21 − 10 = +11.
- Controls at t = 3: only N (B is treated by then). Change: 35 − 30 = +5.
- ATT(2,3) = 11 − 5 = 6.
All cells: A gets 4, 6, 8; B gets 2, 3. TWFE would also use "B vs already-treated A" from t=2 to 3: (27−22) − (21−16) = 0 instead of 2, because A's own effect grew from 4 to 6.
deeper Then aggregate: by event time (dynamic profile), by calendar time, or overall (average within each cohort first, then across cohorts weighted by size; a plain average over cells overweights early cohorts). Related estimators: Sun & Abraham (2021), de Chaisemartin & D'Haultfœuille (2020, who show TWFE can put negative weights on some ATT(g,t)). Overview: Roth et al. (2023).
Standard errors
Outcomes of the same unit are correlated over time, so ordinary SEs are far too small (Bertrand, Duflo & Mullainathan 2004). Cluster at the level where treatment is assigned (e.g. state). With very few treated clusters (one state!), even clustered SEs are unreliable.
Case study: minimum wage (Card & Krueger 1994)
- April 1992: New Jersey raised the minimum wage from $4.25 to $5.05. Pennsylvania didn't.
- 410 fast-food restaurants surveyed before (Feb–Mar) and after (Nov–Dec).
- Result: NJ employment rose by 2.33 full-time-equivalent workers per store relative to PA. Theory predicted a fall.
- Alternative: use each NJ store's "wage gap" (how much it had to raise wages). Coefficient 15.65 × average gap 0.11 ≈ +1.7 FTE.
- Prices of a meal rose about 3.3 % relative to PA. More full-time workers (+7.3 pp). No clear changes in opening hours.
- Debate: Neumark & Wascher (2000) used payroll data and found negative effects. Card & Krueger (2000) used administrative data and found none. The original paper showed no parallel-trends evidence, and one treated vs one control state limits inference.
Difference-in-differences in five questions
- What problem are we solving?A policy reaches some groups and not others. A before–after comparison mixes the effect with everything else that changed over time.
- How does the method solve it?Subtract the control group's change from the treated group's change. With staggered timing, build clean 2 × 2 comparisons (ATT(g, t)).
- What must hold?
- Parallel trends: same change without treatment (levels may differ).
- No anticipation before the start date.
- No spillovers onto the control group.
- Staggered timing with TWFE: effects must not change over time (otherwise use Callaway & Sant'Anna).
- What happens when it fails?
- Non-parallel trends: the bias equals the gap in trends.
- Anticipation: changes appear before the policy.
- TWFE with staggered, growing effects: wrong, sometimes wrong sign.
- How can we check?
- Event study: leads near zero (evidence, not proof).
- Sensitivity to plausible violations (Rambachan & Roth).
- Compare TWFE with clean estimators.
- Cluster SEs where treatment is assigned.
Apply: work it out
Pick an answer and you get feedback on that exact choice, then the reasoning. Answering every question here marks the lecture as practised.
Challenge: spot the problem
Each one hides a broken assumption or a wrong conclusion. Exam questions often look like this.
Try it in R optional
The same method on simulated data, with the exact output you should get (R 4.3, fixed seed). Skip it if you are only revising theory.
Lab 7 · DiD, an event study and staggered adoption about 20 min
# Lab 7: difference-in-differences, event study and staggered adoption
library(sandwich); library(lmtest); library(dplyr); library(ggplot2)
set.seed(7)
# (a) 2x2: sugar tax in city A, not in city B; 100 shops each, before and after
shops <- expand.grid(shop = 1:200, post = 0:1)
shops$cityA <- as.numeric(shops$shop <= 100)
shop_fe <- rnorm(200, 0, 3)
shops$sales <- 22 - 2 * shops$cityA - 1 * shops$post - 4 * shops$cityA * shops$post +
shop_fe[shops$shop] + rnorm(400)
m <- lm(sales ~ cityA * post, data = shops)
coeftest(m, vcov = vcovCL(m, cluster = ~shop)) # clustered by shop
# (b) Event study: 200 regions, half treated from period 0, effect grows over time
es <- expand.grid(unit = 1:200, t = -5:5)
es$treated <- as.numeric(es$unit <= 100)
es$y <- rnorm(200)[es$unit] + 0.3 * es$t + es$treated * pmax(es$t + 1, 0) + rnorm(nrow(es), 0, 0.5)
for (k in setdiff(-5:5, -1)) # one dummy per event time, k = -1 left out
es[[paste0("k", ifelse(k < 0, paste0("m", -k), k))]] <- as.numeric(es$treated == 1 & es$t == k)
ev <- lm(as.formula(paste("y ~ factor(unit) + factor(t) +",
paste(grep("^k", names(es), value = TRUE), collapse = " + "))), data = es)
ct <- coeftest(ev, vcov = vcovCL(ev, cluster = ~unit))[grep("^k", rownames(coef(summary(ev)))), 1:2]
plot_df <- data.frame(k = setdiff(-5:5, -1), est = ct[, 1], se = ct[, 2])
round(t(plot_df), 2)
ggplot(rbind(plot_df, data.frame(k = -1, est = 0, se = 0)), aes(k, est)) +
geom_hline(yintercept = 0) + geom_vline(xintercept = -0.5, linetype = 2) +
geom_pointrange(aes(ymin = est - 1.96 * se, ymax = est + 1.96 * se)) +
labs(x = "Periods relative to treatment (k)", y = "Estimated effect") + theme_minimal()
# (c) Staggered adoption: cohort A treated from t = 3, B from t = 6, N never; effects grow
st <- expand.grid(unit = 1:150, t = 1:8)
st$g <- c(3, 6, Inf)[ceiling(st$unit / 50)]
st$D <- as.numeric(st$t >= st$g)
st$effect <- ifelse(st$D == 1, 2 * (st$t - st$g + 1), 0) # 2, 4, 6, ... after adoption
st$y <- rnorm(150)[st$unit] + 0.5 * st$t + st$effect + rnorm(nrow(st), 0, 0.5)
twfe <- coef(lm(y ~ factor(unit) + factor(t) + D, data = st))["D"]
att_gt <- function(gg, tt) { # clean 2x2: cohort gg vs not-yet/never treated at tt
base <- gg - 1
ctrl <- st$g > tt
chg <- function(sel) mean(st$y[sel & st$t == tt]) - mean(st$y[sel & st$t == base])
chg(st$g == gg) - chg(ctrl)
}
cells <- do.call(rbind, lapply(c(3, 6), function(gg)
data.frame(g = gg, t = gg:8, att = sapply(gg:8, function(tt) att_gt(gg, tt)))))
true_avg <- mean(st$effect[st$D == 1])
round(cells, 2)
round(c(TWFE = unname(twfe), clean_average = mean(cells$att), true_average = true_avg), 2)
What you should see
t test of coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 22.341 0.321 69.62 < 2e-16 ***
cityA -1.960 0.441 -4.45 1.1e-05 ***
post -0.949 0.153 -6.20 1.4e-09 ***
cityA:post -3.975 0.209 -18.99 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
km5 km4 km3 km2 k0 k1 k2 k3 k4 k5
k -5.0 -4.00 -3.00 -2.00 0.00 1.00 2.00 3.00 4.00 5.00
est -0.2 -0.19 -0.09 -0.07 0.82 1.88 2.92 3.95 4.92 5.88
se 0.1 0.11 0.11 0.11 0.11 0.11 0.11 0.10 0.11 0.12
g t att
1 3 3 1.98
2 3 4 4.10
3 3 5 6.00
4 3 6 7.78
5 3 7 9.79
6 3 8 12.05
7 6 6 2.24
8 6 7 4.11
9 6 8 6.23
TWFE clean_average true_average
3.65 6.03 6.00
How to read it
- 2 × 2:
cityA:post= −3.98 (clustered SE 0.21) is the DiD estimate; the simulated truth is −4.cityAis the baseline gap andpostthe common time change. - Event study: leads (km5 to km2) are small, between −0.20 and −0.07; lags grow from 0.82 to 5.88, tracking the true effect 1, 2, …, 6. One average would hide that growth.
- Staggered adoption: TWFE gives 3.65, the average of clean ATT(g, t) cells gives 6.03, and the truth is 6.00. TWFE is dragged down by comparisons that use already-treated units as controls.
Common mistakes
- Clustering at the wrong level (here: by unit; for a state law, by state).
- Including the k = −1 dummy too: it is the reference period and must be left out.
- Running TWFE with staggered timing and effects that change over time.
- In the
didpackage, never-treated units must havegname = 0, not Inf (the CSV uses Inf; recode it).
With the dedicated packages (not run here)
# install.packages(c("did", "fixest"))
library(did)
st$g0 <- ifelse(is.infinite(st$g), 0, st$g) # did wants 0 for never-treated
cs <- att_gt(yname = "y", tname = "t", idname = "unit", gname = "g0",
data = st, control_group = "notyettreated")
aggte(cs, type = "dynamic") # event-study profile
library(fixest)
feols(sales ~ cityA:post | shop + post, data = shops, cluster = ~shop) # 2x2 with fixed effects
Review: quick recall
Answer in your head first, then tap to check. Come back to these a few days later.
Treated: 50 → 70. Control: 40 → 50. DiD?
(70 − 50) − (50 − 40) = 20 − 10 = 10.
Do treated and control need the same level of Y before the policy?
No. Only the same trend without treatment (parallel trends).
In an event study, what should the leads look like, and what do they prove?
Close to zero. They make parallel trends more plausible but can't prove it: the assumption is about the post-period, and pre-trend tests often lack the power to detect a harmful trend.
Which TWFE comparison causes trouble, and why?
Late-treated vs already-treated early units. If the early group's effect keeps changing, that change gets subtracted.
At what level should you cluster standard errors?
At the level where treatment is assigned (e.g. state for a state law).
- Mastering 'Metrics ch. 5 "Differences-in-Differences": §5.1 Mississippi bank failures (a great 2×2 example), §5.2 drinking age with many states (regression DiD, parallel-trends pictures). Figure 5.7 is John Snow's DD recipe.
- Modern staggered DiD: Roth et al. (2023) overview, or Cunningham's Causal Inference: The Mixtape (free online).
Studied all seven?
Cheat sheet
Everything side by side for the night before the exam.
How the causal methods fit together
One question, four methods: does schooling raise wages?
| Method | Design | What it estimates, and its weak spot |
|---|---|---|
| OLS | Log wage on years of school, with controls | Association for everyone; biased upward if ability is omitted (Lab 1: 0.108 vs 0.086 with ability; truth 0.08) |
| IV | Quarter of birth or college proximity as instrument | LATE for people whose schooling the instrument changed; weak-instrument and exclusion worries |
| RDD | School-entry or exam cut-off | Effect for people near the cut-off; needs no sorting |
| DiD | A reform raising the leaving age in some regions first | ATT for affected cohorts; needs parallel trends |
Why the numbers differ: different populations (everyone, compliers, people at a cut-off, treated cohorts) and different biases. If effects vary between people, these are different, legitimate numbers. State the estimand, check each design's assumptions, and treat agreement across methods as stronger evidence than any single estimate.
Which causal method?
- Can you randomise? Run an experiment and compare means. Done.
- Is there something random that pushes the treatment (lottery, weather, birth timing) but doesn't touch Y directly? Use IV.
- Does a rule switch treatment on at a cut-off of some score, age or date? Use RDD (fuzzy RDD if the cut-off only changes the odds).
- Did a policy hit some groups and not others, with data before and after? Use DiD (Callaway & Sant'Anna if timing is staggered).
- None of these? Regression with controls, but be honest that it shows association unless you've controlled for everything that matters.
The three methods compared
| IV | RDD | DiD | |
|---|---|---|---|
| Source of randomness | An instrument Z | A cut-off c in a running variable | A policy hitting one group at one time |
| Key formula | reduced form ÷ first stage | jump in Y at c (fuzzy: ÷ jump in D) | (ΔY treated) − (ΔY control) |
| Key assumption | Relevance, independence, exclusion (+ monotonicity) | Continuity: nothing else jumps at c; no sorting | Parallel trends; no anticipation; no spillovers |
| What you get | LATE for compliers | Effect at the cut-off | ATT, effect on the treated |
| Checks | First-stage F (≥ 10 classic; ~104 for plain t-tests); overid test | Density test, covariate balance, placebo cut-offs, donut, bandwidths | Event-study pre-trends (evidence, not proof); Bacon decomposition |
| Classic example | KIPP lottery: 0.355/0.741 = 0.48 SD | Austrian leave, 1 July 1990: +4.5 pp births | Snow's cholera: −48 − 49 = −97 |
| Mastering 'Metrics | ch. 3 | ch. 4 | ch. 5 |
Toolbox lectures in one line each
| Tool | Core idea | Key formula |
|---|---|---|
| OLS | Minimise squared residuals | β̂ = (XᵀX)⁻¹Xᵀy; one x: Cov/Var |
| Omitted variable bias | Left-out factor linked to x and y biases β̂ | β₁ = β₁* + β₃*·γ₁ |
| t-test | Is a coefficient zero? | t = β̂ / se(β̂) |
| MLE | Parameter that makes the data most probable | θ̂ = argmax Σ log f(Xᵢ|θ); Var ≈ 1/(nJ) |
| Bootstrap | Resample your data with replacement to see estimate spread | SE = SD of B bootstrap estimates |
| Potential outcomes | Only one of Y(1), Y(0) is seen | Randomisation: Y(1),Y(0) ⫫ D |
Log interpretation table
| Model | Meaning of β₁ |
|---|---|
| level–level | +1 unit x → +β₁ units y |
| log y, level x | +1 unit x → ≈ +100·β₁ % y |
| level y, log x | +1 % x → ≈ +β₁/100 units y |
| log–log | +1 % x → ≈ +β₁ % y (elasticity) |
Standard errors: which ones?
- Error variance changes with x: heteroskedasticity-robust (HC) SEs.
- Treatment assigned to groups, or panel data: cluster at the level of treatment assignment.
- IV: use an IV command, never the manual second stage.
- RDD: robust bias-corrected intervals (
rdrobust). - Estimator with no simple SE formula, moderate n: bootstrap (not a cure for tiny samples).
- Weak first stage in IV: Anderson–Rubin (weak-instrument-robust) intervals.
Choose your identification strategy
Seven research questions. Nobody tells you the method: think about how treatment was assigned, what data you have, and what you could estimate. The feedback explains the estimand and what to check.
Test yourself
Questions from the lectures you choose, in random order. Results are broken down by topic, and your answers here decide which lectures count as mastered.
The cheat sheet is hidden while an exam runs. For "write your own answer" questions you'll see a model answer and rate yourself honestly.
How "mastered" works
- A lecture is mastered when you have answered at least 60 % of its questions in exam mode and got at least 80 % of those right (your latest exam answer to each question counts), or when you get at least 80 % of its questions right in the .
- Wrong answers go on a "missed" list. Each lecture header shows a Retry missed button, and the option above runs only those.
- After a week, mastered lectures show review due. A short mixed exam a few days later does more for memory than rereading.
Everything, one last time
Thirty new questions from all seven lectures and from choosing a research design. Answer them all, then submit: you get a score for each lecture, feedback on every answer, and a study plan that sends you back to exactly what to revisit.
question(s) still unanswered. Unanswered questions count as "not yet known" in the study plan.