Applied Bayesian Inference · Chapter 15

Chapter 15
Bayesian nonparametrics for causal questions

Lectures 35–37 · priors on distributions and on functions, the Dirichlet process and the Bayesian bootstrap, Bayesian causal forests, and why nonparametric does not mean assumption-free

Cartoon: a stick broken into piecesA person holds a long stick and snaps pieces off it, each smaller than the last; the pieces lie on the ground in a row of decreasing lengths. A robot nearby holds a ruler and says: Infinitely many parameters? Then which ones do I estimate? The person answers: The ones the data can see. Infinitely many parameters?Which ones do I estimate? The ones the datacan see.
a prior with room to grow

Every model so far has had a fixed, finite set of parameters: the mean and standard deviation of a normal, the coefficients of a regression, the means and weights of a mixture with K components. When the family is right, that is efficient; when it is wrong, the posterior can be confident and mistaken. Chapter 13's job-training analysis needed a hurdle lognormal for earnings, and the answer for individuals depended on how well it fitted the tails. Ambulance response times in New York have a long right tail and several bumps. Treatment effects in a national education experiment may vary with a dozen characteristics of students and schools in ways nobody would think to write down.

Bayesian nonparametric models put priors on objects that are not described by a fixed number of parameters: on whole probability distributions, and on whole functions. The name is misleading, because such models have more parameters, not fewer, infinitely many, of which the data determine how many matter. This chapter gathers threads that have appeared through the course (the Gaussian processes of Chapter 8, the regression trees of Chapters 10 and 13, the question in Chapter 11 of how many clusters to fit) into one framework, and applies it to causal questions, where flexible models matter most and are most easily misunderstood.

Think About It: Before You Start

A colleague says: "I used a nonparametric method, so my results don't depend on any modelling assumptions." Which assumptions do you think remain? Consider what the method assumes about how units were assigned to treatment, about which covariates were measured, about regions of the data where there are no treated (or no control) units, and about what the prior says when the data are silent. Revisit your list at the end of Lecture 37.

Lecture 35 — Priors on distributions

15.1 When the family is wrong

Chapter 6 analysed the response times of New York City's ambulances to the most serious medical emergencies, on the log scale, with a multilevel normal model. Before trusting any normal model for these data, look at their distribution. The data are the 2024 calls with the highest severity level from NYC Open Data, cached locally after the first download.

R
library(tidyverse)
library(dirichletprocess)
library(stochtree)
library(dbarts)
library(jsonlite)
options(mc.cores = parallel::detectCores())

harbour <- c(ink = "#10191d", muted = "#4a5f68", grid = "#d5e3e6",
             sky = "#1d5187", amber = "#805907", rose = "#ad3b28",
             moss = "#1d6b54", lilac = "#5b3a94")
theme_harbour <- function(base_size = 11) {
  theme_minimal(base_size = base_size) +
    theme(text = element_text(colour = harbour[["ink"]]),
          axis.text = element_text(colour = harbour[["muted"]]),
          panel.grid.major = element_line(colour = harbour[["grid"]]),
          panel.grid.minor = element_blank(),
          strip.text = element_text(colour = harbour[["ink"]], hjust = 0),
          legend.position = "top")
}
theme_set(theme_harbour())
R
f <- "data/ems/ems_2024_severity1.csv"
if (!file.exists(f)) {
  dir.create("data/ems", showWarnings = FALSE)
  url <- paste0("https://data.cityofnewyork.us/resource/76xm-jjuj.json?",
    "$select=incident_datetime,initial_call_type,borough,zipcode,incident_response_seconds_qy,valid_incident_rspns_time_indc",
    "&$where=initial_severity_level_code='1' AND incident_datetime between '2024-01-01T00:00:00' and '2024-12-31T23:59:59'",
    "&$limit=50000")
  write_csv(as_tibble(fromJSON(URLencode(url))), f) }
ems <- read_csv(f, show_col_types = FALSE) |>
  filter(valid_incident_rspns_time_indc == "Y", incident_response_seconds_qy > 0) |>
  mutate(minutes = incident_response_seconds_qy / 60, borough = str_to_title(str_remove(borough, "^RICHMOND / ")))
c(calls = nrow(ems), quantile(ems$minutes, c(0.01, 0.1, 0.5, 0.9, 0.99))) |> round(2)
set.seed(15); y <- sample(log(ems$minutes), 1500)            # a random 1,500 calls for the models below
c(skewness_of_log = mean((y - mean(y))^3) / sd(y)^3, excess_kurtosis_of_log = mean((y - mean(y))^4) / sd(y)^4 - 3) |> round(2)
output
   calls       1%      10%      50%      90%      99% 
24404.00     1.22     3.08     5.57     9.28    16.32 
       skewness_of_log excess_kurtosis_of_log 
                 -0.58                   2.89 

The 24,404 calls in 2024 with valid response times had a median response of 5.6 minutes; 10% took less than 3.1 minutes and 10% more than 9.3, and 1% took more than 16.3. On the log scale, where Chapter 6's model assumed normality, the distribution is skewed to the left (skewness −0.58) and has heavier tails than a normal (excess kurtosis 2.89): there are more very fast responses, and more very slow ones, than a lognormal allows. A normal model of log times would describe the middle well and the tails, where equity questions live, badly.

Which family should we use? We could try a few, lognormal, gamma, a mixture of two lognormals, and compare them, as in earlier chapters. The nonparametric answer is different: put a prior directly on the distribution, wide enough to contain all of these and much else, and let the data decide its shape.

15.2 The Dirichlet process

A prior on distributions must produce a random probability distribution G each time it is sampled. The most important such prior, introduced by Thomas Ferguson (1973), is defined by what it says about every way of cutting the space into pieces.

Definition: Dirichlet Process

Let G0 be a probability distribution (the base measure) and α>0 (the concentration). A random distribution G follows a Dirichlet process, G∼DP(α,G0), if for every partition of the space into sets A1,…,Ak,

(G(A1),…,G(Ak))∼Dirichlet(αG0(A1),…,αG0(Ak)).

Two consequences follow from properties of the Dirichlet distribution. The expected value of G(A) is G0(A), so G0 is the prior guess; and its variance is G0(A)(1−G0(A))/(α+1), so α says how tightly G concentrates around the guess, like a prior sample size. The definition is abstract; three constructive descriptions make it concrete.

The posterior is as simple as the prior. Given observations x1,…,xn from G,

G∣x1,…,xn∼DP(α+n,αα+nG0+1α+n∑i=1nδxi),

a Dirichlet process whose base measure mixes the prior guess with the empirical distribution of the data, weighted by α and n. It is the beta–binomial update of Chapter 1, carried out on every set at once.

Breaking sticks

One draw of G from a Dirichlet process with a standard normal base measure, by stick-breaking: each spike is an atom at a location drawn from G0, with height equal to its weight. The bars below show how 200 customers of the Chinese restaurant process with the same α divided themselves between tables.

15.3 The Bayesian bootstrap

Let the concentration go to zero. The posterior then puts all its weight on the observed values, and the weights it gives them are Dirichlet(1,…,1): the posterior of the unknown distribution is a random reweighting of the data. This is Donald Rubin's (1981) Bayesian bootstrap. To get the posterior of any summary of the distribution (a mean, a median, a 90th percentile, a share above a threshold), draw weights w∼Dirichlet(1,…,1), compute the weighted summary, and repeat. It is Efron's bootstrap with smooth weights instead of whole-number resampling counts, and it shows that the bootstrap, usually justified by frequentist arguments, also has a Bayesian reading: it is the posterior under a model that assumes nothing about the shape of the distribution, only that the observed values are the possible ones.

R
bb <- function(x, stat, draws = 4000) {                       # Bayesian bootstrap posterior of stat(x, w)
  map_dbl(seq_len(draws), function(i) { w <- rexp(length(x)); stat(x, w / sum(w)) }) }
wq <- function(x, w, p) { o <- order(x); x[o][which(cumsum(w[o]) >= p)[1]] }
set.seed(15)
ems |> group_by(borough) |>
  group_modify(~ { m <- .x$minutes
    p90 <- bb(m, function(x, w) wq(x, w, 0.9), 1000); over10 <- bb(m, function(x, w) sum(w * (x > 10)), 1000)
    tibble(calls = length(m), p90_mean = mean(p90), p90_lo = quantile(p90, 0.05), p90_hi = quantile(p90, 0.95),
           over10_mean = mean(over10), over10_lo = quantile(over10, 0.05), over10_hi = quantile(over10, 0.95)) }) |>
  mutate(across(where(is.double), ~ round(.x, 3)))
output
# A tibble: 5 x 8
# Groups:   borough [5]
  borough       calls p90_mean p90_lo p90_hi over10_mean over10_lo over10_hi
  <chr>         <int>    <dbl>  <dbl>  <dbl>       <dbl>     <dbl>     <dbl>
1 Bronx          5483     9.41   9.25   9.58       0.078     0.072     0.083
2 Brooklyn       6539     8.90   8.78   9.02       0.058     0.053     0.063
3 Manhattan      5597     8.84   8.68   9          0.059     0.054     0.064
4 Queens         5528    10.1    9.98  10.3        0.106     0.099     0.112
5 Staten Island  1257     9.01   8.72   9.42       0.059     0.048     0.071

Queens stands out: its 90th percentile response time is 10.1 minutes (90% interval 10.0 to 10.3) and 10.6% of its calls took more than ten minutes (9.9% to 11.2%), against about 5.8% in Brooklyn and Manhattan and 7.8% in the Bronx. Staten Island, with only 1,257 calls, has the widest intervals. No distributional assumption was needed. One assumption remains: the Bayesian bootstrap treats calls as exchangeable draws, while calls on the same day share traffic and staffing, so these intervals are somewhat too narrow; a version that reweights days rather than calls would respect that dependence.

15.4 Mixtures with no fixed number of components

A distribution drawn from a Dirichlet process is discrete, which is fine for the Bayesian bootstrap but useless as a model for a continuous density. The remedy is to use G as a mixing distribution: give each observation its own parameter drawn from G, and its value from a smooth kernel around that parameter,

G∼DP(α,G0),(μi,σi)∣G∼G,yi∣μi,σi∼normal(μi,σi).

Because G is discrete, observations share parameter values, and the model is a mixture of normals with a random and unbounded number of components: a Dirichlet-process mixture. It answers the question of Chapter 11, how many components, by not fixing the number at all. We fit it to the log response times of 1,500 calls with the dirichletprocess package, which uses a Gibbs sampler built on the restaurant process and puts a gamma prior on α, and compare the fitted density with a lognormal.

R
dp_path <- "fits/ch15_dpm.rds"
z <- (y - mean(y)) / sd(y)                                   # the package's default base measure suits standardised data
if (file.exists(dp_path)) dp <- readRDS(dp_path) else {
  set.seed(15); dp <- Fit(DirichletProcessGaussian(z), 1500, progressBar = FALSE); saveRDS(dp, dp_path) }
keep <- 501:1500
clusters <- map_int(dp$labelsChain[keep], ~ length(unique(.x)))
big <- map_int(dp$labelsChain[keep], ~ sum(table(.x) >= 0.05 * length(z)))  # components holding at least 5% of calls
c(alpha_mean = mean(dp$alphaChain[keep]), components_median = median(clusters), components_q05 = unname(quantile(clusters, 0.05)),
  components_q95 = unname(quantile(clusters, 0.95)), large_components_median = median(big)) |> round(2)
output
             alpha_mean       components_median          components_q05 
                   5.85                   54.00                   33.00 
         components_q95 large_components_median 
                  77.00                    5.00 
R
xg <- seq(min(z) - 0.5, max(z) + 0.5, length.out = 300)
pf <- PosteriorFrame(dp, xg, ndraws = 200, ci_size = 0.1)
dens <- tibble(log_min = mean(y) + sd(y) * xg, dp_mean = pf$Mean / sd(y), lo = pf$X5. / sd(y), hi = pf$X95. / sd(y),
               lognormal = dnorm(mean(y) + sd(y) * xg, mean(y), sd(y)))
ggplot() +
  geom_histogram(aes(x = y, y = after_stat(density)), bins = 60, fill = harbour[["grid"]], colour = NA) +
  geom_ribbon(data = dens, aes(log_min, ymin = lo, ymax = hi), fill = harbour[["rose"]], alpha = 0.25) +
  geom_line(data = dens, aes(log_min, dp_mean), colour = harbour[["rose"]], linewidth = 0.9) +
  geom_line(data = dens, aes(log_min, lognormal), colour = harbour[["sky"]], linewidth = 0.8, linetype = 2) +
  scale_x_continuous(breaks = log(c(1, 2, 5, 10, 20)), labels = c(1, 2, 5, 10, 20)) +
  labs(x = "response time (minutes, log scale)", y = "density")
A histogram over response times from about 1 to 30 minutes on a log scale, peaked near 5.5A histogram over response times from about 1 to 30 minutes on a log scale, peaked near 5.5 minutes with a longer left tail. The red Dirichlet-process curve follows the histogram closely, including a shoulder at short times; the dashed blue lognormal curve is too low at the peak and misses the shoulder. 0.00 0.25 0.50 0.75 1.00 1 2 5 10 20 response time (minutes, log scale) density
The distribution of response times (minutes, log scale) for 1,500 high-severity calls: histogram, posterior mean density from the Dirichlet-process mixture (red, with a 90% band) and the best-fitting lognormal (blue, dashed).

The concentration parameter's posterior mean is 5.9, and the mixture uses a median of 54 components (90% interval 33 to 77), although only about five of them hold more than 5% of the calls each; the rest are small components that shape the tails. The fitted density follows the histogram closely, including a shoulder of fast responses that the lognormal misses and a heavier right tail. The number of components, though, should not be read as the number of kinds of call: as Miller and Harrison (2013) showed, a Dirichlet-process mixture keeps adding small components as the sample grows, even when the truth has only a few.

15.5 Nonparametric estimates of causal effects

The Bayesian bootstrap gives the simplest possible Bayesian analysis of a randomised experiment: a posterior for the difference in mean outcomes that assumes nothing about the shape of the outcome distributions. For the job-training experiment of Section 13.3, reweight the trainees and the controls separately and take the difference of weighted means.

R
gh <- "https://raw.githubusercontent.com/cran/"
tmp <- tempfile(); download.file(paste0(gh, "causaldata/master/data/nsw_mixtape.rda"), tmp, quiet = TRUE, mode = "wb"); load(tmp)
nsw <- nsw_mixtape |> transmute(treat, re78 = re78 / 1000)
set.seed(15)
wmean <- function(x) { w <- rexp(length(x)); sum(w * x) / sum(w) }
ate_bb <- map_dbl(1:4000, ~ wmean(nsw$re78[nsw$treat == 1]) - wmean(nsw$re78[nsw$treat == 0]))
c(mean = mean(ate_bb), quantile(ate_bb, c(0.05, 0.95)), prob_positive = mean(ate_bb > 0)) |> round(3)
output
         mean            5%           95% prob_positive 
        1.803         0.744         2.884         0.999 

The Bayesian bootstrap gives a posterior mean effect of $1,800, with a 90% interval from $740 to $2,880 and a probability of 0.999 that the effect is positive. That is centred near the hurdle-lognormal model's estimate of Section 13.3 ($1,690) but much narrower than its 90% interval ($90 to $3,380). The difference is instructive. The lognormal model allows earnings far larger than any observed, and the uncertainty about its tail feeds into the uncertainty about the mean; the Bayesian bootstrap assumes that only the observed values can occur, so it cannot imagine a trainee richer than the richest one seen. For heavy-tailed outcomes the nonparametric answer is not automatically the more cautious one (Exercise 3).

Observational studies need more: the effect must be computed by g-computation (Section 13.9), averaging an outcome model's predictions with and without treatment over the distribution of the covariates. A fully nonparametric Bayesian analysis models both parts flexibly, the outcome surface with BART or a Gaussian process (Lecture 36) and the covariate distribution with a Dirichlet process or its limit, the Bayesian bootstrap. Using Bayesian-bootstrap weights instead of the fixed sample average adds the uncertainty about the population the covariates come from, so the posterior describes the super-population effect, not just the effect in the sample.

Paper in Focus: A Practical Introduction to Bayesian Causal Estimation

The paper. Arman Oganisian and Jason Roy, "A practical introduction to Bayesian estimation of causal effects: parametric and nonparametric approaches", Statistics in Medicine 40(2), 2021. A tutorial written for applied researchers in health, it walks through Bayesian g-computation, from parametric regressions to nonparametric models, with code.

The argument. Causal effects are functionals of two unknowns, the outcome model and the distribution of confounders, and a Bayesian analysis needs priors on both. The authors show how a Dirichlet process (or its Bayesian-bootstrap limit) handles the confounder distribution without assumptions, how flexible outcome models (Dirichlet-process mixtures of regressions, BART, Gaussian processes) handle the response surface, and how the posterior of the average effect is obtained by combining draws of both. They emphasise that identification still rests on ignorability, positivity and consistency, which no prior can supply.

Reading it. Find where each identifying assumption is used, which parts of the model are nonparametric and which are not, and how uncertainty about the covariate distribution enters. Then compare the paper's nonparametric posterior for an average effect with a parametric one on the same data, as we did in this section: when the two agree, the parametric model was adequate for that estimand; when they disagree, the disagreement is information.

The Bayesian bootstrap has a second derivation that leads somewhere new. A parameter such as a mean is a property of the whole population. If we could see the population, we would know the parameter; our uncertainty about it is uncertainty about the observations we have not seen. So, Edwin Fong, Chris Holmes and Stephen Walker (2023) argued, the posterior can be computed by imputing the missing population: draw yn+1 from a predictive distribution given y1:n, then yn+2 given y1:n+1, and so on, and compute the parameter from the completed sequence. Repeating the imputation gives a distribution for the parameter, the martingale posterior. It is a proper posterior if the predictive distributions form a martingale: the predictive for the next-but-one observation, averaged over the next, is the predictive for the next. Then the imputed sequence settles down (its empirical distribution converges), and the parameter has a well-defined limit.

The simplest predictive rule is the Pólya urn: the next value is a copy of one of the values seen so far, chosen at random, including the imputed ones. It is the Dirichlet-process predictive of this lecture with α→0. Its martingale posterior is exactly the Bayesian bootstrap, as the simulation shows for the NSW earnings.

R
gh <- "https://raw.githubusercontent.com/cran/"
tmp <- tempfile(); download.file(paste0(gh, "causaldata/master/data/nsw_mixtape.rda"), tmp, quiet = TRUE, mode = "wb"); load(tmp)
nsw <- nsw_mixtape |> transmute(treat, re78 = re78 / 1000)
polya_impute <- function(x, N) { n <- length(x); v <- c(x, numeric(N))   # Polya urn: copy a random earlier value
  for (i in 1:N) v[n + i] <- v[sample.int(n + i - 1, 1)]; v }
t1 <- nsw$re78[nsw$treat == 1]; t0 <- nsw$re78[nsw$treat == 0]
set.seed(15)
mp <- map_dbl(1:2000, ~ mean(polya_impute(t1, 5000)) - mean(polya_impute(t0, 5000)))   # impute 5,000 more per arm
wmean <- function(x) { w <- rexp(length(x)); sum(w * x) / sum(w) }
bb <- map_dbl(1:2000, ~ wmean(t1) - wmean(t0))
rbind(martingale_posterior = c(mean = mean(mp), sd = sd(mp), quantile(mp, c(0.05, 0.95))),
      bayesian_bootstrap   = c(mean = mean(bb), sd = sd(bb), quantile(bb, c(0.05, 0.95)))) |> round(3)
set.seed(2); v <- polya_impute(t1, 20000)
round(c(cumsum(v) / seq_along(v))[length(t1) + c(0, 10, 100, 1000, 5000, 20000)], 3)   # running mean settles down
output
                      mean    sd    5%   95%
martingale_posterior 1.787 0.677 0.709 2.935
bayesian_bootstrap   1.788 0.685 0.714 2.981
[1] 6.349 6.304 6.132 6.275 6.231 6.072

The two posteriors for the effect of the training programme agree to within simulation error: mean $1,790, standard deviation about $680, 90% interval roughly $710 to $2,950. The running mean of one imputed population of treated earnings moves from $6,350 (the observed mean) to $6,070 after 20,000 imputations, changing less and less: the martingale has converged, and its limit is one draw from the posterior.

Why bother, if the answer is the Bayesian bootstrap? Because the predictive rule can be anything that forms a martingale, and it need not come from a likelihood and a prior. Fong, Holmes and Walker use a smooth rule, a sequence of bivariate copula updates, which gives a posterior for densities and regression functions with no prior specified at all, and which can be computed in parallel quickly. The approach puts the emphasis where Section 13.3 put it, on predicting the unseen, and it offers a principled answer to the question this lecture keeps returning to: how to be Bayesian when you do not believe any parametric model.

Check Yourself

A Dirichlet-process mixture fitted to 1,500 response times uses about 40 components on average. What does this tell you?

Key Takeaways: Lecture 35

  • Bayesian nonparametric models put priors on infinite-dimensional objects, distributions and functions, so that the data decide how much structure to fit.
  • The Dirichlet process is a prior on distributions with a conjugate posterior; stick-breaking, the Pólya urn and the restaurant process describe it constructively.
  • Its limit as α→0 is the Bayesian bootstrap, which gives posteriors for any summary of a distribution, and nonparametric posteriors for randomised experiments.
  • Dirichlet-process mixtures estimate densities without fixing the number of components (the response-time density needed about five substantial components and dozens of tiny ones), but the number of components they use is not a reliable count of subgroups.

Lecture 36 — Priors on functions

15.6 Priors on functions

Causal effects are differences between values of a function. Write μ(x,t)=𝔼[Y∣X=x,T=t] for the response surface; under ignorability (Section 13.2) the conditional average treatment effect is τ(x)=μ(x,1)−μ(x,0), and average effects are averages of τ(x) over the covariates. A linear model forces μ into a narrow family; a nonparametric prior lets the data shape it. Two such priors have already appeared in this course.

Both are consistent: with enough data spread across the covariate space, their posteriors concentrate on the true function. But "spread across the covariate space" is the crux. The figure shows both priors fitted to simulated data in which treated units are missing from part of the range of the confounder: the situation that Section 13.9 called a failure of overlap.

R
set.seed(15)
x <- runif(150, -2, 1); f <- function(x) 1 + x + 0.5 * x^2; yv <- f(x) + rnorm(150, 0, 0.5)
xg <- seq(-2, 3, length.out = 120)
bfit <- bart(x.train = matrix(x), y.train = yv, x.test = matrix(xg), ndpost = 1000, nskip = 500, verbose = FALSE)
bart_band <- tibble(x = xg, mean = colMeans(bfit$yhat.test), lo = apply(bfit$yhat.test, 2, quantile, 0.05),
                    hi = apply(bfit$yhat.test, 2, quantile, 0.95), model = "BART")
gp_fit <- function(x, y, xs) {                                 # GP with squared-exponential kernel; hyperparameters by marginal likelihood
  nll <- function(p) { l <- exp(p[1]); s <- exp(p[2]); n <- exp(p[3])
    K <- s^2 * exp(-outer(x, x, "-")^2 / (2 * l^2)) + diag(n^2, length(x)); L <- chol(K)
    a <- backsolve(L, forwardsolve(t(L), y - mean(y))); 0.5 * sum((y - mean(y)) * a) + sum(log(diag(L))) }
  p <- exp(optim(c(0, 0, -1), nll)$par)
  K <- p[2]^2 * exp(-outer(x, x, "-")^2 / (2 * p[1]^2)) + diag(p[3]^2, length(x)); Ks <- p[2]^2 * exp(-outer(xs, x, "-")^2 / (2 * p[1]^2))
  Ki <- solve(K); m <- mean(y) + Ks %*% Ki %*% (y - mean(y)); v <- p[2]^2 - rowSums((Ks %*% Ki) * Ks)
  tibble(x = xs, mean = as.vector(m), lo = as.vector(m) - 1.645 * sqrt(pmax(v, 0)), hi = as.vector(m) + 1.645 * sqrt(pmax(v, 0)), model = "Gaussian process") }
bands <- bind_rows(bart_band, gp_fit(x, yv, xg)) |> mutate(truth = f(x))
ggplot(bands, aes(x)) + geom_ribbon(aes(ymin = lo, ymax = hi), fill = harbour[["sky"]], alpha = 0.25) +
  geom_line(aes(y = mean), colour = harbour[["sky"]], linewidth = 0.9) + geom_line(aes(y = truth), linetype = 2, colour = harbour[["ink"]]) +
  geom_point(data = tibble(x = x, y = yv), aes(x, y), size = 0.7, alpha = 0.5, colour = harbour[["muted"]], inherit.aes = FALSE) +
  geom_vline(xintercept = 1, colour = harbour[["grid"]]) + facet_wrap(~ model) +
  coord_cartesian(ylim = c(-2, 9)) + labs(x = "confounder x", y = "outcome under treatment")
Two panels over x from minus 2 to 3Two panels over x from minus 2 to 3. Points and both posterior means follow a rising dashed curve up to x equals 1. To the right, the BART curve is flat with a narrow band that misses the rising truth; the Gaussian process curve bends back toward zero with a wide band that includes the truth. BART Gaussian process -2 -1 0 1 2 3 -2 -1 0 1 2 3 -2.5 0.0 2.5 5.0 7.5 confounder x outcome under treatment
Posterior means and 90% bands for the treated response surface from BART (left) and a Gaussian process (right), fitted to 150 simulated treated units observed only for x below 1 (points). The true surface (dashed) keeps rising. Beyond the data, BART extrapolates flat, with bands that widen only modestly; the Gaussian process reverts toward its prior mean, with much wider bands.

Neither prior can know what happens where there are no data; each answers according to its own assumptions. BART's sum of trees is constant beyond the last split, so it extrapolates flat, and because its uncertainty comes mostly from the data it has seen, its bands stay too narrow. The Gaussian process returns to its prior mean, with uncertainty growing back to the prior's, which is more honest but still a prior, not knowledge. Wherever treated and control units do not overlap, a nonparametric model's estimate of τ(x) is an extrapolation dressed as an estimate.

15.7 Regularisation-induced confounding

Flexible models need regularisation, and in causal inference regularisation can do harm in a way it cannot in prediction. Richard Hahn, Jared Murray and Carlos Carvalho (2020) identified the mechanism. Suppose treatment is targeted: units with high expected outcomes are more likely to be treated, so the propensity score π(x)=Pr(T=1∣x) rises with μ(x,0). A BART model with T as just another covariate must learn μ(x,0) from the covariates, but its prior shrinks toward simple functions, and when a simple function of x fits less well than T, the model attributes to treatment what belongs to the confounding: regularisation-induced confounding. The cure is to give the model the propensity score as an extra covariate, so that the confounding is easy to represent, and, better still, to separate the prognostic surface from the treatment effect:

yi=μ(xi,π^(xi))+τ(xi)ti+εi,εi∼normal(0,σ2),

with separate BART priors on μ and τ. This is the Bayesian causal forest (BCF). The prior on τ uses fewer, shallower trees, so it shrinks toward a homogeneous effect, and heterogeneity has to earn its place. We compare the three approaches on simulated data with targeted selection and a constant true effect of 3, following the design of Hahn, Murray and Carvalho (2020), over 30 data sets of 250 units. The propensity score is itself estimated by BART, so that it can capture the nonlinear targeting.

R
ric_path <- "fits/ch15_ric.rds"
sim_ric <- function(n = 250) {
  x <- matrix(rnorm(n * 3), n, 3); x4 <- rbinom(n, 1, 0.5); x5 <- sample(1:3, n, replace = TRUE)
  mu <- -6 + c(2, -1, -4)[x5] + 6 * abs(x[, 3] - 1)                 # prognostic surface
  pi <- 0.8 * pnorm(3 * mu / sd(mu) - 0.5 * x[, 1]) + 0.05 + runif(n) / 10   # targeted selection
  t <- rbinom(n, 1, pi); y <- mu + 3 * t + rnorm(n)                  # constant effect of 3
  list(X = data.frame(x1 = x[, 1], x2 = x[, 2], x3 = x[, 3], x4 = x4, x5 = x5), t = t, y = y, pi = pi) }
fit_ric <- function(d, seed) {
  set.seed(seed)
  ps <- colMeans(pnorm(dbarts::bart(as.matrix(d$X), d$t, ndpost = 500, nskip = 250, verbose = FALSE)$yhat.train))  # BART propensity score
  X1 <- cbind(d$X, t = d$t)
  ate_bart <- function(Xtr) { f <- stochtree::bart(X_train = Xtr, y_train = d$y, num_gfr = 10, num_burnin = 200, num_mcmc = 1000,
      general_params = list(random_seed = seed))
    p1 <- predict(f, X = transform(Xtr, t = 1))$y_hat; p0 <- predict(f, X = transform(Xtr, t = 0))$y_hat; colMeans(p1 - p0) }
  b <- stochtree::bcf(X_train = d$X, Z_train = d$t, y_train = d$y, propensity_train = ps, num_gfr = 10, num_burnin = 200, num_mcmc = 1000,
                      general_params = list(random_seed = seed))
  list(BART = ate_bart(X1), `BART with propensity` = ate_bart(cbind(X1, ps = ps)), BCF = colMeans(b$tau_hat_train)) }
if (file.exists(ric_path)) ric <- readRDS(ric_path) else {
  set.seed(15); ric <- map(1:30, function(r) fit_ric(sim_ric(), r)); saveRDS(ric, ric_path) }
map_dfr(ric, function(r) imap_dfr(r, ~ tibble(method = .y, estimate = mean(.x), lo = quantile(.x, 0.05), hi = quantile(.x, 0.95)))) |>
  summarise(mean_estimate = mean(estimate), bias = mean(estimate - 3), mcse_bias = sd(estimate) / sqrt(n()),
            rmse = sqrt(mean((estimate - 3)^2)), coverage_90 = mean(lo < 3 & hi > 3), interval_width = mean(hi - lo), .by = method) |>
  mutate(across(where(is.double), ~ round(.x, 3))) |> as.data.frame()
output
                method mean_estimate   bias mcse_bias  rmse coverage_90
1                 BART         3.096  0.096     0.030 0.186       0.933
2 BART with propensity         2.902 -0.098     0.033 0.201       0.933
3                  BCF         2.931 -0.069     0.035 0.200       0.867
  interval_width
1          0.649
2          0.752
3          0.721

All three methods recover the effect of 3 to within about 0.1 on average, with root mean squared errors of about 0.2. Plain BART overestimates it by 0.096 (Monte Carlo standard error 0.030): a small bias, but in the direction regularisation-induced confounding predicts, since part of the confounding has been credited to treatment. Adding the BART-estimated propensity score reverses the sign of the bias (−0.098), and BCF's bias is the smallest in size (−0.069), though with 30 data sets the three are not clearly distinguishable, and neither are their coverages (93%, 93% and 87%, each with a Monte Carlo standard error of about 5 points). With 250 units and a modern BART sampler, the prognostic surface in this design is learned well enough that the problem is mild. The mechanism bites hardest when the prognostic surface is hard to learn relative to the sample size and selection is strongly targeted on it; there, separating μ from τ and giving the model the propensity score matter more. The safe default is to do both, since they cost little when they are not needed.

15.8 Bayesian causal forests and the mindset study

A growth mindset is the belief that intellectual abilities can be developed. The National Study of Learning Mindsets randomised about 12,000 ninth-graders in a national probability sample of 65 US public high schools to a short online growth-mindset intervention or a control programme, and measured their grades at the end of the year. Its main questions were about heterogeneity: for whom, and in which schools, does such a cheap intervention work? To let methodologists study those questions without releasing the study's confidential data, its investigators created a synthetic data set with the same structure and similar effect sizes, about 10,000 students in 76 schools, used at the 2018 Atlantic Causal Inference Conference (Carvalho et al., 2019). Unlike the real study, the synthetic data mimic an observational study: treatment depends on the covariates. The data may be used only to study methods for treatment effects.

R
f <- "data/nlsm/synthetic_data.csv"
if (!file.exists(f)) { dir.create("data/nlsm", showWarnings = FALSE)
  download.file("https://raw.githubusercontent.com/grf-labs/grf/master/experiments/acic18/synthetic_data.csv", f) }
ms <- read_csv(f, show_col_types = FALSE)
# S3 expected success; C1 race; C2 gender; C3 first-generation; XC urbanicity; X1 school mindset norms;
# X2 school achievement; X3 minority share; X4 poverty; X5 school size
X <- ms |> transmute(S3, C1 = factor(C1), C2 = factor(C2), C3, XC = factor(XC), X1, X2, X3, X4, X5)
Xm <- as.data.frame(model.matrix(~ . - 1, X))
ps_ms <- glm(ms$Z ~ ., data = Xm, family = binomial)$fitted.values
c(students = nrow(ms), schools = n_distinct(ms$schoolid), treated_share = mean(ms$Z),
  naive_difference = mean(ms$Y[ms$Z == 1]) - mean(ms$Y[ms$Z == 0]), propensity_range = range(ps_ms)) |> round(3)
bcf_path <- "fits/ch15_bcf_mindset.rds"
if (file.exists(bcf_path)) tau_ms <- readRDS(bcf_path) else {
  fit_ms <- stochtree::bcf(X_train = Xm, Z_train = ms$Z, y_train = ms$Y, propensity_train = ps_ms,
                           num_gfr = 10, num_burnin = 500, num_mcmc = 1000, general_params = list(random_seed = 15))
  tau_ms <- fit_ms$tau_hat_train; saveRDS(tau_ms, bcf_path) }
ate_ms <- colMeans(tau_ms)
c(ATE = mean(ate_ms), q05 = unname(quantile(ate_ms, 0.05)), q95 = unname(quantile(ate_ms, 0.95))) |> round(3)
output
         students           schools     treated_share  naive_difference 
        10391.000            76.000             0.326             0.304 
propensity_range1 propensity_range2 
            0.132             0.534 
  ATE   q05   q95 
0.249 0.231 0.266 

The synthetic study has 10,391 students in 76 schools, a third of them treated. Because treatment depends on the covariates, the naive difference in mean outcomes, 0.304 standard deviations, is biased; the estimated propensity scores range from 0.13 to 0.53, so every kind of student has a reasonable chance of either arm, and overlap is good. The Bayesian causal forest estimates the average effect at 0.249 (90% interval 0.231 to 0.266). The forest also gives a posterior for the effect of every student, and averaging those within schools gives a posterior for each school's effect.

R
school <- ms |> mutate(row = row_number()) |> group_by(schoolid) |>
  summarise(X1 = first(X1), X2 = first(X2), rows = list(row), .groups = "drop") |>
  mutate(draws = map(rows, ~ colMeans(tau_ms[.x, , drop = FALSE])),
         mean = map_dbl(draws, mean), lo = map_dbl(draws, ~ quantile(.x, 0.05)), hi = map_dbl(draws, ~ quantile(.x, 0.95)))
school |> select(X1, X2, mean, lo, hi) |> pivot_longer(c(X1, X2), names_to = "moderator", values_to = "value") |>
  mutate(moderator = recode(moderator, X1 = "school fixed-mindset norms (X1)", X2 = "school achievement level (X2)")) |>
  mutate(moderator = factor(moderator, c("school achievement level (X2)", "school fixed-mindset norms (X1)"))) |>
  ggplot(aes(value, mean)) + geom_linerange(aes(ymin = lo, ymax = hi), colour = harbour[["muted"]], alpha = 0.35) +
  geom_point(colour = harbour[["sky"]], size = 1.3) + geom_smooth(method = "loess", se = FALSE, colour = harbour[["rose"]], linewidth = 0.8, formula = y ~ x) +
  facet_wrap(~ moderator, scales = "free_x") + labs(x = "school characteristic (standardised)", y = "average effect in school")
Two scatter plots of 76 school effects, between about 0.1 and 0.37, with faint vertical inTwo scatter plots of 76 school effects, between about 0.1 and 0.37, with faint vertical intervals. Against school achievement there is no clear trend. Against fixed-mindset norms, effects are around 0.3 in schools below the average and fall to around 0.2 in schools above it. school achievement level (X2) school fixed-mindset norms (X1) -2 0 2 -3 -2 -1 0 1 2 3 0.1 0.2 0.3 0.4 school characteristic (standardised) average effect in school
Posterior mean and 90% interval of the average treatment effect in each of the 76 schools, from the Bayesian causal forest, against the school's achievement level (left) and the school's mean fixed-mindset score (right). Each point is a school; the line is a smooth of the posterior means.
R
lowhigh <- function(v) { cut_v <- median(v); hi <- school$draws[v > cut_v]; lo <- school$draws[v <= cut_v]
  d <- Reduce(`+`, hi) / length(hi) - Reduce(`+`, lo) / length(lo); c(mean = mean(d), q05 = unname(quantile(d, 0.05)), q95 = unname(quantile(d, 0.95)), p_positive = mean(d > 0)) }
rbind(`high minus low achievement (X2)` = lowhigh(school$X2), `high minus low fixed mindset (X1)` = lowhigh(school$X1)) |> round(3)
output
                                    mean    q05    q95 p_positive
high minus low achievement (X2)    0.023 -0.005  0.056      0.909
high minus low fixed mindset (X1) -0.072 -0.105 -0.033      0.000

The schools' effects range from about 0.1 to 0.37 standard deviations, and the intervals overlap a good deal. Comparing schools above and below the median of each moderator, draw by draw, gives the posterior of the difference in average effects, which is what a claim of moderation needs. Schools with stronger fixed-mindset norms have smaller effects, by 0.072 (90% interval 0.033 to 0.105), with posterior probability essentially 1 that the difference is negative. Schools with higher achievement have slightly larger effects, by 0.023, but the interval (−0.005 to 0.056) includes zero and the probability of a positive difference is 0.91: suggestive, not established. A median split is a blunt summary; the figure shows the whole relationship, and the forest's prior, which favours homogeneous effects, means that the variation it does report has survived shrinkage.

Paper in Focus: Where a Growth Mindset Improves Achievement

The paper. David Yeager and 24 co-authors, "A national experiment reveals where a growth mindset improves achievement", Nature 573, 364–369, 2019. A pre-registered randomised trial of a less-than-one-hour online intervention in a national probability sample of US high schools, analysed by an independent team.

The claim. Among lower-achieving students, the intervention raised ninth-grade grade point averages by about 0.10 grade points (95% interval 0.04 to 0.16) and increased enrolment in advanced mathematics courses. The effects were heterogeneous: larger in schools with low and middle achievement levels than in the highest-achieving ones, and larger where peer norms supported taking on challenges. The heterogeneity analyses used a Bayesian causal forest, described in the paper as a conservative Bayesian machine-learning method, which is the main reason this paper belongs in this chapter.

Reading it. Look for what was pre-registered (the primary outcome, the subgroup of lower-achieving students, the hypothesised moderators) and what was exploratory, and note that heterogeneity was estimated with a method whose prior favours homogeneous effects, so that the reported moderation had to overcome shrinkage. Then ask the questions of Section A.4: how large is the effect in units that matter to a school (a tenth of a grade point), how much would it cost to deliver at scale, and would it replicate in other countries? Our analysis used the synthetic version of the data, which can teach us about the method but not about mindsets.

Key Takeaways: Lecture 36

  • Gaussian processes and BART are priors on response surfaces; conditional effects are differences of the surface, and average effects are their averages.
  • Where the data do not overlap, flexible priors extrapolate according to their own structure: BART flat and overconfident, Gaussian processes back toward the prior.
  • Regularisation-induced confounding: shrinkage can attribute confounding to treatment; including the propensity score and separating μ from τ, as Bayesian causal forests do, guards against it. In our simulation plain BART's bias was small, about 0.1 on an effect of 3, but in the direction the theory predicts.
  • Bayesian causal forests estimate heterogeneous effects with a prior that favours homogeneity, so that reported moderation has to earn its place.

Lecture 37 — Nonparametric is not assumption-free

15.9 A competition with known answers

How well do flexible Bayesian methods recover causal effects when nobody knows the right model? The 2016 Atlantic Causal Inference Conference ran a competition to find out (Dorie et al., 2019). The organisers took the covariates of a real study, 4,802 infants and 58 variables from the Collaborative Perinatal Project, and simulated treatment assignments and outcomes from 77 different data-generating processes that varied the degree of nonlinearity, the overlap between groups, the alignment between the assignment mechanism and the outcome, and the heterogeneity of effects, 100 data sets each. Thirty teams submitted methods, and because the truth was known, they could be scored. The simulation code is public as the R package aciccomp2016.

We repeat a small version of the competition for BART, the method that did best among the simplest submissions: ten of the 77 settings, one data set each, estimating the conditional average effect on the treated (the mean of μ(x,1)−μ(x,0) over the treated units) by g-computation, with and without an estimated propensity score as an extra covariate.

R
# remotes::install_github("vdorie/aciccomp/2016")    # the competition's simulation package
library(aciccomp2016)
acic_path <- "fits/ch15_acic.rds"
settings <- c(1, 9, 17, 23, 31, 39, 47, 55, 63, 71)
if (file.exists(acic_path)) acic <- readRDS(acic_path) else {
  acic <- map_dfr(settings, function(s) {
    d <- dgp_2016(input_2016, s, 1); X <- model.matrix(~ . - 1, input_2016); tr <- d$z == 1
    truth <- mean(d$mu.1[tr] - d$mu.0[tr])
    ps <- colMeans(pnorm(bart(X, d$z, ndpost = 500, nskip = 250, verbose = FALSE)$yhat.train))     # BART propensity score
    est <- function(Xa) { f <- bart(cbind(Xa, z = d$z), d$y, x.test = cbind(Xa, z = 1 - d$z)[tr, ], ndpost = 500, nskip = 250, verbose = FALSE)
      att <- rowMeans(f$yhat.train[, tr] - f$yhat.test); c(est = mean(att), lo = unname(quantile(att, 0.025)), hi = unname(quantile(att, 0.975))) }
    bind_rows(tibble(setting = s, method = "BART", truth = truth, !!!est(X)),
              tibble(setting = s, method = "BART with propensity", truth = truth, !!!est(cbind(X, ps = ps))),
              tibble(setting = s, method = "difference in means", truth = truth, est = mean(d$y[tr]) - mean(d$y[!tr]), lo = NA, hi = NA)) })
  saveRDS(acic, acic_path) }
acic |> summarise(mean_abs_error = mean(abs(est - truth)), rmse = sqrt(mean((est - truth)^2)),
                  coverage_95 = mean(lo < truth & hi > truth), mean_width = mean(hi - lo), .by = method) |>
  mutate(across(where(is.double), ~ round(.x, 3)))
output
# A tibble: 3 x 5
  method               mean_abs_error  rmse coverage_95 mean_width
  <chr>                         <dbl> <dbl>       <dbl>      <dbl>
1 BART                          0.043 0.052         0.9      0.17 
2 BART with propensity          0.028 0.036         1        0.179
3 difference in means           0.886 1.06         NA       NA    

Both BART analyses are far better than the difference in means, whose error averages 0.89 because treatment in these simulations is strongly confounded. Plain BART misses the true effect by 0.043 on average, and 9 of its 10 intervals contain it; adding the propensity score cuts the average error to 0.028 and all 10 intervals cover, with intervals only slightly wider (0.179 against 0.170). Ten data sets are a small competition, and one miss in ten is consistent with 95% coverage, but the direction of the comparison is the one Dorie and colleagues found across 7,700 data sets: a flexible outcome model does most of the work, and giving it the propensity score makes its estimates and intervals more trustworthy.

Paper in Focus: A Causal Inference Competition

The paper. Vincent Dorie, Jennifer Hill, Uri Shalit, Marc Scott and Dan Cervone, "Automated versus do-it-yourself methods for causal inference: lessons learned from a data analysis competition", Statistical Science 34(1), 2019.

The claim. Across 7,700 simulated data sets, the most consistent finding was that methods which model the response surface flexibly do better than those that do not, and that BART-based methods were among the best. Combining a flexible outcome model with a model for the assignment mechanism helped further. Interval coverage was the weak point: many methods' nominal 95% intervals covered far less often, and plain BART averaged about 82%. Adding an estimated propensity score as a covariate brought BART's coverage to a little over 90% without widening its intervals, an empirical counterpart of the regularisation-induced confounding of Section 15.7. The settings in which methods struggled most were those where the assignment mechanism and the response surface were aligned and the surface was nonlinear.

Reading it. This is a methods paper, so the questions differ from those for a substantive study. Who chose the data-generating processes, and could a method be tuned to them? (The organisers kept the settings secret until submission.) Are the covariates realistic? (They come from a real study.) What is the estimand, and is it the one practitioners need? Which results would change with a different set of settings? Benchmarks with known answers are the only way to measure coverage; they are also only as good as their simulations.

15.10 Overlap, extrapolation and coverage

Lecture 34 drew a distinction between calibration averaged over a prior and calibration for every fixed truth. Nonparametric Bayesian methods are attractive because their priors are wide enough to contain almost any truth, so one might hope for both. Benchmarks such as ACIC show that this does not come free. A prior on functions is wide, but not uniform: BART prefers sums of shallow trees, Gaussian processes prefer smooth functions of a certain length-scale, and BCF prefers homogeneous effects. Where the data are plentiful the prior's preferences hardly matter; where they are thin, which in causal inference means where treated and control units do not overlap, the preferences become the answer, and posterior intervals inherit their confidence.

Three practices follow. First, examine overlap before modelling, by plotting propensity scores, and restrict the estimand to the region of overlap (the effect on the treated, or on a trimmed population) when overlap fails. Second, include the propensity score in the outcome model, or use BCF, which does so by design. Third, check coverage by simulation in settings that resemble your data, by generating outcomes from a model fitted to your data with known effects and refitting. It is the calibrated-Bayes recipe of Lecture 34, and it is the only way to know how much the posterior interval can be trusted.

15.11 Nonparametric Bayes in practice

Research Practice: Reporting a Nonparametric Bayesian Analysis

  • Name the prior on functions or distributions and its key settings (number of trees, kernel, concentration parameter), and show that results are stable when they change.
  • State the identifying assumptions separately from the model: ignorability, overlap and consistency are not provided by flexibility.
  • Show overlap (propensity score distributions) and report where the effect is estimated by interpolation and where by extrapolation.
  • For heterogeneous effects, distinguish pre-specified moderators from exploratory findings, and report the posterior for the difference in effects between subgroups, not two separate intervals.
  • Report a coverage check by simulation from a model close to the data, as the ACIC benchmark does.

Project Connections: Flexible Models in the Five Projects

For NYC EMS response equity, Dirichlet-process mixtures describe the full distribution of response times by borough, and the Bayesian bootstrap gives posteriors for the tail measures (the 90th percentile, the share over ten minutes) that equity questions are about. For AI tools in university mathematics and the education projects of the Afterword, Bayesian causal forests are the natural tool for asking which students benefit from an intervention, with the mindset study as the model of how to pre-register and report heterogeneity. For the cancer gene-expression project, BART and Gaussian processes give flexible dose-response or prognosis surfaces over many genes. For AI-generated text detection, nonparametric priors on the distribution of detector scores give calibrated probabilities without assuming a parametric shape for each source.

History: From Ferguson to Causal Forests

Thomas Ferguson defined the Dirichlet process in 1973, answering a question that had made nonparametric Bayesian inference look impossible: how to put a tractable prior on the space of all distributions. Charles Antoniak (1974) introduced mixtures of Dirichlet processes, David Blackwell and James MacQueen (1973) the urn scheme, and Jayaram Sethuraman (1994) the stick-breaking construction. The models became practical only with Markov chain Monte Carlo: Michael Escobar and Mike West (1995) gave the Gibbs sampler for Dirichlet-process mixtures that is still the basis of most software. Donald Rubin's Bayesian bootstrap (1981) came from asking what Efron's bootstrap would mean to a Bayesian. On the regression side, Hugh Chipman, Edward George and Robert McCulloch developed Bayesian regression trees in 1998 and BART in 2010; Jennifer Hill's 2011 paper made BART a standard tool for causal inference, and Richard Hahn, Jared Murray and Carlos Carvalho's Bayesian causal forests (2020) adapted it to heterogeneous effects.

Check Yourself

A BART analysis of an observational study reports a 95% interval for the average effect on the treated that is narrow and excludes zero. The propensity scores show that 20% of the treated units have no comparable controls. What is the most important next step?

Key Takeaways: Lecture 37

  • Benchmarks with known answers, such as the ACIC 2016 competition, show that flexible response-surface models estimate average effects well, but that their intervals often cover less often than they claim; adding the propensity score helps (in our ten settings, error fell from 0.043 to 0.028).
  • Where groups do not overlap, a nonparametric posterior is an extrapolation by its prior; examine overlap and restrict the estimand when needed.
  • Including the propensity score in the outcome model improves both bias and coverage.
  • Coverage should be checked by simulation from a model close to the data: calibrated Bayes, applied to nonparametric models.

15.12 Exercises

Each exercise has a hint and an answer button. Code for the answers is in R/ex15.R.

Exercises

  1. How many tables?. In the Chinese restaurant process, the ith customer opens a new table with probability α/(α+i−1). Find the expected number of occupied tables after 1,000 customers when α=1 and when α=10, and check by simulation. How does the number grow with the number of customers?

    The expected number of tables is the sum of the probabilities of opening a new one.

    The expected number of tables is ∑i=1nα/(α+i−1). For n=1,000 it is 7.49 when α=1 and 46.65 when α=10; 5,000 simulated restaurants give 7.44 and 46.55. The sum grows like αlog⁡(1+n/α) (6.91 and 46.15 here): with α=1 it is 5.2 tables for 100 customers, 7.5 for 1,000, 9.8 for 10,000 and 12.1 for 100,000, about 2.3 more for every tenfold increase. The number of groups never stops growing, but very slowly, which is why a Dirichlet-process mixture keeps adding small components as the data grow (Miller and Harrison's warning in Section 15.4).

  2. Two bootstraps. For the Queens calls of Section 15.3, compare the Bayesian bootstrap posterior of the 90th percentile of response time with Efron's bootstrap distribution (resampling calls with replacement). How different are they?

    Replace the exponential weights by multinomial counts: tabulate(sample(n, n, replace = TRUE), n).

    For the 5,528 Queens calls the two are almost identical: the Bayesian bootstrap gives a mean of 10.14 minutes with standard deviation 0.103 and 90% interval 9.98 to 10.32; Efron's bootstrap gives 10.15, 0.104 and the same interval. With thousands of observations, continuous Dirichlet weights and integer multinomial counts produce nearly the same distribution of weighted quantiles; Rubin (1981) showed that the two are close in general, and the Bayesian version has the advantage of a posterior interpretation. They differ noticeably only in small samples, where the Bayesian bootstrap's smooth weights give slightly smoother distributions.

  3. One extreme earner. Add one hypothetical trainee who earned $500,000 in 1978 to the job-training experiment, and recompute the Bayesian-bootstrap posterior of the average effect. What happens, and what does it reveal about the Bayesian bootstrap's assumptions?

    Append a row with treat = 1, re78 = 500 and rerun the chunk.

    With one trainee earning $500,000 among 186, the posterior mean of the effect jumps from $1,800 to $4,410 and its standard deviation from $660 to $2,610; the 90% interval runs from $1,530 to $9,740, and the posterior is strongly skewed (median $3,730). Each posterior draw gives the extreme earner a random weight, and the effect estimate moves almost one-for-one with it, so the posterior's spread mostly reflects how much weight one person receives. The Bayesian bootstrap's assumption is that the population consists exactly of the observed values, each with unknown frequency: it has no notion that $500,000 is implausible for a participant in a job-training programme, and no way to shrink it. A parametric model with a sensible tail, a robust estimand (a median or a trimmed mean), or a check of the data would each handle the case better; nonparametric does not mean robust.

  4. First-generation students. Using the Bayesian causal forest fitted to the mindset data, compute the posterior of the difference between the average effect for first-generation students (C3 = 1) and for the others. Is there evidence of heterogeneity by first-generation status?

    Average the columns of tau_ms over each group of rows, then take the difference draw by draw.

    First-generation students make up 63% of the sample. Their posterior average effect is 0.240 against 0.264 for the others, a difference of −0.025 (90% interval −0.042 to −0.007), with posterior probability 0.975 that it is negative. There is evidence of a small difference in the synthetic data: the intervention helps first-generation students slightly less. The difference is about a tenth of the average effect, and because the synthetic data were generated by the study's investigators to resemble the real study, the result says that the forest can find heterogeneity of this size, not that the real intervention behaves this way.

  5. Width and error. For the ten ACIC settings of Section 15.9, plot the absolute error of the BART estimate against the width of its 95% interval, with and without the propensity score. Are wider intervals more honest?

    Use the saved object acic.

    Across the ten settings, the absolute errors range from 0.01 to 0.11 and the interval widths from 0.13 to 0.31. Wider intervals do go with larger errors, more clearly with the propensity score (correlation 0.54) than without it (0.22): the largest error for both methods is in setting 39, and the propensity version widens its interval there to 0.31. The ratio of the error to the posterior standard deviation (width divided by 3.92) averages 0.99 for plain BART, where about 0.8 is expected if the intervals are calibrated, and 0.60 with the propensity score. So plain BART's intervals are, if anything, slightly too narrow, its one miss (setting 17) being an interval that excluded the truth by a small margin, while the propensity version's are honest or a little conservative. Ten settings are too few for firm conclusions; Dorie and colleagues' 7,700 data sets are the evidence.

Further reading and references

Further Reading

  • Müller, Quintana, Jara and Hanson, Bayesian Nonparametric Data Analysis (2015): an applied introduction, with R code.
  • Ghosal and van der Vaart, Fundamentals of Nonparametric Bayesian Inference (2017): the theory, including when posteriors concentrate on the truth.
  • Hill, Linero and Murray, "Bayesian additive regression trees: a review and look forward" (2020): BART and its causal applications.
  • Oganisian and Roy (2021), the Paper in Focus of Lecture 35: a hands-on tutorial with code.
  1. Antoniak, C. E. (1974). Mixtures of Dirichlet processes with applications to Bayesian nonparametric problems. Annals of Statistics, 2(6), 1152–1174.
  2. Blackwell, D. and MacQueen, J. B. (1973). Ferguson distributions via Pólya urn schemes. Annals of Statistics, 1(2), 353–355.
  3. Carvalho, C., Feller, A., Murray, J., Woody, S. and Yeager, D. (2019). Assessing treatment effect variation in observational studies: results from a data challenge. Observational Studies, 5(2), 21–35.
  4. Chipman, H. A., George, E. I. and McCulloch, R. E. (2010). BART: Bayesian additive regression trees. Annals of Applied Statistics, 4(1), 266–298.
  5. Dorie, V., Hill, J., Shalit, U., Scott, M. and Cervone, D. (2019). Automated versus do-it-yourself methods for causal inference: lessons learned from a data analysis competition. Statistical Science, 34(1), 43–68.
  6. Escobar, M. D. and West, M. (1995). Bayesian density estimation and inference using mixtures. Journal of the American Statistical Association, 90(430), 577–588.
  7. Ferguson, T. S. (1973). A Bayesian analysis of some nonparametric problems. Annals of Statistics, 1(2), 209–230.
  8. Fong, E., Holmes, C. and Walker, S. G. (2023). Martingale posterior distributions. Journal of the Royal Statistical Society: Series B, 85(5), 1357–1391.
  9. Ghosal, S. and van der Vaart, A. (2017). Fundamentals of Nonparametric Bayesian Inference. Cambridge University Press.
  10. Hahn, P. R., Murray, J. S. and Carvalho, C. M. (2020). Bayesian regression tree models for causal inference: regularization, confounding, and heterogeneous effects. Bayesian Analysis, 15(3), 965–1056.
  11. Hill, J. L. (2011). Bayesian nonparametric modeling for causal inference. Journal of Computational and Graphical Statistics, 20(1), 217–240.
  12. Hill, J., Linero, A. and Murray, J. (2020). Bayesian additive regression trees: a review and look forward. Annual Review of Statistics and Its Application, 7, 251–278.
  13. Miller, J. W. and Harrison, M. T. (2013). A simple example of Dirichlet process mixture inconsistency for the number of components. Advances in Neural Information Processing Systems, 26.
  14. Miller, J. W. and Harrison, M. T. (2018). Mixture models with a prior on the number of components. Journal of the American Statistical Association, 113(521), 340–356.
  15. Müller, P., Quintana, F. A., Jara, A. and Hanson, T. (2015). Bayesian Nonparametric Data Analysis. Springer.
  16. Oganisian, A. and Roy, J. A. (2021). A practical introduction to Bayesian estimation of causal effects: parametric and nonparametric approaches. Statistics in Medicine, 40(2), 518–551.
  17. Rubin, D. B. (1981). The Bayesian bootstrap. Annals of Statistics, 9(1), 130–134.
  18. Sethuraman, J. (1994). A constructive definition of Dirichlet priors. Statistica Sinica, 4(2), 639–650.
  19. Yeager, D. S., Hanselman, P., Walton, G. M., et al. (2019). A national experiment reveals where a growth mindset improves achievement. Nature, 573, 364–369.