Update repo

This commit is contained in:
2026-06-08 10:15:36 -07:00
parent 73e5d46c30
commit 867c37696f
15 changed files with 1899 additions and 5 deletions
+124
View File
@@ -0,0 +1,124 @@
suppressPackageStartupMessages({
library(dplyr)
library(tidyr)
})
# simulate_icu_cohort_longitudinal() generates a sparse longitudinal dataset
# for the ICU septic shock teaching example.
#
# It reuses simulate_icu_cohort() for the baseline data-generating process so
# that cross-sectional and longitudinal results are directly comparable.
# Then it adds time-varying covariates, treatment timing, and censoring events
# at sparse time points.
#
# A "longitudinal" dataset has multiple rows per patient: one row for each
# time at which a variable was measured or an event could occur.
#
# Arguments:
# - n_patients: how many ICU patients to simulate.
# - seed: reproducibility seed.
#
# Returns:
# - A tibble with one row per patient per time point.
# - Time points: 0h, 2h, 6h, 12h, 24h, day7, day14, day21, day28.
# - Columns include baseline variables, time-varying covariates,
# treatment indicators, and censoring/outcome variables.
#
# Example REPL use:
#
# source("R/simulate_icu_cohort.R")
# source("R/simulate_icu_cohort_longitudinal.R")
# long_data <- simulate_icu_cohort_longitudinal(n_patients = 5, seed = 1)
# long_data |> filter(patient_id == 1)
#
simulate_icu_cohort_longitudinal <- function(n_patients = 1000, seed = 20260531) {
# Step 1: Generate baseline cross-sectional data.
# simulate_icu_cohort() handles set.seed() and the full baseline DGP.
# By calling it here, we guarantee that the baseline columns are identical
# to those produced by the cross-sectional notebook workflows.
patients <- simulate_icu_cohort(n_patients = n_patients, seed = seed)
# Step 2: Add the longitudinal-specific variable.
# Some 28-day deaths occur before the 2-hour treatment window closes.
# This creates censoring for the per-protocol analysis.
# Only patients who die by day 28 can die early; the probability is 15%.
patients <- patients |>
mutate(
death_before_2h = ifelse(death_28d == 1, rbinom(n(), 1, 0.15), 0)
)
# Step 3: Define sparse time points for longitudinal follow-up.
# We measure at clinically meaningful intervals during the first day,
# then weekly until day 28.
time_grid <- tibble(
time_hours = c(0, 2, 6, 12, 24, 168, 336, 504, 672),
time_label = c("0h", "2h", "6h", "12h", "24h", "day7", "day14", "day21", "day28")
)
# Step 4: Expand each patient to all time points.
# crossing() creates the Cartesian product: every patient paired with every
# time point. This is a standard tidyverse way to build a longitudinal grid.
longitudinal <- patients |>
crossing(time_grid) |>
arrange(patient_id, time_hours)
# Step 5: Add time-varying indicators.
# These depend on the patient's actual treatment timing and baseline values.
longitudinal <- longitudinal |>
mutate(
# Has vasopressor therapy started by this time point?
vasopressor_started = as.integer(time_hours >= time_to_vasopressor_hours),
# Time-varying MAP: improves modestly after vasopressors start.
# this is a huge assumption/limitation to the simulation dataset, since some
# patients will get better, some stay same, and some worse
map_current = case_when(
vasopressor_started == 1 ~ pmin(map + 5 + rnorm(n(), mean = 0, sd = 3), 95),
TRUE ~ pmax(map - 1 + rnorm(n(), mean = 0, sd = 3), 35)
) |>
round(1),
# Time-varying lactate: decreases modestly after vasopressors start.
lactate_current = case_when(
vasopressor_started == 1 ~ pmax(lactate - 0.5 + rnorm(n(), mean = 0, sd = 0.3), 0.5),
TRUE ~ pmin(lactate + 0.2 + rnorm(n(), mean = 0, sd = 0.3), 15)
) |>
round(1),
# Alive indicator: 0 if the patient has died by this time point.
# Simplified: death_before_2h occurs at 2h; all other deaths at day 28.
alive = case_when(
death_before_2h == 1 & time_hours > 2 ~ 0,
death_28d == 1 & time_hours > 672 ~ 0,
TRUE ~ 1
)
)
# Step 6: Select columns for the analytic dataset.
# We drop intermediate helper columns (severity_score, prob_early_vasopressor,
# mortality_linear_predictor, prob_death_28d) so learners focus on the
# observable variables.
longitudinal |>
select(
patient_id,
time_hours,
time_label,
age,
sex,
sofa_score,
lactate,
map,
lactate_current,
map_current,
suspected_sepsis,
hypotension_at_baseline,
elevated_lactate_at_baseline,
eligible,
early_vasopressor,
time_to_vasopressor_hours,
vasopressor_started,
death_before_2h,
death_28d,
alive
)
}