



| DGP ID | synth_placebo_kangschafer |
| Version | 1.4.0 |
| Status | experimental |
| Difficulty | ★★★★★ (5/5) |
| Stress Profile | overlap: moderatenoise: gaussianlinearity: smootheffect: constanttarget: both |
Intent: The “Blindfold Test” (Misspecification). Based on the classic Kang & Schafer (2007) design. The true selection mechanism relies on latent variables \(Z\), but the analyst only observes nonlinear transformations \(X\). This guarantees the propensity model (Linear in \(X\)) is misspecified.
The Scientific Question: “Does Double Robustness hold when the propensity model is dead wrong?” Since \(\hat{p}(X)\) is wrong, the estimator must rely on the outcome model (\(\hat{\mu}_0(X)\)) to save the day. This stresses the “Double Robust” property of modern estimators (AIPW, TMLE, DR-Learners).
Latent covariates: [ Z = (Z_1, Z_2, Z_3, Z_4)^_4(0, I_4) ]
Observed covariates (nonlinear transforms): [ X_1 = (Z_1/2),X_2 = + 10, ] [ X_3 = (Z_1 Z_3 / 25 + 0.6)^3,X_4 = (Z_2 + Z_4 + 20)^2 ]
Propensity in latent space: [ p(Z) = (-Z_1 + 0.5 Z_2 - 0.25 Z_3 - 0.1 Z_4) ]
Outcome in latent space: [ _0(Z) = 210 + 27.4 Z_1 + 13.7 Z_2 + 13.7 Z_3 + 13.7 Z_4 ]
Sharp null effect: [ (Z) ,Y_1 = Y_0 ]
Noise: [ (0, 1^2) ]




Oracle should be zero. IPW is expected to be noisy or biased under misspecification.
| estimator_id | mean_bias | rmse |
|---|---|---|
| ipw_att | -6.871 | 7.052 |
| oracle_att | 0.000 | 0.000 |
Interpretation: The oracle remains at zero as expected. IPW can show erratic behavior because the propensity model is slightly misspecified in the observed covariate space, producing unstable weights.
R/dgp-synth-placebo-kangschafer.Rcs_dgp_registry() entry for synth_placebo_kangschafercs_get_oracle_qst("synth_placebo_kangschafer", version = "1.4.0")Source code for: dgp_synth_placebo_kangschafer_v140
function (n, seed = NULL, include_truth = TRUE, oracle_only = FALSE)
{
if (!is.null(seed)) {
cs_set_rng(seed)
}
Z1 <- stats::rnorm(n, mean = 0, sd = 1)
Z2 <- stats::rnorm(n, mean = 0, sd = 1)
Z3 <- stats::rnorm(n, mean = 0, sd = 1)
Z4 <- stats::rnorm(n, mean = 0, sd = 1)
X1 <- exp(Z1/2)
X2 <- Z2/(1 + exp(Z1)) + 10
X3 <- (Z1 * Z3/25 + 0.6)^3
X4 <- (Z2 + Z4 + 20)^2
lin_ps <- -Z1 + 0.5 * Z2 - 0.25 * Z3 - 0.1 * Z4
p <- stats::plogis(lin_ps)
w <- stats::rbinom(n, size = 1L, prob = p)
mu0 <- 210 + 27.4 * Z1 + 13.7 * Z2 + 13.7 * Z3 + 13.7 * Z4
eps <- stats::rnorm(n, mean = 0, sd = 1)
y0 <- mu0 + eps
y1 <- y0
if (isTRUE(oracle_only)) {
return(list(df = tibble::tibble(w = w, y0 = y0, y1 = y1)))
}
y <- ifelse(w == 1L, y1, y0)
tau <- rep(0, n)
true_att <- cs_true_att(structural_te = tau, w = w)
true_qst <- if (isTRUE(include_truth))
cs_get_oracle_qst("synth_placebo_kangschafer", version = "1.4.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), true_att = true_att, true_qst = true_qst, meta = list(dgp_id = "synth_placebo_kangschafer",
version = "1.4.0", type = "synthetic", params = list(n = n,
seed = seed), structural_te = tau))
cs_check_dgp_synthetic(out)
out
}
<bytecode: 0x564437c8d368>
<environment: namespace:CausalStress>