Act I
The U.S. spends more on health care than any other rich country — yet millions of adults have no insurance.
If we gave them coverage, would their health improve, or only their health-care bills?
| Group | Average health (1 = poor, 5 = excellent) |
|---|---|
| Uninsured | 3.66 |
| Insured | 3.98 |
National Health Interview Survey (NHIS) 2009 — married adults aged 26–59, \(n = 18{,}790\).
The insured look healthier. The question is whether insurance caused the gap.
import pandas as pd # pandas → load and summarize tables
# every course dataset lives in the repo's data/ folder on GitHub
GITHUB_DATA_URL = "https://raw.githubusercontent.com/cmg777/intro2causal/main/data/"
nhis = pd.read_csv(GITHUB_DATA_URL + "ch1/nhis_clean.csv") # NHIS 2009 extract
nhis.groupby("insurance")["health"].mean() # mean health: 0 = uninsured, 1 = insuredEach person has a health level with insurance (\(Y_{1i}\)) and without it (\(Y_{0i}\)).
People who choose treatment differ from those who don’t — before any treatment.
A lottery decides who is treated, so the groups are alike on average.
The chapter is the story of how the third idea solves the problem the first two reveal.
Act II
\[Y_{1i} - Y_{0i}\] Only one road is ever travelled, so one term is always missing.
A causal effect compares a person with themselves in another world — not with someone else.
| Anika | Ben | |
|---|---|---|
| Health without insurance (\(Y_{0i}\)) | 3 | 5 |
| Health with insurance (\(Y_{1i}\)) | 4 | 5 |
| Buys insurance? (\(D_i\)) | Yes | No |
| Observed health | 4 | 5 |
| True effect | +1 | 0 |
Insured Anika (4) looks worse than uninsured Ben (5), yet insurance helped her by +1.
\[\text{Observed difference} \;=\; \kappa \;+\; \text{Selection bias}\]
\[\text{Selection bias} \;=\; \text{Avg}[Y_{0i} \mid D_i=1] \;-\; \text{Avg}[Y_{0i} \mid D_i=0]\]
The health gap that would exist between the two groups even if nobody had insurance.
Unless the bias term is zero, the observed gap does not equal the causal effect \(\kappa\).
Because the same traits drive both insurance and health, the raw gap mixes their influence with any true effect.
The individual effect \(Y_{1i} - Y_{0i}\) is unobservable — this is the fundamental problem of causal inference.
We cannot fix it with more data on the same people. We need a research design that makes the selection-bias term disappear.
Causal inference is the art of building a believable stand-in for the road not taken.
\[Y_i = \alpha + \beta D_i + e_i\]
health ~ insuranceRegression on a dummy is just a comparison of means — plus a standard error for free.
| Purpose of the regression | What we hope to see |
|---|---|
| Balance check | small \(t\) — groups look alike |
| Treatment effect | large \(t\) — evidence of an effect |
Read every coefficient together with its standard error.
pf.feols call gives the gap and its standard error!pip install -q pyfixest # pyfixest is not preinstalled in Colab
import pandas as pd # pandas → load tables
import pyfixest as pf # pyfixest → regressions with robust SEs
GITHUB_DATA_URL = "https://raw.githubusercontent.com/cmg777/intro2causal/main/data/"
nhis = pd.read_csv(GITHUB_DATA_URL + "ch1/nhis_clean.csv") # NHIS 2009 extract
# health on the insurance dummy, with survey weights and robust (hetero) SEs
m = pf.feols("health ~ insurance", data=nhis, weights="weight", vcov="hetero")
m.summary() # Intercept 3.66 = uninsured; insurance 0.35 (0.02)| Characteristic | Uninsured mean | Insured − uninsured (SE) |
|---|---|---|
| Health index (1–5) | 3.66 | 0.35 (0.02) |
| Nonwhite (share) | 0.17 | −0.02 (0.01) |
| Age (years) | 40.51 | 2.61 (0.21) |
| Education (years) | 11.67 | 2.70 (0.07) |
| Family size | 3.95 | −0.45 (0.04) |
| Employed (share) | 0.72 | 0.13 (0.01) |
| Family income ($) | 45,989 | 60,352 (976) |
Insured adults are older, ~3 years more educated, and $60,000 richer — red flags for selection bias.
When a lottery — not the person — decides who is treated, choice can no longer bias the comparison.
The law of large numbers does the same for randomized groups: their averages converge to the population average — for age, income, motivation, genes, everything.
Large randomized groups are alike on observed and unobserved traits.
\[E[Y_{0i} \mid D_i = 1] = E[Y_{0i} \mid D_i = 0]\]
\[\Rightarrow\quad E[Y_i \mid D_i = 1] - E[Y_i \mid D_i = 0] = \kappa\]
Randomization makes the simple difference in means an unbiased estimate of the causal effect.
| When | Who | Contribution |
|---|---|---|
| 1747 | James Lind | Compared scurvy remedies on sailors — citrus worked |
| 1885 | Peirce & Jastrow | First deliberate random assignment |
| 1925 | R. A. Fisher | Formalized randomized experimental design |
| 1974 | RAND | The largest social experiment of its era |
Randomization went from a statistician’s idea to the gold standard for policy evaluation.
| Plan | Participants pay | Role |
|---|---|---|
| Catastrophic | ~95% of costs (capped) | Control (≈ no insurance) |
| Deductible | 95% of outpatient costs | Moderate treatment |
| Coinsurance | 25–50% of costs | Moderate treatment |
| Free | Nothing | Most generous treatment |
Two questions: does cheaper care raise use — and does more care improve health?
| Baseline trait | Catastrophic mean | Free − Cat. | Deductible − Cat. | Coinsurance − Cat. |
|---|---|---|---|---|
| Female (share) | 0.56 | −0.04 (0.01) | −0.02 (0.02) | −0.02 (0.02) |
| Age (years) | 32.4 | 0.43 (0.61) | 0.56 (0.68) | 0.97 (0.65) |
| Education (years) | 12.1 | −0.26 (0.18) | −0.16 (0.19) | −0.06 (0.19) |
| Family income ($) | 31,603 | −976 (1,345) | −2,104 (1,384) | 970 (1,389) |
| Health index | 70.9 | −1.31 (0.87) | −1.44 (0.95) | 0.21 (0.92) |
| Mental health | 73.9 | 0.89 (0.77) | −0.12 (0.82) | 1.19 (0.81) |
Gaps are small, point in both directions, and are mostly insignificant — randomization worked.
!pip install -q pyfixest # pyfixest is not preinstalled in Colab
import pandas as pd # pandas → load tables
import pyfixest as pf # pyfixest → regressions with clustered SEs
GITHUB_DATA_URL = "https://raw.githubusercontent.com/cmg777/intro2causal/main/data/"
rand = pd.read_csv(GITHUB_DATA_URL + "ch1/rand_balance.csv") # RAND baseline traits
# age on plan dummies; the catastrophic plan is the omitted control group
m = pf.feols("age ~ plan_free + plan_deductible + plan_coinsurance",
data=rand, vcov={"CRV1": "family_id"}) # cluster SEs by family
m.summary() # Intercept 32.4; plan gaps 0.4–1.0, all |t| < 2| Use per person-year | Catastrophic mean | Free effect (SE) | Deductible effect | Coinsurance effect |
|---|---|---|---|---|
| Doctor visits | 2.78 | +1.66 (0.25) | +0.19 (0.25) | +0.48 (0.24) |
| Outpatient spending ($) | 248 | +169 (20) | +42 (21) | +60 (21) |
| Hospital admissions | 0.10 | +0.03 (0.01) | +0.02 (0.01) | +0.00 (0.01) |
| Total spending ($) | 636 | +285 (72) | +114 (79) | +152 (84) |
When care became free, people used much more of it — the demand curve at work.
+45%
more health spending when care was free (+$285 on a $636 base)
!pip install -q pyfixest # pyfixest is not preinstalled in Colab
import pandas as pd # pandas → load tables
import pyfixest as pf # pyfixest → regressions with clustered SEs
GITHUB_DATA_URL = "https://raw.githubusercontent.com/cmg777/intro2causal/main/data/"
hie = pd.read_csv(GITHUB_DATA_URL + "ch1/rand_utilization.csv") # person-year use data
# total spending on plan dummies — causal, because plans were randomized
m = pf.feols("total_expenses ~ plan_free + plan_deductible + plan_coinsurance",
data=hie, vcov={"CRV1": "family_id"}) # cluster SEs by family
m.summary() # Intercept 636; plan_free +285 (SE 72)| Exit health measure | Catastrophic mean | Free effect (SE) | Deductible effect | Coinsurance effect |
|---|---|---|---|---|
| General health index | 68.5 | −0.78 (0.87) | −0.87 (0.96) | 0.61 (0.90) |
| Cholesterol | 203.2 | −1.83 (2.39) | 0.69 (2.57) | −2.31 (2.47) |
| Blood pressure | 121.9 | −0.52 (0.93) | 1.17 (1.06) | −1.39 (0.98) |
| Mental health index | 75.5 | 0.43 (0.82) | 0.45 (0.91) | 1.07 (0.87) |
45% more care bought no measurable improvement in health.
Free-plan effect on the general health index: \(-0.78\) with SE \(0.87\)
95% confidence interval \(\approx -0.78 \pm 2 \times 0.87 = [-2.5,\ 1.0]\) — on a control mean of 68.5
The experiment was big enough to find a meaningful effect; the effect simply wasn’t there.
A lottery for a public program created a new randomized experiment — for the people today’s debates are about.
\[\text{effect of insurance on enrollees} \approx \frac{\text{ITT}}{\text{effect of winning on enrollment}}\]
Scaling the ITT by the enrollment jump previews instrumental variables (Chapter 3).
| Outcome | Effect of winning the lottery |
|---|---|
| Medicaid enrollment | +25.6 percentage points |
| Emergency-department visits | +10% (a decrease was expected) |
| Self-reported health | Modest improvement (+3.9 pp) |
| Physical health (cholesterol, blood pressure) | No significant change |
| Mental health | Improved |
| Catastrophic expenses and medical debt | Decreased |
Insurance protected wallets and moods; it did not move clinical measures.
| RAND (1974–1982) | Oregon (2008) | |
|---|---|---|
| Population | Middle-class families | Low-income adults |
| More care used? | Yes | Yes |
| Better physical health? | No | No |
| Better mental health? | Not measured | Yes |
| Less financial hardship? | Not measured | Yes |
Insurance’s clearest benefits may be financial protection and peace of mind.
| Tool | Symbol | Plain meaning |
|---|---|---|
| Sample mean | \(\bar{Y}\) | The average in our data |
| Standard error | \(SE(\bar{Y})\) | How much \(\bar{Y}\) would vary across samples |
| t-statistic | coef / SE | How many SEs the estimate is from zero |
| 95% confidence interval | estimate \(\pm 2 \times SE\) | Values consistent with the data |
Any estimate from a sample would change with a new sample — inference quantifies by how much.
Always report the size of a coefficient and its precision.
Act III
Objection. Insured adults report better health, so coverage must improve health.
Response. The gap bundles any causal effect with selection bias — the insured were already older, richer and better educated. When RAND randomized coverage, the health advantage disappeared.
Objection. RAND’s health effects are insignificant, so maybe the study was just too small.
Response. Only if the estimate is imprecise. RAND’s confidence intervals are narrow and exclude large gains — a precisely estimated null, not a failure to look.