Quantile Treatment Effects in R

What are Quantile Treatment Effects?

The Average Treatment Effect (ATE) or Average Treatment Effect on the Treated (ATT) summarizes the causal effect of a treatment by a single number: the mean difference in outcomes between treated and untreated units. This is often a natural quantity, but it can miss important heterogeneity. If a job training program substantially raises earnings at the bottom of the distribution while having little effect at the top, the ATE obscures this. If a minimum wage increase compresses the wage distribution, an average effect conceals the compression.

The Quantile Treatment Effect at level τ is QTE(τ) = QY(1)(τ) − QY(0)(τ), where QY(d)(τ) is the τ-th quantile of the potential outcome distribution under treatment status d. Mapping QTE(τ) across τ ∈ (0, 1) traces how the treatment shifts the entire outcome distribution. The Quantile Treatment Effect on the Treated (QTT) conditions on the treated group.

This vignette demonstrates unc_qte(), the qte package’s estimator for cross-sectional settings (no panel data required). For DiD-based estimators and staggered treatment adoption, see vignette("panel-estimators") and vignette("staggered-adoption").

library(qte)
library(ggplot2)
set.seed(42)
data(lalonde)

Random assignment

Under random assignment, no covariate adjustment is needed: the unconditional quantiles of the treated and control outcome distributions identify the QTE directly. We use the experimental Lalonde data (lalonde.exp), where treatment was randomly assigned.

res_exp <- unc_qte(
  yname  = "re78",
  dname  = "treat",
  data   = lalonde.exp,
  target = "qte",
  probs  = seq(0.1, 0.9, 0.1),
  biters = 50
)
summary(res_exp)
#> 
#> Overall ATE:  
#>       ATE    Std. Error     [ 95%  Conf. Int.]  
#>  1794.343      655.1211   510.3294    3078.357 *
#> 
#> 
#> QTE:
#>  Tau      QTE Std. Error [ 95% Simult.  Conf. Band]  
#>  0.1    0.000     0.0000         0.0000      0.0000  
#>  0.2    0.000   127.5450      -249.9836    249.9836  
#>  0.3  929.884   361.5139       221.3297   1638.4382 *
#>  0.4 1176.944   955.7239      -696.2409   3050.1281  
#>  0.5 1091.468   966.2678      -802.3823   2985.3180  
#>  0.6 1466.690   956.3346      -407.6908   3341.0718  
#>  0.7 1810.696  1047.0280      -241.4415   3862.8329  
#>  0.8 2300.454  1161.7136        23.5371   4577.3705 *
#>  0.9 2856.068  1811.5137      -694.4332   6406.5699  
#> ---
#> Signif. codes: `*' confidence band does not cover 0
autoplot(res_exp, ylab = "QTE (earnings, 1978)")

QTE curve under random assignment

The confidence band indicates that the QTE is positive across much of the distribution, with the effect somewhat larger in the lower quantiles.

Unconfoundedness

When treatment is not randomly assigned, the unconfoundedness assumption (conditional independence of potential outcomes given covariates) provides identification. We use the observational Lalonde data (lalonde.psid), which combines the treated group from the experiment with a comparison group from the PSID.

unc_qte() supports three estimation methods:

  • "ipw" — inverse probability weighting (propensity score re-weighting)
  • "or" — outcome regression (conditional quantile regression)
  • "aipw" — augmented IPW (doubly robust; recommended)
xf <- ~ age + I(age^2) + education + black + hispanic + married + nodegree

res_psid <- unc_qte(
  yname      = "re78",
  dname      = "treat",
  data       = lalonde.psid,
  xformla    = xf,
  est_method = "aipw",
  target     = "qte",
  probs      = seq(0.1, 0.9, 0.1),
  biters     = 100
)
summary(res_psid)
#> 
#> Overall ATE:  
#>        ATE    Std. Error     [ 95%  Conf. Int.]  
#>  -13194.78      1560.465  -16253.24   -10136.33 *
#> 
#> 
#> QTE:
#>  Tau        QTE Std. Error [ 95% Simult.  Conf. Band]  
#>  0.1      0.000   588.8148      -1154.056    1154.056  
#>  0.2  -6404.679  1584.0092      -9509.280   -3300.078 *
#>  0.3  -8523.773  1953.6323     -12352.822   -4694.724 *
#>  0.4 -11114.084  2294.6286     -15611.474   -6616.695 *
#>  0.5 -12684.516  2724.8925     -18025.207   -7343.824 *
#>  0.6 -13532.837  2583.5902     -18596.581   -8469.093 *
#>  0.7 -14181.882  1777.4537     -17665.627  -10698.137 *
#>  0.8 -18285.313  1578.3310     -21378.785  -15191.841 *
#>  0.9 -22580.785  3263.6655     -28977.452  -16184.118 *
#> ---
#> Signif. codes: `*' confidence band does not cover 0
autoplot(res_psid, ylab = "QTE (earnings, 1978)")

QTE curve under unconfoundedness

QTT under unconfoundedness

Setting target = "qtt" estimates the Quantile Treatment Effect on the Treated — the distributional effect for the subpopulation that actually received treatment.

res_qtt <- unc_qte(
  yname      = "re78",
  dname      = "treat",
  data       = lalonde.psid,
  xformla    = xf,
  est_method = "aipw",
  target     = "qtt",
  probs      = seq(0.1, 0.9, 0.1),
  biters     = 100
)
summary(res_qtt)
#> 
#> Overall ATT:  
#>        ATT    Std. Error     [ 95%  Conf. Int.]  
#>  -4685.583       891.021  -6431.952   -2939.214 *
#> 
#> 
#> QTT:
#>  Tau         QTT Std. Error [ 95% Simult.  Conf. Band]  
#>  0.1      0.0001   109.2934       -214.211    214.2112  
#>  0.2  -1002.7420   849.0036      -2666.759    661.2746  
#>  0.3  -3400.4907  1676.6172      -6686.600   -114.3814 *
#>  0.4  -5009.2491  1294.4744      -7546.372  -2472.1259 *
#>  0.5  -4602.6065  1059.1682      -6678.538  -2526.6751 *
#>  0.6  -5229.1454  1358.6184      -7891.989  -2566.3023 *
#>  0.7  -5506.6015  1633.4905      -8708.184  -2305.0188 *
#>  0.8  -6885.7233  1652.8669     -10125.283  -3646.1636 *
#>  0.9 -10517.0625  2309.0005     -15042.620  -5991.5047 *
#> ---
#> Signif. codes: `*' confidence band does not cover 0
autoplot(res_qtt, ylab = "QTT (earnings, 1978)")

QTT curve under unconfoundedness

Reading the output

summary() prints the overall ATT, the QTE/QTT at each quantile, and standard errors with confidence intervals. autoplot() returns a ggplot object so you can add layers:

autoplot(res_qtt) +
  ggplot2::labs(title = "QTT — Lalonde (observational)",
                subtitle = "AIPW with pre-treatment covariates")

By default autoplot() shows a uniform confidence band (cband = TRUE), which provides simultaneous coverage over all quantile levels — a stronger guarantee than pointwise intervals. Pass cband = FALSE for pointwise intervals instead.

Standard errors are computed via the empirical bootstrap. The biters argument controls the number of iterations (default 100). Parallel computation is available via the cl argument.

References