The Question

Act I

Does health insurance actually make people healthier?

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?

In survey data, insured adults report better health

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.

Three lines of pandas reproduce the naive comparison

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 = insured

↗ Try it in Colab

Our route through the chapter

  • The question — why a simple comparison cannot settle cause and effect
  • The investigation — potential outcomes, selection bias, and regression on a dummy; why random assignment removes the bias; two landmark insurance experiments (RAND and Oregon)
  • The resolution — what the experiments teach, how to read standard errors, and the traps to avoid

Three ideas carry the whole chapter

Potential outcomes

Each person has a health level with insurance (\(Y_{1i}\)) and without it (\(Y_{0i}\)).

Selection bias

People who choose treatment differ from those who don’t — before any treatment.

Random assignment

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.

The Investigation

Act II

Every person has two potential outcomes, but we only ever see one

The two roads

  • \(Y_{1i}\) — person \(i\)’s health with insurance
  • \(Y_{0i}\) — the same person’s health without it

The causal effect

\[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.

A naive comparison can even get the sign wrong

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.

Any observed gap = causal effect + selection bias

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

Confounders open a back-door path from insurance to health

affects

affects

causal effect?

Confounders: education, income

Insurance status

Health

Because the same traits drive both insurance and health, the raw gap mixes their influence with any true effect.

We can never observe both roads for the same person

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.

Regressing an outcome on a dummy reproduces the difference in means

\[Y_i = \alpha + \beta D_i + e_i\]

  • \(\alpha\) (intercept) = average \(Y\) when \(D_i = 0\)
  • \(\beta\) (slope) = treated mean − control mean
  • \(D_i\) is a dummy: 1 = insured, 0 = uninsured
  • In code: health ~ insurance

Regression on a dummy is just a comparison of means — plus a standard error for free.

A standard error tells us whether a gap is bigger than noise

  • Standard error (SE): how much an estimate would wobble across different samples
  • t-statistic = coefficient ÷ SE
  • Rule of thumb: \(|t| > 2\) → statistically significant at the 5% level
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.

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

↗ Try it in Colab

The insured differ from the uninsured on almost everything

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.

With enough people, randomization balances even what we cannot measure

  • Roll a die 10 times: the average may be far from 3.5
  • Roll it 10,000 times: the average is almost exactly 3.5

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.

Under random assignment the selection-bias term vanishes

\[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.

Controlled comparisons have a long history

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.

RAND randomized ~4,000 people across insurance plans of different generosity

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?

Before the experiment, plan groups looked alike

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.

A balance check is the same regression, run on a baseline trait

!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

↗ Try it in Colab

Free care raised doctor visits and spending sharply

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.

Total spending: free plan vs catastrophic plan

+45%

more health spending when care was free (+$285 on a $636 base)

Estimating a causal effect uses exactly the same call

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

↗ Try it in Colab

Yet health at the end was no better on the free plan

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.

The null is precise: the data rule out large health gains

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.

Oregon’s 2008 lottery tested insurance for low-income adults

  • RAND studied middle-class families who all had at least catastrophic coverage
  • Oregon: about 75,000 low-income, uninsured adults applied to expanded Medicaid
  • About 30,000 were drawn by lottery to apply for coverage

A lottery for a public program created a new randomized experiment — for the people today’s debates are about.

Only a quarter of winners enrolled, so we first measure intent-to-treat

  • Intent-to-treat (ITT): the effect of winning the lottery, whether or not the winner enrolled
  • Winning raised Medicaid enrollment by about 25.6 percentage points

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

Winning improved finances and mental health, not physical health

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.

Two experiments, decades apart, agree on physical health

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.

Standard errors and confidence intervals measure sampling noise

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.

Significance measures precision, not importance

  • A huge sample can make a tiny effect “significant”
  • A small sample can leave a large effect “insignificant”
  • Not significant ≠ no effect — it may just mean too little data

Always report the size of a coefficient and its precision.

The Resolution

Act III

The tempting mistake: “The insured are healthier, so insurance works”

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.

The tempting mistake: “Not significant means no effect”

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.

Key takeaways

  • Any comparison of groups = causal effect + selection bias
  • Random assignment sets selection bias to zero — then check balance
  • Regression on a dummy = difference in means + a standard error
  • RAND: free care raised spending 45% but did not improve health
  • Oregon: insurance brought financial protection and better mental health

Random assignment turns a comparison of groups into a causal effect.