ccwr is an R package for building reproducible clone-censor-weight (CCW)
workflows for target trial emulation.
This repository is meant to be easy to use from a fresh GitHub clone and easy to maintain as a shared collaboration project. The recommended setup uses:
rig to install and switch to the project R versionrenv to restore the same package environment for every collaboratorgit clone https://github.com/CausalInferenceLab/ccwr.git
cd ccwr
rig to install R 4.4.2This project is pinned to R 4.4.2 for reproducibility.
rig add 4.4.2
rig default 4.4.2
If you already have R 4.4.2, you can skip rig add 4.4.2.
renv
Open R in the project directory and run:
install.packages("renv")
renv::restore()
renv::restore() installs the package versions recorded in renv.lock, so
everyone works with the same dependency set.
Note: this repository includes a .Rprofile file that activates renv
automatically when you open the project in R.
From the project root:
R CMD INSTALL .
Then in R:
library(ccwr)
library(ccwr)
data(lungcancer)
arms <- c("Control", "Surgery")
clones <- clone_arms(lungcancer, arms)
policies <- create_policy_A(
arms,
treatment = "surgery",
time_to_treatment = "timetosurgery",
grace_period = 182.62,
outcome = "death",
followup = "fup_obs",
clone_outcome = "outcome",
clone_followup = "fup"
)
clones_policy <- apply_logics(clones, policies)
censoring_logics <- create_censoring_logics_A(
arms,
treatment = "surgery",
time_to_treatment = "timetosurgery",
grace_period = 182.62,
followup = "fup_obs",
clone_censoring = "censoring",
clone_uncensored_followup = "fup_uncensored"
)
clones_censored <- apply_logics(clones_policy, censoring_logics)
fup_uncensored is the strategy-specific follow-up time for the censoring
rules. It remains the observed follow-up time when a clone adheres to its
assigned strategy and is set to the artificial-censoring time only when that
clone deviates from the strategy.
clones_final <- create_final_data(
clones_censored,
clone_followup = "fup",
clone_outcome = "outcome",
clone_censoring = "censoring",
col_ids = "id"
)
clones_estimated <- estimate_censoring(
clones_final,
predictors = c("age", "sex"),
method = "pooled_logit"
)
clones_weighted <- weight_cases(clones_estimated)
fit <- emul_estimate(
clones_weighted,
method = "Cox",
weights = "weight_Cox",
predictors = c("age", "sex")
)
exp(stats::coef(fit))
boot <- emul_estimate_bootstrap(
lungcancer,
arms = arms,
id = "id",
treatment = "surgery",
time_to_treatment = "timetosurgery",
grace_period = 182.62,
outcome = "death",
followup = "fup_obs",
censoring_predictors = c("age", "sex"),
predictors = c("age", "sex"),
n_bootstrap = 200,
seed = 1
)
The full process is:
clone_arms().create_policy_A() and
apply_logics().create_censoring_logics_A() and apply_logics().create_final_data().estimate_censoring().weight_cases().emul_estimate().emul_estimate_bootstrap() to resample the original subjects and repeat
the complete workflow when bootstrap confidence intervals are needed.Pooled-logistic censoring models use a linear interval-start-time term by
default. Set time_spline_df = 3 in estimate_censoring(), or
censoring_time_spline_df = 3 in emul_estimate_bootstrap(), to use a natural
cubic spline when the censoring data contain enough events to support the
additional flexibility.
The package currently supports two-arm grace-period strategies for subject-level observational time-to-event data. It includes:
read_trial_data() to read trial-style CSV data into a tibblemake_surv_response() to build a survival::Surv() response objectclone_arms() to duplicate observations across treatment strategiescreate_policy_A() and create_censoring_logics_A() to generate example
treatment-policy and artificial-censoring logic for the lung cancer scenarioapply_logics() to apply policy or censoring logic to cloned datacreate_final_data() to create long-form interval data for censoring modelsestimate_censoring() and weight_cases() to estimate censoring
probabilities and add IPC weightsemul_estimate() to estimate treatment effectsemul_estimate_bootstrap() to obtain subject-level bootstrap confidence
intervals by repeating cloning, censoring, weighting, and outcome estimationFor collaborative work, the safest pattern is:
4.4.2 with rig.renv::restore().Useful commands:
R CMD INSTALL .
R CMD check --no-manual .
In R:
testthat::test_local()
If you add, remove, or upgrade dependencies, update the lockfile from R:
renv::settings$snapshot.type("explicit")
renv::status(dev = TRUE)
renv::snapshot(dev = TRUE)
Please commit both code changes and the updated renv.lock when dependency
changes are intentional.
This repository includes two GitHub Actions workflows:
check-reproducible.yaml runs R CMD check with pinned R 4.4.2 and renv
check-latest.yaml runs broader checks across operating systems and R versionsTogether, these workflows help keep the project reproducible for collaborators and stable for future users.