library(qte)
library(ggplot2)
set.seed(42)
data(lalonde)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)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 0autoplot(res_exp, ylab = "QTE (earnings, 1978)")The confidence band indicates that the QTE is positive across much of the distribution, with the effect somewhat larger in the lower quantiles.
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 0autoplot(res_psid, ylab = "QTE (earnings, 1978)")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 0autoplot(res_qtt, ylab = "QTT (earnings, 1978)")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.