| Title: | Clone-Censor-Weight Methods for Target Trial Emulation |
| Version: | 0.0.2 |
| Description: | Provides methods for clone-censor-weight analyses that emulate target trials with grace-period treatment strategies using observational time-to-event data. Tools clone participants across strategies, apply strategy-specific artificial censoring, and estimate inverse probability of censoring weights using pooled logistic or Cox models, fit weighted outcome models, and obtain subject-level bootstrap confidence intervals by repeating the complete analysis. The methods are described by Maringe et al. (2020) <doi:10.1093/ije/dyaa057> and Gaber et al. (2024) <doi:10.1002/cam4.70461>. |
| License: | MIT + file LICENSE |
| Copyright: | See inst/COPYRIGHTS |
| URL: | https://github.com/CausalInferenceLab/ccwr |
| BugReports: | https://github.com/CausalInferenceLab/ccwr/issues |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.4.0) |
| Imports: | dplyr, readr, survival, tibble, rlang, glue |
| Suggests: | knitr, rmarkdown, testthat (≥ 3.2.0) |
| Config/testthat/edition: | 3 |
| RoxygenNote: | 8.0.0 |
| LazyData: | true |
| VignetteBuilder: | knitr |
| NeedsCompilation: | no |
| Packaged: | 2026-08-20 23:02:09 UTC; sanghopark |
| Author: | Sang Ho Park [aut, cre], Youngrok Lee [aut], Jihyeon Baek [aut], Hye Won Yang [aut], Donghoon Jeong [aut] |
| Maintainer: | Sang Ho Park <shstat1729@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-09-03 12:00:16 UTC |
ccwr: Clone-Censor-Weight Methods for Target Trial Emulation
Description
Provides methods for clone-censor-weight analyses that emulate target trials with grace-period treatment strategies using observational time-to-event data. Tools clone participants across strategies, apply strategy-specific artificial censoring, and estimate inverse probability of censoring weights using pooled logistic or Cox models, fit weighted outcome models, and obtain subject-level bootstrap confidence intervals by repeating the complete analysis. The methods are described by Maringe et al. (2020) doi:10.1093/ije/dyaa057 and Gaber et al. (2024) doi:10.1002/cam4.70461.
Author(s)
Maintainer: Sang Ho Park shstat1729@gmail.com
Authors:
Youngrok Lee
Jihyeon Baek
Hye Won Yang
Donghoon Jeong
See Also
Useful links:
Report bugs at https://github.com/CausalInferenceLab/ccwr/issues
Apply case_when logics (e.g. policy, censoring) to clones
Description
Apply case_when logics (e.g. policy, censoring) to clones
Usage
apply_logics(clones, logics)
Arguments
clones |
A list of data frame. Each element of the list represents each treatment arm. |
logics |
A nested list. Each element of outer list represents each
treatment arm. Each element of inner list represents each new variable
to be created by applying logics. Each element of inner list containts a
character vector that represents a sequence of logics to be passed into
|
Value
A list of data frame that each data frame include new variables created by the provided logics.
Examples
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)
Duplicate data frame for each treatment arm to emulate
Description
Duplicate data frame for each treatment arm to emulate
Usage
clone_arms(data, arms)
Arguments
data |
Input data frame that contains all observations of interest. Each row represents an observation, and columns include observation identifiers, binary treatment variable (0/1), time to treatement (continuous), binary outcome variable (0/1), observed followup time (continuous), and covariates. |
arms |
Character vector that each element represents each arm's name. |
Value
A list of data frame. Each element of list is associated with each arm.
Examples
data(lungcancer)
clones <- clone_arms(lungcancer, c("Control", "Surgery"))
Generate censoring logic for scenario A
Description
Generate censoring logic for scenario A
Usage
create_censoring_logics_A(
arms,
treatment,
time_to_treatment,
grace_period,
followup,
clone_censoring = ".censoring",
clone_uncensored_followup = ".fup_uncensored"
)
Arguments
arms |
A character vector of length 2. The first element represents a name of the untreated arm, and the second element represents a name of the treated arm. |
treatment |
A name of variable that represents whether each observation was treated or not in observational data. The treatment variable should exists in data frame that the return value of this function will be applied, and the treatment variable value in the data frame should be either 0 and 1, i.e. binary treatment. |
time_to_treatment |
A name of variable that represent time to treatment in the observational data. The time-to-treatment variable should exists in data frame that the return value of this function will be applied, and the value in the data frame should be either numeric value or NA if the observation was untreated in the observational data. |
grace_period |
A numeric value to represent grace period of treatment. Treatment policy is assumed to be "provide treatment within the grace period." |
followup |
A name of variable that represent follow up time in the observational data. The follow up time variable should exists in data frame that the return value of this function will be applied, and the follow up time variable value in the data frame should be numeric. |
clone_censoring |
A name of binary indicator variable to be newly created to represent whether the observation violates arm's policy or not. The new variable name should not already exist in the data frame that the return value of this function will be applied, to avoid accidental overwriting. |
clone_uncensored_followup |
A name of a variable to be newly created for the strategy-specific follow-up time used by the censoring rules. It equals the observed follow-up time for a clone that remains adherent to its assigned strategy, and the artificial-censoring time for a clone that violates it. The new variable name should not already exist in the data frame that the return value of this function will be applied, to avoid accidental overwriting. |
Value
A nested list. The first element of the outer list represents
untreated arm, while the second element of the outer list represents
treated arm. For each element of outer list, the first element of the inner
list represents emulated censoring binary indicator (0/1) that represents
whether the observation violated the arm's policy within the grace period.
The second element of the inner list represents the strategy-specific
follow-up time for the censoring process: observed follow-up for adherent
clones and artificial-censoring time for non-adherent clones. Each element
of the inner list represents a sequence of logics to be passed into
case_when() when creating new variables for the emulated censoring
indicator and follow-up time.
Examples
arms <- c("Control", "Surgery")
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"
)
Create training data for censoring probability estimation
Description
Create training data for censoring probability estimation
Usage
create_final_data(
clones,
clone_followup,
clone_outcome,
clone_censoring,
col_ids,
timestamp_start = "Tstart",
id = "ID",
timestamp_stop = "Tstop"
)
Arguments
clones |
A list of data frame. Each element of the list represents
each treatment arm. This version of clones must contain a column that
represents an emulated follow up time (corresponding to |
clone_followup |
A column name that represents the emulated follow up
time in each arm of clones. The variable should exists in each element
data frame of |
clone_outcome |
A column name that represents the emulated outcome
in each arm of clones. The variable should exists in each element
data frame of |
clone_censoring |
A column name that represent whether the observation
violates arm's policy or not. The variable should exists in each element
data frame of |
col_ids |
A vector of column names that a combination of their values uniquely identifies each observation. |
timestamp_start |
A new variable name to denote start time of each subrecord of observations in a long-form data. |
id |
A new variable name for a unique observation identifier, to represents that multiple rows in output data frame is associated with the same observation. |
timestamp_stop |
A new variable name to denote end time of each subrecord of observations in a long-form data. |
Value
A list of long-form data frames. Each data frame represents each
clone arm. Each row of the long-form data frame represents a subrecord of
each observation associated with each specific time interval. The first
subrecord starts with time 0, and the rows are expanded up to
clone_followup, where cut times are determined by t_events argument.
Examples
data(lungcancer)
arms <- c("Control", "Surgery")
clones <- clone_arms(lungcancer, arms)
policies <- create_policy_A(
arms, "surgery", "timetosurgery", 182.62, "death", "fup_obs",
clone_outcome = "outcome", clone_followup = "fup"
)
clones_policy <- apply_logics(clones, policies)
censoring_logics <- create_censoring_logics_A(
arms, "surgery", "timetosurgery", 182.62, "fup_obs",
clone_censoring = "censoring",
clone_uncensored_followup = "fup_uncensored"
)
clones_censored <- apply_logics(clones_policy, censoring_logics)
clones_final <- create_final_data(
clones_censored,
clone_followup = "fup",
clone_outcome = "outcome",
clone_censoring = "censoring",
col_ids = "id"
)
Generate clone policy for scenario A
Description
Generate clone policy for scenario A
Usage
create_policy_A(
arms,
treatment,
time_to_treatment,
grace_period,
outcome,
followup,
clone_outcome = ".outcome",
clone_followup = ".fup"
)
Arguments
arms |
A character vector of length 2. The first element represents a name of the untreated arm, and the second element represents a name of the treated arm. |
treatment |
A name of variable that represents whether each observation was treated or not in observational data. The treatment variable should exists in data frame that the return value of this function will be applied, and the treatment variable value in the data frame should be either 0 and 1, i.e. binary treatment. |
time_to_treatment |
A name of variable that represent time to treatment in the observational data. The time-to-treatment variable should exists in data frame that the return value of this function will be applied, and the value in the data frame should be either numeric value or NA if the observation was untreated in the observational data. |
grace_period |
A numeric value to represent grace period of treatment. Treatment policy is assumed to be "provide treatment within the grace period." |
outcome |
A name of variable that represent outcome in the observational data. The outcome variable should exists in data frame that the return value of this function will be applied, and the outcome variable value in the data frame should be either 0 and 1, i.e. binary outcome. |
followup |
A name of variable that represent follow up time in the observational data. The follow up time variable should exists in data frame that the return value of this function will be applied, and the follow up time variable value in the data frame should be numeric. |
clone_outcome |
A name of variable to be newly created to represent emulated outcome in cloned data frame. The new variable name should not already exist in the data frame that the return value of this function will be applied, to avoid accidental overwriting. |
clone_followup |
A name of variable to be newly created to represent emulated follow up time in cloned data frame. The new variable name should not already exist in the data frame that the return value of this function will be applied, to avoid accidental overwriting. |
Value
A nested list. The first element of the outer list represents
untreated arm, while the second element of the outer list represents
treated arm. For each element of outer list, the first element of the inner
list represents emulated outcome, and the second element of the inner list
represents emulated follow up time. Each element of the inner list
represents a sequence of logics to be passed into case_when() when
creating new variables for emulated outcome and follow up time.
Examples
arms <- c("Control", "Surgery")
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"
)
Estimate the emulated trial effect
Description
Estimate the emulated trial effect
Usage
emul_estimate(
clones_weighted,
method = c("Cox", "logistic", "KM"),
cluster = "id",
weights = NULL,
predictors = NULL,
outcome = "outcome",
time_start = "Tstart",
time_stop = "Tstop",
arm = "arms"
)
Arguments
clones_weighted |
A named list of weighted clone data frames, or a single data frame containing an arm column. |
method |
Analysis method: |
cluster |
Column name used for robust clustering in Cox models. |
weights |
Optional weight column name. Unquoted column names are also accepted when they exist in the data. |
predictors |
Optional adjustment predictors. For KM, predictors define additional strata rather than covariate adjustment. |
outcome |
Column name for the outcome indicator. |
time_start |
Column name for interval start time. |
time_stop |
Column name for interval stop time. |
arm |
Column name for treatment arm after binding clone lists. |
Value
A fitted model object: "coxph" for Cox, "glm" for logistic, or
"survfit" for KM.
Examples
data(lungcancer)
arms <- c("Control", "Surgery")
clones <- clone_arms(lungcancer, arms)
policies <- create_policy_A(
arms, "surgery", "timetosurgery", 182.62, "death", "fup_obs",
clone_outcome = "outcome", clone_followup = "fup"
)
clones_policy <- apply_logics(clones, policies)
censoring_logics <- create_censoring_logics_A(
arms, "surgery", "timetosurgery", 182.62, "fup_obs",
clone_censoring = "censoring",
clone_uncensored_followup = "fup_uncensored"
)
clones_censored <- apply_logics(clones_policy, censoring_logics)
clones_final <- create_final_data(
clones_censored, "fup", "outcome", "censoring", "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")
)
Bootstrap a complete clone-censor-weight analysis
Description
Resamples subjects from the original subject-level data and repeats the complete analysis in every bootstrap replicate: cloning, strategy-specific artificial censoring, person-time expansion, censoring-model estimation, weighting, and outcome-model estimation.
Usage
emul_estimate_bootstrap(
data,
arms,
id,
treatment,
time_to_treatment,
grace_period,
outcome,
followup,
censoring_predictors = NULL,
censoring_method = c("pooled_logit", "stabilized_logit", "Cox"),
numerator_predictors = NULL,
censoring_time_spline_df = NULL,
method = c("Cox", "logistic"),
predictors = NULL,
n_bootstrap = 200,
conf_level = 0.95,
eps = 1e-06,
seed = NULL
)
Arguments
data |
A data frame with one row per subject. |
arms |
A character vector of length two. The first element names the strategy of not receiving treatment during the grace period; the second names the strategy of receiving treatment during the grace period. |
id |
Name of the column that uniquely identifies subjects. |
treatment |
Name of the binary treatment column. |
time_to_treatment |
Name of the numeric time-to-treatment column. |
grace_period |
Positive numeric length of the treatment grace period. |
outcome |
Name of the binary outcome column. |
followup |
Name of the numeric follow-up-time column. |
censoring_predictors |
Optional character vector of predictors for the denominator censoring model. |
censoring_method |
Censoring model passed to |
numerator_predictors |
Optional character vector of predictors for the
numerator model when |
censoring_time_spline_df |
Degrees of freedom for the natural cubic
spline of interval start time in pooled-logistic censoring models. Set to
an integer of at least |
method |
Outcome analysis method: |
predictors |
Optional adjustment predictors for the outcome model. |
n_bootstrap |
Number of bootstrap resamples. |
conf_level |
Confidence level for the percentile interval. |
eps |
Probability floor passed to |
seed |
Optional random seed. |
Value
A list containing the full-sample effect estimate, percentile confidence limits, bootstrap estimates, and analysis settings. Effects are returned on the exponentiated coefficient scale: a hazard ratio for Cox models and an odds ratio for logistic models.
Examples
data(lungcancer)
boot <- emul_estimate_bootstrap(
lungcancer,
arms = c("Control", "Surgery"),
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 = 3,
seed = 1
)
Estimate censoring probabilities
Description
Estimate censoring probabilities
Usage
estimate_censoring(
clones,
predictors = NULL,
method = c("Cox", "pooled_logit", "stabilized_logit"),
numerator_predictors = NULL,
censoring = "censoring",
id = "id",
time_start = "Tstart",
time_stop = "Tstop",
time_spline_df = NULL,
eps = 1e-06
)
Arguments
clones |
A named list of long-form clone data frames. |
predictors |
Optional character vector of denominator model predictors. |
method |
Censoring model. |
numerator_predictors |
Optional character vector of numerator model
predictors for stabilized pooled-logit weights. When |
censoring |
Column name for the censoring indicator. |
id |
Column name for the subject identifier. |
time_start |
Column name for interval start time. |
time_stop |
Column name for interval stop time. |
time_spline_df |
Degrees of freedom for the natural cubic spline of
|
eps |
Small probability floor to avoid division by zero. |
Value
A named list of clone data frames with censoring probability
columns added. All methods add P_uncens; pooled-logit methods also add
p_cens_den, and stabilized pooled logit adds p_cens_num and
P_uncens_num.
Examples
data(lungcancer)
arms <- c("Control", "Surgery")
clones <- clone_arms(lungcancer, arms)
policies <- create_policy_A(
arms, "surgery", "timetosurgery", 182.62, "death", "fup_obs",
clone_outcome = "outcome", clone_followup = "fup"
)
clones_policy <- apply_logics(clones, policies)
censoring_logics <- create_censoring_logics_A(
arms, "surgery", "timetosurgery", 182.62, "fup_obs",
clone_censoring = "censoring",
clone_uncensored_followup = "fup_uncensored"
)
clones_censored <- apply_logics(clones_policy, censoring_logics)
clones_final <- create_final_data(
clones_censored, "fup", "outcome", "censoring", "id"
)
clones_estimated <- estimate_censoring(
clones_final,
predictors = c("age", "sex"),
method = "pooled_logit"
)
Simulated lung cancer patients data by Maringe et al. (2020)
Description
The dataset is from the supplementary files of Maringe et al. (2020), doi:10.1093/ije/dyaa057. The dataset is a set of 200 simulated lung cancer patients. These patients are followed up for a year following their cancer diagnosis: 106 of them received surgery within six months of their diagnosis and 48 died in the year.
Usage
lungcancer
Format
lungcancer
A data frame with 200 rows and 12 columns:
- id
patient identifier
- fup_obs
observed follow-up time (time to death or 1 year if censored alive)
- death
observed event of interest (all-cause death) 1: dead, 0:alive
- timetosurgery
time to surgery (NA if no surgery)
- surgery
observed treatment 1 if the patient received surgery within 6 month, 0 otherwise
- age
age at diagnosis
- sex
patient's sex
- perf
performance status at diagnosis
- stage
stage at diagnosis
- deprivation
deprivation score
- charlson
Charlson's comorbidity index
- emergency
route to diagnosis
Details
See system.file("COPYRIGHTS", package = "ccwr") for the source and
copyright notice.
Source
Examples
data(lungcancer)
Construct a survival response
Description
Construct a survival response
Usage
make_surv_response(data, follow_up, event)
Arguments
data |
A data frame with follow-up and event columns. |
follow_up |
The name of the follow-up time column. |
event |
The name of the event indicator column. |
Value
An object of class "Surv".
Examples
data(lungcancer)
surv_response <- make_surv_response(
lungcancer,
follow_up = "fup_obs",
event = "death"
)
13 types of patient records in Maringe et al. (2020)
Description
The dataset is from Figure 2 in Maringe et al. (2020), doi:10.1093/ije/dyaa057. The dataset illustrates all possible censoring mechanisms with 13 types of patients records that could be seen in the cancer registry data, when allowing at most one treatment (surgery in this example) for each patient and outcome event (death in this example) happens at most once for each patient.
Usage
patients13
Format
patients13
A data frame with 13 rows and 5 columns:
- id
patient identifier
- surgery
observed treatment 1 if the patient received surgery during follow-up, 0 otherwise
- time_to_surgery
time to surgery in days (NA if no surgery)
- death
observed event at the latest follow-up, 1: dead, 0: alive
- followup
observed follow-up time (time to death or time to latest followup with 1 year (365 days) at maximum)
Details
See system.file("COPYRIGHTS", package = "ccwr") for the source and
copyright notice.
Source
Examples
data(patients13)
Read trial-style data from CSV
Description
Read trial-style data from CSV
Usage
read_trial_data(file, show_col_types = FALSE)
Arguments
file |
Path to a CSV file. |
show_col_types |
Passed to |
Value
A tibble.
Examples
csv_file <- tempfile(fileext = ".csv")
writeLines(
c(
"id,follow_up,event,treatment",
"1,10,1,A",
"2,12,0,B"
),
con = csv_file
)
trial_data <- read_trial_data(csv_file)
Add inverse probability of censoring weights
Description
Add inverse probability of censoring weights
Usage
weight_cases(
clones,
uncensored_prob = "P_uncens",
numerator_uncensored_prob = "P_uncens_num",
weight = "weight_Cox",
eps = 1e-06
)
Arguments
clones |
A named list of clone data frames returned by
|
uncensored_prob |
Column name for denominator uncensoring probability. |
numerator_uncensored_prob |
Column name for numerator uncensoring probability. When this column exists, stabilized weights are created. |
weight |
Column name for the output weight. |
eps |
Small probability floor to avoid division by zero. |
Value
A named list of clone data frames with the weight column added.
Examples
data(lungcancer)
arms <- c("Control", "Surgery")
clones <- clone_arms(lungcancer, arms)
policies <- create_policy_A(
arms, "surgery", "timetosurgery", 182.62, "death", "fup_obs",
clone_outcome = "outcome", clone_followup = "fup"
)
clones_policy <- apply_logics(clones, policies)
censoring_logics <- create_censoring_logics_A(
arms, "surgery", "timetosurgery", 182.62, "fup_obs",
clone_censoring = "censoring",
clone_uncensored_followup = "fup_uncensored"
)
clones_censored <- apply_logics(clones_policy, censoring_logics)
clones_final <- create_final_data(
clones_censored, "fup", "outcome", "censoring", "id"
)
clones_estimated <- estimate_censoring(
clones_final,
predictors = c("age", "sex"),
method = "pooled_logit"
)
clones_weighted <- weight_cases(clones_estimated)