



| DGP ID | synth_qte1 |
| Version | 1.6.0 |
| Status | experimental |
| Difficulty | ★★★★☆ (4/5) |
| Stress Profile | overlap: moderatenoise: heavylinearity: lineareffect: heterogeneoustarget: both |
This DGP is conceptually hard rather than numerically hard: the treatment effect is sign-switching (+1 or -1) across the population. A mean-ATT estimator can look excellent (low bias/RMSE for the ATT) while completely missing that roughly half the population is harmed. The oracle QST curve is the “X-ray” that reveals the sign flip.
Covariates: [ X ^5,X_j (0, 1^2) j = 1,,5 ]
Outcome (control): [ _0(X) = 1 + X_1 + 0.5 X_2 ]
Propensity: [ (p(X)) = 0.5 X_1 - 0.5 X_2 ]
Discontinuous effect: [ (X) = \[\begin{cases} +1, & X_1 > 0 \\ -1, & X_1 \le 0 \end{cases}\]]
Noise: [ = 0.5,U,U t_4 ]
structural_te among treated units.cs_get_oracle_qst()).



OLS (lm_att) passes the numerical benchmark (low ATT bias/RMSE), but fails the scientific benchmark: it reports only a mean ATT and provides no distributional view. The oracle QST curve shows that the treatment is positive in one half of the population and negative in the other.
| estimator_id | mean_bias | rmse |
|---|---|---|
| lm_att | -0.008 | 0.056 |
| oracle_att | 0.000 | 0.000 |
R/dgp-synth-qte1.Rcs_dgp_registry() entry for synth_qte1cs_get_oracle_qst("synth_qte1", version = "1.6.0")Source code for: dgp_synth_qte1_v160
function (n, seed = NULL, include_truth = TRUE, oracle_only = FALSE)
{
if (!is.null(seed)) {
cs_set_rng(seed)
}
X1 <- stats::rnorm(n, mean = 0, sd = 1)
X2 <- stats::rnorm(n, mean = 0, sd = 1)
if (!isTRUE(oracle_only)) {
X3 <- stats::rnorm(n, mean = 0, sd = 1)
X4 <- stats::rnorm(n, mean = 0, sd = 1)
X5 <- stats::rnorm(n, mean = 0, sd = 1)
}
mu0 <- 1 + X1 + 0.5 * X2
tau <- ifelse(X1 > 0, 1, -1)
p <- stats::plogis(0.5 * X1 - 0.5 * X2)
w <- stats::rbinom(n, size = 1L, prob = p)
eps0 <- 0.5 * stats::rt(n, df = 4)
if (isTRUE(oracle_only)) {
eps1 <- eps0
}
else {
eps1 <- 0.5 * stats::rt(n, df = 4)
}
y0 <- mu0 + eps0
y1 <- mu0 + tau + eps1
if (isTRUE(oracle_only)) {
return(list(df = tibble::tibble(w = w, y0 = y0, y1 = y1)))
}
y <- ifelse(w == 1L, y1, y0)
true_att <- cs_true_att(structural_te = tau, w = w)
true_qst <- if (isTRUE(include_truth))
cs_get_oracle_qst("synth_qte1", version = "1.6.0")
else NULL
out <- list(df = tibble::tibble(y = y, w = w, y0 = y0, y1 = y1,
p = p, structural_te = tau, X1 = X1, X2 = X2, X3 = X3,
X4 = X4, X5 = X5), true_att = true_att, true_qst = true_qst,
meta = list(dgp_id = "synth_qte1", version = "1.6.0",
type = "synthetic", params = list(n = n, seed = seed),
structural_te = tau))
cs_check_dgp_synthetic(out)
out
}
<bytecode: 0x5592107cbf50>
<environment: namespace:CausalStress>