Files
2026-06-08 10:15:36 -07:00

7.5 KiB

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

  • Choose ICU teaching scenario: early vasopressor strategy in septic shock.
  • Simulate a simple ICU observational cohort.
  • Define the target trial protocol explicitly.
  • Estimate a naive observational association.
  • Show why naive comparison can be biased.
  • Align time zero and eligibility criteria.
  • Introduce treatment assignment windows.
  • Add censoring logic.
  • Add inverse probability weighting from first principles.
  • 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.
  • 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.