Using the Inverse Normal Combination Test for Analyzing a Trial with Continuous Endpoint and Potential Sample Size Re-Assessment with rpact

Analysis
Means
This document provides an example for analysing trials with a continuous endpoint and sample size reassessment using rpact.
Author
Published

February 16, 2024

Define the type of design to be used for the analysis

First, load the rpact package

library(rpact)
packageVersion("rpact") 
[1] '4.0.0'

In this vigentte, we want to illustrate a design where at interim stages we are able to perform data-driven sample size adaptations. For this purpose, we use the inverse normal combination test for combining the \(p\)-values from the stages of the trial. This type of design ensures that the Type I error rate is controlled.

We want to use a three stage design with O`Brien and Fleming boundaries and additionally want to consider futility bounds -0.5 and 0.5 for the test statistics at the first and the second stage, respectively. Accordingly,

# Example of an inverse normal combination test:
designIN <- getDesignInverseNormal(futilityBounds = c(-0.5, 0.5))

defines the design to be used for this purpose. By default, this is a design with equally spaced information rates and one sided \(\alpha = 0.025\). The critical values can be displayed on the \(z\)-value or the \(p\)-value scale:

plot(designIN, type = 1)

plot(designIN, type = 3)

Note that we are using non-binding futility bounds that do not affect the boundaries for the rejection of the null hypothesis. It does have an effect, however, on the calculation of the conditional power at interim stages.

By the use of the function getDesignInverseNormal() the way how to analyse the data is fixed. With this definition, the unweighted inverse normal combination test is used, i.e., the stage results are combined through the use on the inverse normal combination test statistics

\[\frac{\Phi^{-1}(1 - p_1) + \Phi^{-1}(1 - p_2)}{\sqrt{2}} \text{ and }\frac{\Phi^{-1}(1 - p_1) + \Phi^{-1}(1 - p_2)+ \Phi^{-1}(1 - p_3)}{\sqrt{3}}\]

for the second and the third (final) stage of the trial, respectively.

Entering the data

In rpact, the way of using data for adaptive analysis is through summary statistics that summarize the data from the separate stages. Generally, the function getDataset() is used and depending on which summary statistics are entered, rpact knows the type of endpoint and the number of treatment groups. For testing means in a two-treatment parallel group design, means1, means2, stDevs1, stDevs2, n1, and n2 must be defined as vectors with length number of the observed interim stages.

As an example, assume that the following results in the control and the experimental treatment arm were obtained for the first and the second stage of the trial.

First stage:

arm n mean std
experimental 34 112.3 44.4
control 37 98.1 46.7

Second stage:

arm n mean std
experimental 31 113.1 42.9
control 33 99.3 41.1

Here, sample size, mean and standard deviation were obtained separately from the stages. Enter these results as follows to obtain a dataset object in rpact:

datasetExample <- getDataset(
    means1 = c(112.3, 113.1),
    means2 = c(98.1, 99.3),
    stDevs1 = c(44.4, 42.9),
    stDevs2 = c(46.7, 41.1),
    n1 = c(34, 31),
    n2 = c(37, 33)
)

The object datasetExample also contains the cumulative results which were calculated from the separate stage results:

stages groups sampleSizes means stDevs overallSampleSizes overallMeans overallStDevs
1 1 34 112.3 44.4 34 112.30000 44.40000
1 2 37 98.1 46.7 37 98.10000 46.70000
2 1 31 113.1 42.9 65 112.68154 43.35132
2 2 33 99.3 41.1 70 98.66571 43.83630

You might alternatively enter the cumulative results by specifying
cumulativeMeans1, cumulativeMeans2, cumulativeStDevs1, cumulativeStDevs2, cumulativeN1, and cumulativeN2 (note that you can also use the prefix cum instead of cumulative for this):

kable(getDataset(
    cumulativeMeans1 = c(112.3, 112.68),
    cumulativeMeans2 = c(98.1, 98.67),
    cumulativeStDevs1 = c(44.4, 43.35),
    cumulativeStDevs2 = c(46.7, 43.84),
    cumulativeN1 = c(34, 65),
    cumulativeN2 = c(37, 70)
))
stages groups sampleSizes means stDevs overallSampleSizes overallMeans overallStDevs
1 1 34 112.30000 44.40000 34 112.30 44.40
1 2 37 98.10000 46.70000 37 98.10 46.70
2 1 31 113.09677 42.89719 65 112.68 43.35
2 2 33 99.30909 41.10836 70 98.67 43.84

Analysis results

The easiest way to obtain the analysis results is through the function getAnalysisResults(), where design and dataInput needs to be specified. In our case,

results <- getAnalysisResults(design = designIN, dataInput = datasetExample, stage = 2)

does the job, and the output is

normalApproximation directionUpper equalVariances assumedStDev testActions conditionalRejectionProbabilities repeatedConfidenceIntervalLowerBounds repeatedConfidenceIntervalUpperBounds repeatedPValues
FALSE TRUE TRUE 43.6036 continue 0.0676674 -25.271358 53.67136 0.2977556
FALSE TRUE TRUE 43.6036 continue 0.1912050 -4.802969 32.79790 0.0785407
FALSE TRUE TRUE 43.6036 NA NA NA NA NA

Displayed as summary:

kable(summary(results))
Warning in is.na(parameterValues): is.na() auf Nicht-(Liste oder Vektor) des
Typs 'environment' angewendet
object NA NA NA NA NA NA NA NA
FALSE TRUE TRUE 43.6036 continue 0.0676674 -25.271358 53.67136 0.2977556
FALSE TRUE TRUE 43.6036 continue 0.1912050 -4.802969 32.79790 0.0785407
FALSE TRUE TRUE 43.6036 NA NA NA NA NA

We note that the variable stage needs not to be specified because it is actually obtained from the data. You might, however, specify stage = 1 in order to obtain the results for the first stage only.

The inverse normal combination test statistic is calculated to be 1.837 which is smaller than 2.454. Hence, the hypothesis cannot be rejected, which is also reflected in the repeated \(p\)-value = 0.0785 larger than \(\alpha = 0.025\) and the lower bound of the repeated confidence interval (= -4.803) being smaller than 0 (i.e., the RCI contains the null hypothesis value).

Reassessing the sample size for the last stage

The conditional power is calculated if nPlanned is specified. nPlanned contains the sample size of the remaining separate stages for both treatment groups and is a vector with that length. For example, if we specify nPlanned = 60, the conditional power is calculated for a total of 60 patients in the 2 treatment groups for the final stage with equal allocation between the treatment groups (the latter can be changed with allocationRatioPlanned which is 1 by default).

results <- getAnalysisResults(
    design = designIN, datasetExample,
    stage = 2, nPlanned = 60
)

yields the following output:

nPlanned normalApproximation directionUpper equalVariances assumedStDev testActions conditionalRejectionProbabilities conditionalPower repeatedConfidenceIntervalLowerBounds repeatedConfidenceIntervalUpperBounds repeatedPValues
NA FALSE TRUE TRUE 43.6036 continue 0.0676674 NA -25.271358 53.67136 0.2977556
NA FALSE TRUE TRUE 43.6036 continue 0.1912050 NA -4.802969 32.79790 0.0785407
60 FALSE TRUE TRUE 43.6036 NA NA 0.6448514 NA NA NA

The conditional power 0.645 is not very large and so a sample size increase might be appropriate. This conditional power calculation, however, is performed with the observed effect and observed standard deviation and so it might be reasonable to take a look at the effect size and its variability. This is graphically illustrated by the plot of the conditional power and the likelihood function over a range of alternative values. E.g., specify thetaRange = c(0,30) and obtain the graph below.

plot(results, thetaRange = c(0, 30))

You can also specify an alternative effect and standard deviation (e.g., thetaH1 = 15, assumedStDev = 35) for which the conditional power should be calculated, and graph the results. Here, the function getConditionalPower() together with getStageResults() are used but you obtain the same result when specifying thetaH1 = 15, assumedStDev = 35 in getAnalysisResults().

stageResults <- getStageResults(design = designIN, dataInput = datasetExample)

kable(getConditionalPower(stageResults,
    nPlanned = 60, thetaH1 = 15,
    assumedStDev = 35
))
nPlanned allocationRatioPlanned conditionalPower thetaH1 assumedStDev
NA 1 NA 15 35
NA 1 NA 15 35
60 1 0.7841792 15 35
plot(stageResults,
    nPlanned = 60, thetaRange = c(0, 30),
    assumedStDev = 35
)

Overall, using a slightly smaller standard deviation than observed, the conditional power calculated with the originally planned sample size seems to be reasonably high.

Final analysis

Assume now that it was decided to continue with the originally planned sample size ( = 60 per stage) and the final stage shows the results:

Final stage:

arm n mean std
experimental 32 111.3 41.4
control 31 100.1 39.5

We obtain the test results for the final stage and show it as a summary output as follows:

datasetExample <- getDataset(
    means1 = c(112.3, 113.1, 111.3),
    means2 = c(98.1, 99.3, 100.1),
    stDevs1 = c(44.4, 42.9, 41.4),
    stDevs2 = c(46.7, 41.1, 39.5),
    n1 = c(34, 31, 32),
    n2 = c(37, 33, 31)
)

kable(summary(getAnalysisResults(design = designIN, dataInput = datasetExample)))
Calculation of final confidence interval performed for kMax = 3 (for kMax > 2, it is theoretically shown that it is valid only if no sample size change was performed)
Warning in is.na(parameterValues): is.na() auf Nicht-(Liste oder Vektor) des
Typs 'environment' angewendet
object NA NA NA NA NA NA NA NA NA NA NA NA
FALSE TRUE TRUE 42.43211 continue 0.0676674 -25.271358 53.67136 0.2977556 NA NA NA NA
FALSE TRUE TRUE 42.43211 continue 0.1912050 -4.802969 32.79790 0.0785407 NA NA NA NA
FALSE TRUE TRUE 42.43211 reject NA 0.767624 25.30962 0.0182781 0.0196787 0.620916 24.51941 12.61984

The warning indicates that the calculation of the final CI can be critical if sample size changes were performed. This is not the case here, and so the confidence interval that is based on the stagewise ordering given by (0.621; 24.519) is a valid inference tool. Together with the RCI at stage 3 (0.768; 25.31) it corresponds to the test decision of rejecting the null hypothesis. The test decision is also reflected in both the final \(p\)-value and repeated \(p\)-value being smaller than \(\alpha = 0.025\).

References

  1. Gernot Wassmer and Werner Brannath, Group Sequential and Confirmatory Adaptive Designs in Clinical Trials, Springer 2016, ISBN 978-3319325606

System: rpact 4.0.0, R version 4.3.3 (2024-02-29 ucrt), platform: x86_64-w64-mingw32

To cite R in publications use:

R Core Team (2024). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing, Vienna, Austria. https://www.R-project.org/. To cite package ‘rpact’ in publications use:

Wassmer G, Pahlke F (2024). rpact: Confirmatory Adaptive Clinical Trial Design and Analysis. R package version 4.0.0, https://www.rpact.com, https://github.com/rpact-com/rpact, https://rpact-com.github.io/rpact/, https://www.rpact.org.