Here, we’ll go over some examples of using per-protocol, censoring. First we need to load the library before getting in to some sample use cases.
Per-protocol, censoring, weights in pre-expanded data and no truncation, no excused conditions (i.e. static interventions)
options <- SEQopts(# tells SEQuential to create Kaplan-Meier curves
km.curves = TRUE,
# tells SEQuential to weight the outcome model
weighted = TRUE,
# tells SEQuential to build weights from the pre-expanded data
weight.preexpansion = TRUE)
# use some example data in the package
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Non-required columns provided, pruning for efficiency
#>
#> Pruned
#>
#> Original dataset (eligible subjects): 9,203 observations, 9 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 15 variables
#>
#> Expanded dataset (post-censoring): 102,749 observations, 15 variables
#> entering outcome model (uncensored): 96,251
#> artificially censored (treatment switch): 6,498
#>
#> Expansion Successful
#>
#> Final analysis dataset: 102,749 observations, 15 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
# retrieve risk plot
km_curve(model, plot.type = "risk")
# retrieve survival and risk data
survival_data <- km_data(model)
risk_data(model)
#> Index: <Followup>
#> Method Followup A Risk
#> <char> <num> <char> <num>
#> 1: censoring 60 0 0.6723717
#> 2: censoring 60 1 0.9243324
risk_comparison(model)
#> Followup A_x A_y Risk Ratio Risk Difference
#> <num> <fctr> <fctr> <num> <num>
#> 1: 60 risk_0 risk_1 1.3747342 0.2519607
#> 2: 60 risk_1 risk_0 0.7274133 -0.2519607Per-protocol, censoring, weights in post-expanded data and no truncation, no excused conditions (i.e. static interventions)
options <- SEQopts(km.curves = TRUE,
weighted = TRUE,
# tells SEQuential to build weights from the post-expanded data
weight.preexpansion = FALSE)
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Non-required columns provided, pruning for efficiency
#>
#> Pruned
#>
#> Original dataset (eligible subjects): 9,203 observations, 9 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 18 variables
#>
#> Expanded dataset (post-censoring): 102,749 observations, 18 variables
#> entering outcome model (uncensored): 96,251
#> artificially censored (treatment switch): 6,498
#>
#> Expansion Successful
#>
#> Final analysis dataset: 102,749 observations, 18 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
km_curve(model, plot.type = "risk")
risk_data(model)
#> Index: <Followup>
#> Method Followup A Risk
#> <char> <num> <char> <num>
#> 1: censoring 60 0 0.6533049
#> 2: censoring 60 1 0.9281893
risk_comparison(model)
#> Followup A_x A_y Risk Ratio Risk Difference
#> <num> <fctr> <fctr> <num> <num>
#> 1: 60 risk_0 risk_1 1.4207598 0.2748844
#> 2: 60 risk_1 risk_0 0.7038488 -0.2748844Per-protocol, censoring, weights in pre-expanded data and no truncation, excused conditions for initiators and non-initiators (i.e. dynamic interventions)
options <- SEQopts(km.curves = TRUE,
weighted = TRUE,
weight.preexpansion = TRUE,
# tells SEQuential to run a dynamic intervention
excused = TRUE,
# excusedZero excuses switches from treatment 0 to 1,
# excusedOne switches from 1 to 0
excused.cols = c("excusedZero", "excusedOne"),
# tells SEQuential to expect treatment levels 0, 1
# (mapping to the same positions as the list in excused.cols)
treat.level = c(0, 1))
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Original dataset (eligible subjects): 9,203 observations, 11 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 18 variables
#>
#> Expanded dataset (post-censoring): 188,475 observations, 18 variables
#> entering outcome model (uncensored): 186,259
#> artificially censored (treatment switch): 2,216
#>
#> Expansion Successful
#>
#> Final analysis dataset: 188,475 observations, 18 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
km_curve(model, plot.type = "risk")
risk_data(model)
#> Index: <Followup>
#> Method Followup A Risk
#> <char> <num> <char> <num>
#> 1: censoring 60 0 0.8730463
#> 2: censoring 60 1 0.8855556
risk_comparison(model)
#> Followup A_x A_y Risk Ratio Risk Difference
#> <num> <fctr> <fctr> <num> <num>
#> 1: 60 risk_0 risk_1 1.0143283 0.01250924
#> 2: 60 risk_1 risk_0 0.9858741 -0.01250924Per-protocol, censoring, a flexible baseline hazard in the weight models
By default the weight models are quadratic in time:
followup, followup_sq, trial and
trial_sq when weight.preexpansion = FALSE, and
the time column and its square when
weight.preexpansion = TRUE. A quadratic only lets the
hazard of treatment rise or flatten off, which can fit poorly when the
cumulative incidence of treatment moves through several phases over
follow-up. weight.spline = TRUE replaces each of those
quadratics with a natural cubic spline basis
(splines::ns()) with weight.spline.df degrees
of freedom, letting the baseline hazard take a flexible shape over time.
This applies to the treatment weight models and, when cense
or visit is set, to those models too.
options <- SEQopts(km.curves = TRUE,
weighted = TRUE,
weight.preexpansion = FALSE,
# model time in the weight models as a natural cubic spline
weight.spline = TRUE,
# 4 degrees of freedom, i.e. 3 interior knots at quantiles
weight.spline.df = 4L)
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Non-required columns provided, pruning for efficiency
#>
#> Pruned
#>
#> Original dataset (eligible subjects): 9,203 observations, 9 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 18 variables
#>
#> Expanded dataset (post-censoring): 102,749 observations, 18 variables
#> entering outcome model (uncensored): 96,251
#> artificially censored (treatment switch): 6,498
#>
#> Expansion Successful
#>
#> Final analysis dataset: 102,749 observations, 18 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
# the weight model formulas, with the spline knots fixed from the data
covariates(model)
#> $Outcome
#> [1] "outcome ~ tx_init_bas + followup + followup_sq + trial + trial_sq + sex + N_bas + L_bas + P_bas + tx_init_bas*followup"
#>
#> $Numerator
#> [1] "tx_init ~ sex + N_bas + L_bas + P_bas + ns(followup, knots = c(2, 7, 14), Boundary.knots = c(0, 58)) + ns(trial, knots = c(8, 16, 26), Boundary.knots = c(0, 59))"
#>
#> $Denominator
#> [1] "tx_init ~ sex + N + L + P + N_bas + L_bas + P_bas + ns(followup, knots = c(2, 7, 14), Boundary.knots = c(0, 58)) + ns(trial, knots = c(8, 16, 26), Boundary.knots = c(0, 59))"For finer control - a spline in followup but not
trial, a different number of knots per term, or a spline in
a time-varying confounder - write the ns() terms into
numerator and denominator yourself. Any
ns(x, df = N) term in a model formula has its knots fixed
from the data the model is fit on before fitting, so the basis is the
same when the model is fit and when it is used to predict the weights,
and is held constant across bootstrap resamples.
options <- SEQopts(weighted = TRUE,
weight.preexpansion = FALSE,
numerator = "sex+N_bas+L_bas+P_bas+ns(followup, df = 5)+trial+trial_sq",
denominator = "sex+N+L+P+N_bas+L_bas+P_bas+ns(followup, df = 5)+trial+trial_sq")
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Non-required columns provided, pruning for efficiency
#>
#> Pruned
#>
#> Original dataset (eligible subjects): 9,203 observations, 9 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 18 variables
#>
#> Expanded dataset (post-censoring): 102,749 observations, 18 variables
#> entering outcome model (uncensored): 96,251
#> artificially censored (treatment switch): 6,498
#>
#> Expansion Successful
#>
#> Final analysis dataset: 102,749 observations, 18 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Completed
covariates(model)
#> $Outcome
#> [1] "outcome ~ tx_init_bas + followup + followup_sq + trial + trial_sq + sex + N_bas + L_bas + P_bas"
#>
#> $Numerator
#> [1] "tx_init ~ sex + N_bas + L_bas + P_bas + ns(followup, knots = c(2, 5, 9, 16), Boundary.knots = c(0, 58)) + trial + trial_sq"
#>
#> $Denominator
#> [1] "tx_init ~ sex + N + L + P + N_bas + L_bas + P_bas + ns(followup, knots = c(2, 5, 9, 16), Boundary.knots = c(0, 58)) + trial + trial_sq"Per-protocol, censoring, weights in post-expanded data and no truncation, excused conditions for initiators and non-initiators (i.e. dynamic interventions)
options <- SEQopts(km.curves = TRUE,
weighted = TRUE,
weight.preexpansion = FALSE,
excused = TRUE,
excused.cols = c("excusedZero", "excusedOne"),
treat.level = c(0, 1),
weight.p99 = TRUE)
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Original dataset (eligible subjects): 9,203 observations, 11 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 21 variables
#>
#> Expanded dataset (post-censoring): 188,475 observations, 21 variables
#> entering outcome model (uncensored): 186,259
#> artificially censored (treatment switch): 2,216
#>
#> Expansion Successful
#>
#> Final analysis dataset: 188,475 observations, 21 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
km_curve(model, plot.type = "risk")
risk_data(model)
#> Index: <Followup>
#> Method Followup A Risk
#> <char> <num> <char> <num>
#> 1: censoring 60 0 0.8454007
#> 2: censoring 60 1 0.8691113
risk_comparison(model)
#> Followup A_x A_y Risk Ratio Risk Difference
#> <num> <fctr> <fctr> <num> <num>
#> 1: 60 risk_0 risk_1 1.0280466 0.02371061
#> 2: 60 risk_1 risk_0 0.9727186 -0.02371061Per-protocol, censoring, weights in post-expanded data and no truncation, excused conditions for initiators and non-initiators (i.e. dynamic interventions) and a competing event
options <- SEQopts(km.curves = TRUE,
weighted = TRUE,
weight.preexpansion = FALSE,
excused = TRUE,
excused.cols = c("excusedZero", "excusedOne"),
treat.level = c(0, 1),
# add a competing event
compevent = "LTFU")
data <- SEQdata.LTFU
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 54,687 observations, 13 variables
#>
#> Non-required columns provided, pruning for efficiency
#>
#> Pruned
#>
#> Original dataset (eligible subjects): 29,624 observations, 12 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 1,609,859 observations
#>
#> Expanded dataset (pre-censoring): 1,119,229 observations, 22 variables
#>
#> Expanded dataset (post-censoring): 814,689 observations, 22 variables
#> entering outcome model (uncensored): 806,922
#> artificially censored (treatment switch): 7,767
#>
#> Expansion Successful
#>
#> Final analysis dataset: 814,689 observations, 22 variables
#>
#> Moving forward with censoring analysis
#>
#> censoring model created successfully
#>
#> Creating Survival curves
#>
#> Completed
km_curve(model, plot.type = "risk")
risk_data(model)
#> Index: <Followup>
#> Method Followup A Risk
#> <char> <num> <char> <num>
#> 1: censoring 60 0 0.01707101
#> 2: censoring 60 1 0.02094034
risk_comparison(model)
#> Followup A_x A_y Risk Ratio Risk Difference
#> <num> <fctr> <fctr> <num> <num>
#> 1: 60 inc_0 inc_1 1.2266611 0.003869334
#> 2: 60 inc_1 inc_0 0.8152211 -0.003869334Per-protocol, censoring, weights in post-expanded data and no truncation, excused conditions for initiators and non-initiators (i.e. dynamic interventions) and hazard ratio
options <- SEQopts(# tell SEQuential to run hazard ratios
hazard = TRUE,
weighted = TRUE,
weight.preexpansion = FALSE,
excused = TRUE,
excused.cols = c("excusedZero", "excusedOne"),
weight.p99 = TRUE)
data <- SEQdata
model <- SEQuential(data,
id.col = "ID",
time.col = "time",
eligible.col = "eligible",
treatment.col = "tx_init",
outcome.col = "outcome",
time_varying.cols = c("N", "L", "P"),
fixed.cols = "sex",
method = "censoring",
options = options)
#>
#> Full dataset: 12,180 observations, 11 variables
#>
#> Original dataset (eligible subjects): 9,203 observations, 11 variables
#>
#> Expanding Data...
#>
#> Pre-filter expansion: 310,080 observations
#>
#> Expanded dataset (pre-censoring): 248,485 observations, 21 variables
#>
#> Expanded dataset (post-censoring): 188,475 observations, 21 variables
#> entering outcome model (uncensored): 186,259
#> artificially censored (treatment switch): 2,216
#>
#> Expansion Successful
#>
#> Final analysis dataset: 188,475 observations, 21 variables
#>
#> Moving forward with censoring analysis
#>
#> Completed
hazard_ratio(model)
#> Hazard ratio LCI UCI
#> 1.170182 NA NA