# Target Trial Emulation Learning Project This project is for learning how to code and understand target trial emulation in R. The main teaching example is an ICU septic shock study: > Among ICU patients with suspected septic shock at ICU admission, what is the effect of initiating vasopressors early versus not initiating vasopressors early on 28-day mortality? ## How To Work With The User - Teach by showing small code chunks that the user can type in manually. - Explain each meaningful line of code before moving on. - Add generous learner-focused comments, especially around R functions, function arguments, return values, and unfamiliar package functions. - Include many small examples and REPL-style checkpoints with code, expected output, and interpretation. - Keep report-style Quarto notebooks focused on rendered tables and interpretation unless the user asks for REPL-style checkpoints there. - Teach the base R mechanics when a concept is new, then prefer readable tidyverse-style code for routine analysis. - Make the smallest correct change when editing project files. - Keep scripts and notebooks numbered so the learning sequence is obvious. - Avoid over-abstraction early; build reusable primitives only after the concept is clear. - When adding reusable code, explain what future project need it supports. - When using external dependencies, prefer wrapping them behind project functions so the user learns stable primitives. ## Code Style - Use the native pipe `|>`, not `%>%` - snake_case for all names - Prefer explicit, teaching-oriented comments over terse production-style code while this remains a learning project. - For reusable functions, include comments describing purpose, arguments, return value, and at least one example call. - Prefer `vapply` over `sapply`; explicit return types - Use `cli::cli_*` for messages, not `message()`/`cat()` - Prefer cleaner imports with grouped startup message suppression, for example `suppressPackageStartupMessages({ library(readr); library(dplyr) })`. - Prefer `dplyr` verbs for data manipulation when external dependencies are allowed. - Prefer `skimr` for quick data summaries. - Prefer `gt` and `gtsummary` for clear analytic tables in notebooks and reports. - Style with `styler::style_pkg()` before commits ## Don'ts - Don't modify `renv.lock` by hand - Don't `setwd()` — rely on the project root (`here::here()`) - Don't introduce new dependencies beyond the approved stack without asking ## Stack - R 4.4 managed by rig - renv for dependency management; lockfile is source of truth - targets for pipeline orchestration - tidyverse, especially `dplyr`, for routine data manipulation - ggplot2 for exploratory plots and visual diagnostics - data.table for performance-oriented data manipulation when needed - skimr for quick data summaries - gt for presentation tables - gtsummary for descriptive and model summary tables - Quarto for reports ## Dependency Preference - The first scripts may remain base R to teach the underlying mechanics. - Going forward, use `dplyr`, `tidyverse`, `skimr`, `gt`, and `gtsummary` where they make the code clearer. - Keep base R explanations available when they help the user understand what the package code is doing. - Do not add packages outside the approved stack without asking first. ## Workflow Roles - Put reusable logic in `R/` functions. - Put reusable smoke checks or command-line workflows in `scripts/`. - Put polished displays, interpretation, exploratory visualization, and rendered result tables in Quarto notebooks. - Avoid CSV intermediates when functions can be called directly and reproducibly. - Use `run_all.sh` as the lightweight end-to-end runner until the project is ready for `targets`. - Keep rendered notebook reports in `outputs/reports/`. ## Learning Roadmap - [x] Choose ICU teaching scenario: early vasopressor strategy in septic shock. - [x] Simulate a simple ICU observational cohort. - [x] Define the target trial protocol explicitly. - [x] Estimate a naive observational association. - [x] Show why naive comparison can be biased. - [x] Align time zero and eligibility criteria. - [x] Introduce treatment assignment windows. - [x] Add censoring logic. - [x] Add inverse probability weighting from first principles. - [x] Refactor repeated logic into reusable project functions. - [ ] Re-implement selected steps with external dependencies. - [ ] Build wrapper functions around external dependency workflows. - [ ] Add a targets pipeline. - [x] Add initial Quarto report for reproducible analysis. ## Initial Target Trial Protocol Clinical question: Among ICU patients with suspected septic shock at ICU admission, what is the effect of starting vasopressors within 2 hours compared with not starting vasopressors within 2 hours on 28-day mortality? Eligibility criteria: - ICU admission. - Suspected sepsis. - Hypotension at baseline. - Elevated lactate at baseline. Time zero: - ICU admission. Treatment strategies: - Early vasopressor strategy: start vasopressors within 2 hours of ICU admission. - No early vasopressor strategy: do not start vasopressors within 2 hours of ICU admission. Outcome: - Death within 28 days. Baseline confounders in the first simulated dataset: - Age. - Sex. - SOFA score. - Lactate. - Mean arterial pressure. Initial estimand: - Risk difference in 28-day mortality. - Risk ratio for 28-day mortality. ## File Sequence - `run_all.sh`: run the key scripts and render all current notebooks. - `scripts/01_simulate_icu_data.R`: simulate an ICU cohort in memory and print a quick `skimr` summary. - `R/simulate_icu_cohort.R`: first reusable simulation primitive. - `R/estimate_naive_vasopressor_mortality_effect.R`: shared naive mortality-effect primitive used by notebook workflows. - `R/estimate_standardized_vasopressor_mortality_effect.R`: shared outcome-regression standardization primitive. - `R/estimate_iptw_vasopressor_mortality_effect.R`: shared inverse-probability-of-treatment weighting primitive with unstabilized weights, propensity model diagnostics, weighted baseline balance, and weighted effect estimates. - `notebooks/01_target_trial_basics.qmd`: report-style walkthrough of the target trial protocol and initial results. - `notebooks/02_explore_simulated_data.qmd`: exploratory visual diagnostics for the simulated cohort. - `notebooks/03_why_naive_analysis_is_biased.qmd`: explanation of confounding by indication and first standardized mortality-effect estimate. - `notebooks/04_inverse_probability_weighting.qmd`: IPTW from first principles, weight diagnostics, baseline balance table, weighted effect estimates, and a three-way comparison of naive, standardized, and IPTW results. - `R/simulate_icu_cohort_longitudinal.R`: longitudinal simulation primitive with time-varying covariates, treatment timing, and censoring events at sparse time points. - `R/clone_trial_arms.R`: cloning function that duplicates each eligible patient into two trial arms at time zero and applies censoring rules at the 2-hour mark. - `R/estimate_iptw_and_ipcw_effect.R`: combined IPTW + IPCW weighting primitive for per-protocol effect estimation in the cloned dataset. - `notebooks/05_treatment_assignment_windows.qmd`: treatment assignment windows, grace periods, cloning mechanics, and censoring rules. - `notebooks/06_censoring_and_ipcw.qmd`: inverse probability of censoring weighting, combined weight diagnostics, and comparison with cross-sectional IPTW. - `notebooks/07_full_emulation_pipeline.qmd`: end-to-end target trial emulation pipeline with four-way comparison of naive, standardized, cross-sectional IPTW, and longitudinal IPTW + IPCW methods.