--- title: "Cookbook: Continuous Outcome, End to End" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Cookbook: Continuous Outcome, End to End} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` One complete, runnable script: design a two-arm experiment with a continuous outcome, assign treatment, record responses, and run every matched inference procedure. Every chunk executes when the vignette is built; copy the whole thing into a session and it works. The other cookbooks (`vignette("cookbook-incidence")`, `-count`, `-proportion`, `-survival`, `-ordinal`) follow exactly this shape, differing only in the response and the inference class. The pattern is always the same four steps: 1. **construct a `Design`** for `n` subjects and a `response_type`; 2. **assign treatment** — all at once (fixed designs) or one subject at a time as they arrive (sequential designs); 3. **record responses**; 4. **construct an `Inference` class** on the completed design and call its methods — or hand the design to `InferenceSuite` to run every applicable procedure at once. ## Setup and a simulated population EDI is not on CRAN yet, so `install.packages("EDI")` fails — install the prebuilt binaries from R-universe instead (fallback: straight from GitHub; the R package is the `R/EDI` subdirectory). Not evaluated here: the vignette builds inside an already-installed package. ```{r install, eval = FALSE, purl = FALSE} install.packages("EDI", repos = c("https://kapelner.r-universe.dev", "https://cloud.r-project.org")) # or: remotes::install_github("kapelner/EDI", subdir = "R/EDI") ``` ```{r setup} library(EDI) set.seed(20260916) n = 60 X = data.frame( age = round(rnorm(n, 45, 12)), bmi = round(rnorm(n, 27, 4), 1), male = rbinom(n, 1, 0.5) ) true_effect = 2.5 ``` ## A fixed design: everyone is known up front `DesignFixedBernoulli` assigns each subject by an independent coin flip — the simplest fixed design, and the one under which every inference method is valid without caveat. Subjects are added all at once, assigned all at once, and responses recorded all at once. ```{r fixed-design} des = DesignFixedBernoulli$new(n = n, response_type = "continuous", verbose = FALSE) des$add_all_subjects_to_experiment(X) des$assign_w_to_all_subjects() w = des$get_w() # 0/1 treatment vector y = 10 + true_effect * w + 0.1 * X$age - 0.3 * X$bmi + rnorm(n, sd = 3) des$add_all_subject_responses(y) ``` ## Inference: covariate-adjusted OLS `InferenceContinOLS` regresses the response on treatment and the covariates. Every inference class exposes the same core verbs: a point estimate, an asymptotic (Wald) interval and p-value, a randomization test that re-randomizes under the design's own mechanism, and — for fixed designs — a nonparametric bootstrap. ```{r ols} inf = InferenceContinOLS$new(des, verbose = FALSE) inf$num_cores = 1L inf$compute_estimate() inf$compute_asymp_confidence_interval(alpha = 0.05) inf$compute_asymp_two_sided_pval() ``` The randomization test uses no distributional assumption: it re-draws treatment `r` times from the same design and compares the observed statistic to that reference distribution. `set_seed()` makes the result reproducible (see `vignette("reproducibility")`). ```{r ols-rand} inf$set_seed(1) inf$compute_rand_two_sided_pval(r = 200, show_progress = FALSE) ``` Bootstrap intervals resample subjects (the design's own resampling structure — rows here; matched pairs for matched designs): ```{r ols-boot} inf$set_seed(1) inf$compute_bootstrap_confidence_interval(alpha = 0.05, B = 200, show_progress = FALSE) ``` ## Everything at once: `InferenceSuite` `InferenceSuite` discovers every inference class compatible with this design and response type, runs each applicable procedure, and reports a single Cauchy-combined p-value across them. With `screen = TRUE` it prints the full results table — the one-call answer to "what does every valid analysis say?" — and returns the same results as an object (`res`) for programmatic use. By default it runs *every* method each class supports, including the resampling ones (randomization, bootstrap, Bayesian bootstrap, jackknife) at their full default replicate counts — a few minutes per class, which is exactly what you want for a real analysis but not inside a vignette that rebuilds on every `R CMD check`. So this chunk restricts `methods` to the asymptotic procedures (`"wald"`, `"score"`, `"lik_ratio"`; classes that don't support one simply skip it) and sets a per-class time guard. Drop the `methods` argument to get the complete report. ```{r suite} suite = InferenceSuite$new(des) res = suite$run_all_inference(screen = TRUE, plots = FALSE, num_cores = 1L, methods = c("wald", "score", "lik_ratio"), max_secs_per_class = 15) ``` ## A sequential design: subjects arrive one at a time Most real trials enroll sequentially. `DesignSeqOneByOneKK21` — the Kapelner–Krieger matching-on-the-fly design — decides each arrival's treatment by trying to match it to an earlier unmatched subject on the covariates (using the reservoir of unmatched subjects otherwise), so covariate balance is built as the trial runs. The API changes only at step 2: assign and record **per subject**. ```{r seq-design} des_seq = DesignSeqOneByOneKK21$new(n = n, response_type = "continuous", verbose = FALSE) for (i in seq_len(n)) { w_i = des_seq$add_one_subject_to_experiment_and_assign(X[i, , drop = FALSE]) y_i = 10 + true_effect * w_i + 0.1 * X$age[i] - 0.3 * X$bmi[i] + rnorm(1, sd = 3) des_seq$add_one_subject_response(i, y_i) } ``` The matched inference class for this design, `InferenceContinKKOLSIVWC`, combines a within-pair estimate with the reservoir's estimate (inverse- variance weighted). Note the nonparametric bootstrap is **not** offered on sequential designs whose assignment depends on earlier subjects (row resampling would not replicate the design); the randomization test, which replays the design's actual mechanism, is the right tool here. ```{r seq-inference} inf_seq = InferenceContinKKOLSIVWC$new(des_seq, verbose = FALSE) inf_seq$num_cores = 1L inf_seq$compute_estimate() inf_seq$compute_asymp_confidence_interval(alpha = 0.05) inf_seq$set_seed(1) inf_seq$compute_rand_two_sided_pval(r = 200, show_progress = FALSE) ``` ## Where to go next - Which classes exist for which design × response: the [reference index](https://kapelner.github.io/EDI/reference/index.html). - How the estimates were validated: `vignette("validation-evidence")`. - Seeds, RNG streams, and parallel reproducibility: `vignette("reproducibility")`. - Power and operating characteristics for a planned design: `SimulationFramework` (see its reference page's `\donttest{}` example).