Applies a treatment-switching rule to simulated trial data, such as the
output of simdata_fast, by changing only the outcomes that
occur after each subject's switch. Subjects of the selected groups switch
either at their intermediate event (for example disease progression), at
an opening time after an interim analysis (crossover at a milestone), or at
the later of the two, and the remaining time to the terminal event after the
switch is either multiplied by an acceleration factor or redrawn from a new
(piecewise) exponential hazard. All simulated trials are processed at once
with vectorized operations. Because the outcomes observed before the switch
are kept exactly, an analysis at or before the opening time gives the same
result before and after switching, so a crossover that depends on an interim
result can be simulated by analyzing the interim, deciding per simulated
trial, and switching afterward.
Usage
switch_fast(
data,
group,
prob = 1,
when = c("intermediate", "cutoff", "later"),
cutoff = NULL,
delay = 0,
sims = NULL,
aft.factor = NULL,
hazard = NULL,
median = NULL,
time = NULL,
seed = NULL,
stream = NULL
)Arguments
- data
A data frame from
simdata_fast(single-endpoint, multi-arm, or illness-death), containing at leastsim,group,accrual_time, and the latent columns described in Details.- group
A vector of group labels whose subjects may switch, for example the control group
1.- prob
A single probability in [0, 1] that an eligible subject switches. Defaults to 1.
- when
A character string, one of
"intermediate","cutoff", and"later", giving the switch time (see Details).- cutoff
Per-simulation calendar times at which switching opens, used with
when = "cutoff"or"later". Either a numeric vector with one element per simulated trial or a one-column matrix fromcutoff_fast. As in thecutoff.looksargument ofanalysis_fast, the names of a vector (or the row names of a matrix) are matched to the values ofdata$sim; without names the elements are taken in the order of the sorted distinct values ofdata$sim.NAdisables switching in that simulated trial.- delay
A single non-negative number added to
cutoff, for example the time needed to implement the crossover after an interim analysis. Defaults to 0.- sims
An optional logical vector with one element per simulated trial (in the order of the sorted distinct values of
data$sim). Switching is applied only in the simulated trials withTRUE. Requireswhen = "cutoff"or"later".- aft.factor
A single positive acceleration factor applied to the remaining time after the switch. Exactly one of
aft.factor,hazard, andmedianmust be supplied.- hazard
Post-switch hazard, a single value or a vector for a piecewise exponential distribution with breakpoints
time, measured from the switch.- median
A single post-switch median; an alternative to a single
hazard.- time
Breakpoints for a piecewise
hazard, starting at 0 and ending withInf, measured from the switch.- seed
Optional integer seed for the
dqrnggenerator. IfNULL(default), the draws come from the current state of that generator, whichset.seed()does not control; supplyseedfor reproducible results (seesimdata_fast).- stream
Optional non-negative whole number selecting a
dqrngstream; requiresseed. Seesimdata_fast.
Value
The data frame data with the latent and observed columns of
the switched subjects updated, and the columns switched (0 or 1)
and switch_time (time from accrual to the switch, NA for
subjects who did not switch) added or updated.
Details
The function works on the latent columns of simdata_fast. For
a single-endpoint simulation these are surv_time and
dropout_time; for an illness-death simulation they are
e1_surv_time (time to the first event, such as progression-free
survival), e2_surv_time (time to the terminal event, such as overall
survival), dropout_time, and intermediate. The observed
columns (tte, event, and calendar_time, or their
e1_ and e2_ counterparts) are recomputed from the modified
latent times, and the columns switched and switch_time (time
from accrual to the switch) record the switches. The columns sim,
group, accrual_time, and the subgroup columns are unchanged.
The switch time s, measured from accrual, is
when = "intermediate": the intermediate event timee1_surv_timeof a subject whose intermediate event occurred (intermediate == 1); illness-death data only;when = "cutoff": the opening timecutoff + delayof the subject's simulated trial minus the accrual time (zero for a subject accrued after the opening);when = "later": the later of the intermediate event time and the opening time, for a subject whose intermediate event occurred; illness-death data only.
A subject of the selected group switches with probability
prob when the switch occurs before the terminal event and before
dropout (s < e2_surv_time, or s < surv_time for
single-endpoint data, and s < dropout_time) and the subject has not
already switched (switched == 1, for example at progression through
the switch.prop argument of simdata_fast). With when = "cutoff" or "later", the switch
never occurs before the opening time, so every outcome observed by calendar
time cutoff + delay is left unchanged; subjects accrued after the
opening are also eligible, as in a protocol that opens crossover from that
time on.
The effect of switching on the terminal time T > s is
aft.factor = f:T_new = s + f * (T - s), the causal accelerated failure time model of the rank-preserving structural failure time (RPSFT) method, under whichf > 1prolongs the remaining survival;hazard(ormedian):T_new = s + R, where the remaining timeRis drawn from the (piecewise) exponential distribution with hazardhazardand breakpointstimemeasured from the switch.
In illness-death data with when = "cutoff", a switch can occur before
the intermediate event. The acceleration factor is then applied in the same
way to the first-event time when it lies after the switch, which keeps the
first event no later than the terminal event; a redrawn hazard is not
available in this case because the latent time to the intermediate event
after the terminal event is not stored.
sims restricts the switching to some simulated trials, for example
those in which the interim analysis crossed an efficacy boundary. It is
available with when = "cutoff" or "later" only, so that the
decision cannot change outcomes observed before it is made.
The random numbers (one uniform per row when prob < 1, and one
exponential per row when hazard or median is used) are drawn
with dqrng, in row order, so the result is reproducible from
seed and stream, or from the state left by a seeded
simdata_fast call.
References
Robins, J. M., & Tsiatis, A. A. (1991). Correcting for non-compliance in randomized trials using rank preserving structural failure time models. Communications in Statistics - Theory and Methods, 20(8), 2609-2631.
Examples
# Crossover at an interim analysis: control subjects still on study at the
# 150th event switch to the experimental treatment, which multiplies their
# remaining survival time by 1.5.
df <- simdata_fast(nsim = 50, n = c(150, 150), a.time = c(0, 12),
a.rate = 300 / 12, e.median = list(12, 18),
d.hazard = 0.01, seed = 1)
ia <- cutoff_fast(df, event.looks = 150)
dfs <- switch_fast(df, group = 1, when = "cutoff", cutoff = ia,
aft.factor = 1.5)
mean(dfs$switched[dfs$group == 1])
#> [1] 0.3404
# The interim analysis is unchanged; the final analysis is diluted.
r0 <- analysis_fast(df, control = 1, cutoff.looks = ia)
r1 <- analysis_fast(dfs, control = 1, cutoff.looks = ia)
all.equal(r0$logrank.z, r1$logrank.z)
#> [1] TRUE
fa0 <- analysis_fast(df, control = 1, event.looks = 220)
fa1 <- analysis_fast(dfs, control = 1, event.looks = 220)
c(mean(fa0$logrank.z), mean(fa1$logrank.z))
#> [1] -3.078266 -2.112271
# Switching at progression in an illness-death simulation: 50 percent of
# the control subjects who progress switch, with a post-progression median
# of 15 instead of 10.
id <- simdata_fast(nsim = 50, n = c(150, 150), a.time = c(0, 12),
a.rate = 300 / 12, h01.median = list(6, 9),
h02.median = list(20, 24), h12.median = list(10, 10),
seed = 2)
ids <- switch_fast(id, group = 1, prob = 0.5, when = "intermediate",
median = 15)
table(ids$group, ids$switched)
#>
#> 0 1
#> 1 4587 2913
#> 2 7500 0
