This tutorial provides two examples:
For a general introduction to “Inference in group sequential designs”, please refer to the book “Group Sequential and Confirmatory Adaptive Designs in Clinical Trials” by Gernot Wassmer & Werner Brannath.
This tutorial only covers survival endpoints. Code for other
endpoints is similar but the dataset needs to be provided in a different
?getDataset for details).
For details about the Gallium trial, we refer to the primary study publication: Marcus et al, N Engl J Med 2017; 377:1331-1344.
Results from standard inference at the futility interim analysis after 113 events:
Results from standard inference at the efficacy interim analysis after 245 events:
First, load the rpact package and
define the group sequential boundaries using the
getDesignGroupSequential. Note that while the
Gallium protocol specified a two-sided significance level of 5%, we
implement this via a one-sided significance level of 2.5% as rpact (sensibly)
only supports one-sided designs if futility interim analyses are
First, load the rpact package
library(rpact) packageVersion("rpact") # version should be version 2.0.5 or later
##  '3.3.2'
and define the design:
# FutilityBounds = c(0,-6) are on the Z-scale; a value of Z = 0 implies futility # if the interim estimate is "in the wrong direction" (i.e., HR >= 1 here), # a value of Z = -6 is essentially the same as Z = -Inf and implies no futility # boundary for the second interim as per the Gallium design <- getDesignGroupSequential( design informationRates = c(113, 245, 370) / 370, typeOfDesign = "asOF", sided = 1, alpha = 0.025, futilityBounds = c(0, -6), bindingFutility = FALSE )
bindingFutility = FALSE has no impact because
it is the default, so actually this could be omitted (same holds for
sided = 1 and
alpha = 0.025).
Second, the results after the first and second
interim are specified using the function
# overallLogRanks: One-sided logrank statistic or Z-score ( = log(HR)/SE) from Cox regression <- getDataSet( results overallEvents = c(113, 245), overallLogRanks = c(-1.86, -3.225), overallAllocationRatio = c(1, 1) )
Since rpact version 3.2, the prefix cumulative[Capital case of first letter of variable name]… or cum[Capital case of first letter of variable name]… can alternatively be used for this. That is,
<- getDataSet( results cumulativeEvents = c(113, 245), cumulativeLogRanks = c(-1.86, -3.225), cumulativeAllocationRatio = c(1, 1) )
defines the same survival dataset. This is used for creating the
adjusted inference using the function
directionUpper = FALSE
is specified because the power is directed towards negative values of
the logrank statistics):
<- getAnalysisResults( adj_result design = design, dataInput = results, directionUpper = FALSE )kable(adj_result)
Analysis results (survival of 2 groups, group sequential design)
User defined parameters
The output is explained as follows:
Critical valuesare group sequential efficacy boundary values on the \(z\)-scale,
Local one-sided significance levelsare the corresponding one-sided local significance levels.
Cumulative effect sizesrefer to hazard ratio estimates that are based on the overall test statistics.
p-valuesrefer to \(z\)-scores and \(p\)-values obtained from the first interim analysis and results which would have been obtained after the second interim analysis if not all data up to the second interim analysis but only new data since the first interim had been included (i.e., per-stage results).
Overall test statisticsare the given (overall, not per-stage) \(z\)-scores from each interim and
Overall p-valuethe corresponding one-sided \(p\)-values.
Repeated confidence intervalsprovide valid (but conservative) inference at any stage of an ongoing or stopped group sequential trial.
Repeated p-valuesare the corresponding \(p\)-values.
Final p-valueis the final one-sided adjusted \(p\)-value based on the stagewise ordering of the sample space.
Median unbiased estimateand
Final CIsare the corresponding median-unbiased adjusted treatment effect estimate and the confidence interval for the hazard ratio at the interim analysis where the trial was stopped.
These results can also be displayed with the
Analysis results for a survival endpoint
Sequential analysis with 3 looks (group sequential design). The results were calculated using a two-sample logrank test (one-sided). H0: hazard ratio = 1 against H1: hazard ratio < 1.
|Efficacy boundary (z-value scale)||3.891||2.520||1.992|
|Futility boundary (z-value scale)||0||-Inf|
|Cumulative alpha spent||<0.0001||0.0059||0.0250|
|Cumulative effect size||0.705||0.662|
|Overall test statistic||-1.860||-3.225|
|Test action||continue||reject and stop|
|Conditional rejection probability||0.1373||0.8616|
|95% repeated confidence interval||[0.339; 1.465]||[0.480; 0.914]|
|Final confidence interval||[0.516; 0.852]|
|Median unbiased estimate||0.663|
Note that for this example, the adjusted final hazard ratio of 0.663 and the adjusted confidence interval of (0.516, 0.852) match the results from the conventional analysis almost exactly for the first two decimals. This is consistent with the finding that stopping a trial after 50% or more of the events had been collected has a negligible impact on estimation.
Monitoring ongoing trials is also possible with the function
getAnalysisResults introduced above. Repeated
confidence intervals which provide valid (but conservative)
inference at any stage of an ongoing or stopped group sequential trial
can be obtained using the same code as introduced in the previous
example. Conditional power calculations require
additional specification of the following arguments:
allocationRatioPlannedfor future interim stages (default is 1).
We illustrate these capabilities by introducing hypothetical interim results for the Gallium trial.
Assume the same design as for the Gallium trial introduced above and the following interim results:
Hypothetical results from standard inference at the futility interim analysis after 113 events:
Hypothetical results from standard inference at the efficacy interim analysis after 245 events:
Calculation of repeated confidence intervals and conditional power:
# 1) Specify results so far using function getDataset as before <- getDataset( results cumulativeEvents = c(113, 245), cumulativeLogRanks = c(-1.86, -1.716), cumulativeAllocationRatio = c(1, 1) ) # 2) Calculate repeated confidence intervals and conditional power using # the function getAnalysisResults as before # Additional arguments for the conditional power calculation are # - nPlanned: additional events from second interim until final analysis # (370-245 for this trial) # - thetaH1: True hazard ratio governing future stages # (set to 0.74 here as per the original protocol assumptions)