|
| 1 | +# Regression tests for the OLE Simulation Workflow vignette. |
| 2 | +# Reproduces the exact simulation pipeline from set.seed(2023) and verifies |
| 3 | +# that the simulation_report_obj slot values remain unchanged. |
| 4 | +# |
| 5 | +# Reference: vignettes/OLE_simulation_workflow.Rmd |
| 6 | + |
| 7 | +# ---------- helper: generate vignette OLE simulation data ------------------- |
| 8 | +generate_OLE_sim_data <- function() { |
| 9 | + set.seed(2023) |
| 10 | + |
| 11 | + data_matrix_list <- list() |
| 12 | + ntrial <- 3 |
| 13 | + true_effect_long <- 0 |
| 14 | + |
| 15 | + for (trial_iter in 1:ntrial) { |
| 16 | + normal <- copula::normalCopula(param = c(0.8), dim = 4, dispstr = "ar1") |
| 17 | + |
| 18 | + X_int <- simulate_X_copula( |
| 19 | + n = 200, p = 4, cp = normal, |
| 20 | + margins = c("binom", "binom", "binom", "exp"), |
| 21 | + paramMargins = list( |
| 22 | + list(size = 1, prob = 0.7), list(size = 1, prob = 0.9), |
| 23 | + list(size = 1, prob = 0.3), list(rate = 1 / 10) |
| 24 | + ) |
| 25 | + ) |
| 26 | + X_int$x4 <- round(X_int$x4) + 1 |
| 27 | + X_int$x5 <- 30 + 10 * X_int$x1 + 7 * X_int$x2 - 6 * X_int$x3 - |
| 28 | + 0.5 * X_int$x4 + rnorm(200, 0, 10) |
| 29 | + |
| 30 | + X_ext <- simulate_X_copula( |
| 31 | + n = 100, p = 4, cp = normal, |
| 32 | + margins = c("binom", "binom", "binom", "exp"), |
| 33 | + paramMargins = list( |
| 34 | + list(size = 1, prob = 0.7), list(size = 1, prob = 0.9), |
| 35 | + list(size = 1, prob = 0.3), list(rate = 1 / 10) |
| 36 | + ) |
| 37 | + ) |
| 38 | + X_ext$x4 <- round(X_ext$x4) + 1 |
| 39 | + X_ext$x5 <- 50 + 10 * X_ext$x1 + 2 * X_ext$x2 - 1 * X_ext$x3 - |
| 40 | + 0.3 * X_ext$x4 + rnorm(100, 0, 10) |
| 41 | + |
| 42 | + varnames <- c("1", paste0("x", 1:5)) |
| 43 | + model_form_x_t1 <- setNames(c(10.0, 0.05, -1.5, -1.0, -0.2, -0.1), varnames) |
| 44 | + model_form_x_t2 <- setNames(c(6.0, 0.5, -0.5, -1.0, -0.3, -0.06), varnames) |
| 45 | + model_form_x_t3 <- setNames(c(5.0, 1.9, 1.4, -1.3, -0.4, -0.15), varnames) |
| 46 | + model_form_x_t4 <- setNames(c(1.2, 1.0, 2.0, -0.5, -0.4, -0.10), varnames) |
| 47 | + |
| 48 | + outcome_model_specs <- list( |
| 49 | + list(effect = 0, model_form_x = model_form_x_t1, |
| 50 | + noise_mean = 0, noise_sd = 4), |
| 51 | + list(effect = 0, model_form_x = model_form_x_t2, |
| 52 | + noise_mean = 0, noise_sd = 4), |
| 53 | + list(effect = 0, model_form_x = model_form_x_t3, |
| 54 | + noise_mean = 0, noise_sd = 4), |
| 55 | + list(effect = true_effect_long, model_form_x = model_form_x_t4, |
| 56 | + noise_mean = 0, noise_sd = 4) |
| 57 | + ) |
| 58 | + |
| 59 | + data_matrix_list[[trial_iter]] <- simulate_trial( |
| 60 | + X_int, X_ext, num_treated = 150, OLE_flag = TRUE, |
| 61 | + T_cross = 2, outcome_model_specs |
| 62 | + ) |
| 63 | + } |
| 64 | + |
| 65 | + data_matrix_list |
| 66 | +} |
| 67 | + |
| 68 | +# ============================================================================= |
| 69 | +# OLE simulation with DID methods (IPW, AIPW, OR) + bootstrap |
| 70 | +# ============================================================================= |
| 71 | +test_that("OLE simulation report matches vignette", { |
| 72 | + sim_data <- generate_OLE_sim_data() |
| 73 | + |
| 74 | + bootstrap_obj <- setup_bootstrap(replicates = 50, bootstrap_CI_type = "perc") |
| 75 | + |
| 76 | + model_form_mu <- c( |
| 77 | + "y1 ~ x1 + x2 + x3 + x4 + x5", |
| 78 | + "y2 ~ x1 + x2 + x3 + x4 + x5", |
| 79 | + "y3 ~ x1 + x2 + x3 + x4 + x5", |
| 80 | + "y4 ~ x1 + x2 + x3 + x4 + x5" |
| 81 | + ) |
| 82 | + |
| 83 | + method_obj_list <- list( |
| 84 | + setup_method_DID( |
| 85 | + method_name = "IPW", bootstrap_flag = TRUE, bootstrap_obj = bootstrap_obj, |
| 86 | + model_form_piS = "S ~ x1 + x2 + x3 + x4 + x5", |
| 87 | + model_form_piA = "A ~ x1 + x2 + x3 + x4 + x5" |
| 88 | + ), |
| 89 | + setup_method_DID( |
| 90 | + method_name = "AIPW", bootstrap_flag = TRUE, bootstrap_obj = bootstrap_obj, |
| 91 | + model_form_piS = "S ~ x1 + x2 + x3 + x4 + x5", |
| 92 | + model_form_piA = "A ~ x1 + x2 + x3 + x4 + x5", |
| 93 | + model_form_mu0_ext = model_form_mu |
| 94 | + ), |
| 95 | + setup_method_DID( |
| 96 | + method_name = "OR", bootstrap_flag = TRUE, bootstrap_obj = bootstrap_obj, |
| 97 | + model_form_mu0_ext = model_form_mu, |
| 98 | + model_form_mu0_rct = model_form_mu, |
| 99 | + model_form_mu1_rct = model_form_mu |
| 100 | + ) |
| 101 | + ) |
| 102 | + |
| 103 | + sim_obj <- setup_simulation_OLE( |
| 104 | + data_matrix_list = sim_data, |
| 105 | + trial_status_col_name = "S", |
| 106 | + treatment_col_name = "A", |
| 107 | + outcome_col_name = c("y1", "y2", "y3", "y4"), |
| 108 | + covariates_col_name = c("x1", "x2", "x3", "x4", "x5"), |
| 109 | + method_obj_list = method_obj_list, |
| 110 | + true_effect = 0, |
| 111 | + T_cross = 2, |
| 112 | + alpha = 0.05, |
| 113 | + method_description = c("IPW, DID", "AIPW, DID", "OR, DID") |
| 114 | + ) |
| 115 | + |
| 116 | + report <- run_simulation(sim_obj, quiet = TRUE) |
| 117 | + |
| 118 | + # Structure |
| 119 | + expect_s4_class(report, "simulation_report_obj") |
| 120 | + |
| 121 | + tol <- 1e-4 |
| 122 | + |
| 123 | + # Method descriptions |
| 124 | + expect_equal( |
| 125 | + slot(report, "method_description"), |
| 126 | + c("IPW, DID", "AIPW, DID", "OR, DID") |
| 127 | + ) |
| 128 | + |
| 129 | + # Bias |
| 130 | + expect_equal(slot(report, "bias")[1], 1.1724755, tolerance = tol) |
| 131 | + expect_equal(slot(report, "bias")[2], 1.6168995, tolerance = tol) |
| 132 | + expect_equal(slot(report, "bias")[3], 0.7888743, tolerance = tol) |
| 133 | + |
| 134 | + # Variance |
| 135 | + expect_equal(slot(report, "variance")[1], 3.2321870, tolerance = tol) |
| 136 | + expect_equal(slot(report, "variance")[2], 2.8917875, tolerance = tol) |
| 137 | + expect_equal(slot(report, "variance")[3], 0.5824244, tolerance = tol) |
| 138 | + |
| 139 | + # MSE |
| 140 | + expect_equal(slot(report, "mse")[1], 4.606886, tolerance = tol) |
| 141 | + expect_equal(slot(report, "mse")[2], 5.506151, tolerance = tol) |
| 142 | + expect_equal(slot(report, "mse")[3], 1.204747, tolerance = tol) |
| 143 | + |
| 144 | + # Coverage and Type I error |
| 145 | + expect_equal(slot(report, "coverage"), c(1, 1, 1)) |
| 146 | + expect_equal(slot(report, "type_I_error"), c(0, 0, 0)) |
| 147 | + |
| 148 | + # Power (OLE simulation has no alt hypothesis, so power slot is empty) |
| 149 | + expect_length(slot(report, "power"), 0) |
| 150 | +}) |
0 commit comments