This R Markdown document provides an example
for updating the group sequential boundaries when using an \(\alpha\)-spending
function approach based on observed information rates in
rpact. Since version 3.1 of
rpact, an additional option in the
`getAnalysisResults()`

function provides an easy way to perform an analysis with
critical values that are calculated subsequently during the stages of the
trial.

Group-sequential designs based on \(\alpha\)-spending functions protect the Type I error exactly even if the pre-planned interim schedule is not exactly adhered to. However, this requires re-calculation of the group sequential boundaries at each interim analysis based on actually observed information fractions. Unless deviations from the planned information fractions are substantial, the re-calculated boundaries are quite similar to the pre-planned boundaries and the re-calculation will affect the actual test decision only on rare occasions.

Importantly, it is not allowed that the timing of future interim analyses is “motivated” by results from earlier interim analyses as this could inflate the Type I error rate. Deviations from the planned information fractions should thus only occur due to operational reasons (as it is difficult to hit the exact number of events exactly in a real trial) or due to external evidence.

The general principles for these boundary re-calculation are as follows (see also, Wassmer & Brannath, 2016, p78f):

- Updates at interim analyses prior to the final analysis:
- Information fractions are updated according to the actually observed
information fraction at the interim analysis relative to the
**planned**maximum information. - The planned \(\alpha\)-spending function is then applied to these updated information fractions.

- Information fractions are updated according to the actually observed
information fraction at the interim analysis relative to the
- Updates at the final analysis in case the observed information at the final
analysis is larger (“over-running”) or smaller (“under-running”) than the
planned maximum information:
- Information fractions are updated according to the actually observed
information fraction at all interim analyses relative to the
**observed**maximum information. \(\Rightarrow\) Information fraction at final analysis is re-set to 1 but information fractions for earlier interim analyses are also changed. - The originally planned \(\alpha\)-spending function cannot be applied to these updated information fractions because this would modify the critical boundaries of earlier interim analyses which is clearly not allowed. Instead, one uses the \(\alpha\) that has actually been spent at earlier interim analyses and spends all remaining \(\alpha\) at the final analysis.

- Information fractions are updated according to the actually observed
information fraction at all interim analyses relative to the

This general principle be implemented via a user-defined \(\alpha\)-spending
function and is illustrated for an example trial with a survival endpoint
below. We provide two solutions to the problem: the first is a way how existing
tools in rpact can directly be used
to solve the problem, the second is an automatic recalculation of the boundaries
using a new parameter set (`maxInformation`

and `informationEpsilon`

) which is
available in the `getAnalysisResults()`

function since
rpact version 3.1. This solution is
described in Section 7 at the end of this document.

**First, load the rpact package **

```
library(rpact)
packageVersion("rpact") # version should be version 3.1 or later
```

`## [1] '3.3.2'`

The original trial design for this example is based on a standard O’Brien & Fleming type \(\alpha\)-spending function with planned efficacy interim analyses after 50% and 75% of information as specified below.

```
# Initial design
<- getDesignGroupSequential(
design sided = 1, alpha = 0.025, beta = 0.2,
informationRates = c(0.5, 0.75, 1), typeOfDesign = "asOF"
)
# Initial sample size calculation
<- getSampleSizeSurvival(
sampleSizeResult design = design,
lambda2 = log(2) / 60, hazardRatio = 0.75,
dropoutRate1 = 0.025, dropoutRate2 = 0.025, dropoutTime = 12,
accrualTime = 0, accrualIntensity = 30,
maxNumberOfSubjects = 1000
)
# Summarize design
kable(summary(sampleSizeResult))
```

**Sample size calculation for a survival endpoint**

Sequential analysis with a maximum of 3 looks (group sequential design), overall significance level 2.5% (one-sided). The sample size was calculated for a two-sample logrank test, H0: hazard ratio = 1, H1: hazard ratio = 0.75, control lambda(2) = 0.012, maximum number of subjects = 1000, accrual time = 33.333, accrual intensity = 30, dropout rate(1) = 0.025, dropout rate(2) = 0.025, dropout time = 12, power 80%.

Stage | 1 | 2 | 3 |
---|---|---|---|

Information rate | 50% | 75% | 100% |

Efficacy boundary (z-value scale) | 2.963 | 2.359 | 2.014 |

Overall power | 0.1680 | 0.5400 | 0.8000 |

Expected number of subjects | 1000.0 | ||

Number of subjects | 1000.0 | 1000.0 | 1000.0 |

Cumulative number of events | 193.4 | 290.1 | 386.8 |

Analysis time | 39.1 | 52.7 | 69.1 |

Expected study duration | 58.0 | ||

Cumulative alpha spent | 0.0015 | 0.0096 | 0.0250 |

One-sided local significance level | 0.0015 | 0.0092 | 0.0220 |

Efficacy boundary (t) | 0.653 | 0.758 | 0.815 |

Exit probability for efficacy (under H0) | 0.0015 | 0.0081 | |

Exit probability for efficacy (under H1) | 0.1680 | 0.3720 |

Legend:

*(t)*: treatment effect scale

Assume that the first interim was conducted after 205 rather than the planned 194 events.

The updated design is calculated as per the code below. Note that for the
calculation of boundary values on the treatment effect scale, we use the
function `getPowerSurvival()`

with the updated design rather than the function
`getSampleSizeSurvival()`

as we are only updating the boundary, not the sample
size or the maximum number of events.

```
# Update design using observed information fraction at first interim.
# Information fraction of later interim analyses is not changed.
<- getDesignGroupSequential(
designUpdate1 sided = 1, alpha = 0.025, beta = 0.2,
informationRates = c(205 / 387, 0.75, 1), typeOfDesign = "asOF"
)
# Recalculate the power to get boundary values on the effect scale
# (Use original maxNumberOfEvents and sample size)
<- getPowerSurvival(
powerUpdate1 design = designUpdate1,
lambda2 = log(2) / 60, hazardRatio = 0.75,
dropoutRate1 = 0.025, dropoutRate2 = 0.025, dropoutTime = 12,
accrualTime = 0, accrualIntensity = 30,
maxNumberOfSubjects = 1000, maxNumberOfEvents = 387, directionUpper = FALSE
)
```

The updated information rates and corresponding boundaries as per the output above are summarized as follows:

**Power calculation for a survival endpoint**

Sequential analysis with a maximum of 3 looks (group sequential design), overall significance level 2.5% (one-sided). The results were calculated for a two-sample logrank test, H0: hazard ratio = 1, power directed towards smaller values, H1: hazard ratio = 0.75, control lambda(2) = 0.012, maximum number of subjects = 1000, maximum number of events = 387, accrual time = 33.333, accrual intensity = 30, dropout rate(1) = 0.025, dropout rate(2) = 0.025, dropout time = 12.

Stage | 1 | 2 | 3 |
---|---|---|---|

Information rate | 53% | 75% | 100% |

Efficacy boundary (z-value scale) | 2.867 | 2.366 | 2.015 |

Overall power | 0.2097 | 0.5391 | 0.8001 |

Expected number of subjects | 1000.0 | ||

Number of subjects | 1000.0 | 1000.0 | 1000.0 |

Expected number of events | 317.0 | ||

Cumulative number of events | 205.0 | 290.2 | 387.0 |

Analysis time | 40.6 | 52.7 | 69.1 |

Expected study duration | 57.8 | ||

Cumulative alpha spent | 0.0021 | 0.0096 | 0.0250 |

One-sided local significance level | 0.0021 | 0.0090 | 0.0220 |

Efficacy boundary (t) | 0.670 | 0.758 | 0.815 |

Exit probability for efficacy (under H0) | 0.0021 | 0.0076 | |

Exit probability for efficacy (under H1) | 0.2097 | 0.3294 |

Legend:

*(t)*: treatment effect scale

Assume that the efficacy boundary was not crossed at the first interim analysis and the trial continued to the second interim analysis which was conducted after 285 rather than the planned 291 events. The updated design is calculated in the same way as for the first interim analysis as per the code below. The idea is to use the cumulative \(\alpha\) spent from the first stage and an updated cumulative \(\alpha\) that is spent for the second stage. For the second stage, this can be obtained with the original O’Brien & Fleming \(\alpha\)-spending function:

```
# Update design using observed information fraction at first and second interim.
<- getDesignGroupSequential(
designUpdate2 sided = 1, alpha = 0.025, beta = 0.2,
informationRates = c(205 / 387, 285 / 387, 1), typeOfDesign = "asOF"
)
# Recalculate power to get boundary values on effect scale
# (Use original maxNumberOfEvents and sample size)
<- getPowerSurvival(
powerUpdate2 design = designUpdate2,
lambda2 = log(2) / 60, hazardRatio = 0.75,
dropoutRate1 = 0.025, dropoutRate2 = 0.025, dropoutTime = 12,
accrualTime = 0, accrualIntensity = 30,
maxNumberOfSubjects = 1000, maxNumberOfEvents = 387, directionUpper = FALSE
)kable(summary(powerUpdate2))
```

**Power calculation for a survival endpoint**

Sequential analysis with a maximum of 3 looks (group sequential design), overall significance level 2.5% (one-sided). The results were calculated for a two-sample logrank test, H0: hazard ratio = 1, power directed towards smaller values, H1: hazard ratio = 0.75, control lambda(2) = 0.012, maximum number of subjects = 1000, maximum number of events = 387, accrual time = 33.333, accrual intensity = 30, dropout rate(1) = 0.025, dropout rate(2) = 0.025, dropout time = 12.

Stage | 1 | 2 | 3 |
---|---|---|---|

Information rate | 53% | 73.6% | 100% |

Efficacy boundary (z-value scale) | 2.867 | 2.393 | 2.011 |

Overall power | 0.2097 | 0.5198 | 0.8004 |

Expected number of subjects | 1000.0 | ||

Number of subjects | 1000.0 | 1000.0 | 1000.0 |

Expected number of events | 317.2 | ||

Cumulative number of events | 205.0 | 285.0 | 387.0 |

Analysis time | 40.6 | 51.9 | 69.1 |

Expected study duration | 57.8 | ||

Cumulative alpha spent | 0.0021 | 0.0090 | 0.0250 |

One-sided local significance level | 0.0021 | 0.0084 | 0.0222 |

Efficacy boundary (t) | 0.670 | 0.753 | 0.815 |

Exit probability for efficacy (under H0) | 0.0021 | 0.0069 | |

Exit probability for efficacy (under H1) | 0.2097 | 0.3101 |

Legend:

*(t)*: treatment effect scale

<