Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final CI for non-inferiority sequential design #36

Closed
fpahlke opened this issue May 22, 2024 · 1 comment
Closed

Final CI for non-inferiority sequential design #36

fpahlke opened this issue May 22, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@fpahlke
Copy link
Member

fpahlke commented May 22, 2024

Aim

We believe that there is something odd in the calculation of Final CIs at the first stage of a group sequential
design in RPACT for non-inferiority designs. We believe that this potential bug is only present for noninferiority
designs and at the first stage.

Our reasoning is based on the following:

  1. The results we get (Median unbiased estimate, Final CIs (lower + upper)) from a minimal reproducible
    example are different from what we would anticipate
  2. If we pretend that the design is not sequential, we should get CIs identical to the first stage Final CIs
    in a non-inferiority GSD context. But we do not.
  3. We get something else than the RPACT results when we calculate the confidence intervals by hand.
  4. Your source-code seems to adjust for the non-inferiority margin at all other interim stages and at the
    final analysis, but not the very first.

We wish with this document to illustrate the potential bug in RPACT for the first stage adjusted confidence
intervals and median unbiased estimate for a non-inferiority design.

Minimal reproducable example

library(rpact)

# our design
design <- getDesignGroupSequential(
    sided = 1, alpha = 0.025, beta = 0.1,
    informationRates = c(2/3, 1), typeOfDesign = "asOF"
)

# our fake data - create a scenario where we stop right at the boundary
fakeData2 <- getDataSet(
    overallEvents = c(1067), # number of events corresponding to 2/3s
    overallLogRanks = c(-1.713),
    overallAllocationRatio = c(1)
)
adjust_noninferiority <- getAnalysisResults(design = design,
    dataInput = fakeData2,
    directionUpper = F,
    thetaH0 = 1.05)

# We look at the final stage (first stage in this example) confidence intervals
adjust_noninferiority

Here we anticipated that the Final CIs upper boundary would be closer to 1.05 (thetaH0) and that the
Median unbiased estimate would be equal to the observed cumulative effect size.
To check that the CI is uncorrect we can use that the stage-wise confidence interval at the first stage should
be equal to the naively calculated confidence interval which we can get from the fixed design model.

Fixed design - no interim

If we assume that it was not a sequential design, we get the following confidence intervals:

# our design
design <- getDesignGroupSequential(
    sided = 1, alpha = 0.025, beta = 0.1,
    informationRates = c(1)
)

# creating data that stops right at the boundary
fakeData <- getDataSet(
    overallEvents = c(1067),
    overallLogRanks = c(-1.713),
    overallAllocationRatio = c(1)
)

# get the naive and adjusted inference
adjust_noninferirority <- getAnalysisResults(design = design,
    dataInput = fakeData,
    directionUpper = F,
    thetaH0 = 1.05)
adjust_noninferirority

Here the upper confidence interval crosses one and is different from the GSD final CI.

Calculating Final CIs (upper limit) by hand

We are going to find the value of μ = log(HR) for which the probability if 2.5% of being greater than
-1.713 (non-inf) or -2.5095 (superiority).

Starting with non-inf:

upper_ci_noninf <- function(x) pnorm(-1.713, mean = (log(x))*sqrt(1067)/2) - 0.025
uniroot(upper_ci_noninf, lower = 0.7, upper = 1.2)$root

We get an upper limit of 1.015 which is different than the RPACT result. But the same as the fixed design
CI.

Comparing non-inferiority to superiority

If the trial was a superiority trial instead, we could re-use the non-inferiority group sequential design but
without specifying the thetaH0 argument. Here we observe that if we reject the null hypothesis at the first
stage, we get identical Final CIs and Median unbiased estimate as in the non-inferiority analysis. However,
all other measures of inference are different in this scenario:

# superiority fake data - creating data that stops right at the boundary
fakeData <- getDataSet(
    overallEvents = c(1067),
    overallLogRanks = c(-2.5095),
    overallAllocationRatio = c(1)
)

# get the naive and adjusted inference
adjust_superiority <- getAnalysisResults(design = design,
    dataInput = fakeData,
    directionUpper = F)
adjust_superiority

Notice to do the analysis on the non-inferiority scale we needed to change the log-rank test to equal the
non-inferiority log-rank. Using the formula from Chen and Chen (Chen YH, Chen C. Testing superiority
at interim analyses in a non-inferiority trial. Stat Med. 2012 Jul 10;31(15):1531-42. doi: 10.1002/sim.5312.
Epub 2012 Mar 22. PMID: 22438208.)

We believe that the bug in RPACT is due to a missing adjustment of non-inferiority in the final CIs at the
first stage. Furthermore, we beleive that the non-inferiority analysis’ median unbiased estimate and final
CIs are incorrectly the same three measures of inference but based on a superiority trial.
This ends the arguments for the potential bug.

rpact_code

fpahlke added a commit that referenced this issue May 27, 2024
…ecalculation rules to the setting of binary endpoints according to [Bokelmann et al. (2024)](https://doi.org/10.1186/s12874-024-02150-4)

* The `getSimulationMultiArmMeans()`, `getSimulationMultiArmRates()`, and `getSimulationMultiArmSurvival()` functions now support an enhanced `selectArmsFunction` argument. Previously, only `effectVector` and `stage` were allowed as arguments. Now, users can optionally utilize additional arguments for more powerful custom function implementations, including `conditionalPower`, `conditionalCriticalValue`, `plannedSubjects/plannedEvents`, `allocationRatioPlanned`, `selectedArms`, `thetaH1` (for means and survival), `stDevH1` (for means), `overallEffects`, and for rates additionally: `piTreatmentsH1`, `piControlH1`, `overallRates`, and `overallRatesControl`.
* Same as below for`getSimulationEnrichmentMeans()`, `getSimulationEnrichmentRates()`, and `getSimulationEnrichmentSurvival()`. Specifically, support for population selection with `selectPopulationsFunction` argument based on predictive/posterior probabilities added (see [#32](#32))
* Issues [#25](#25), [#35](#35), and [#36](#36) fixed
* Minor improvements
@fpahlke fpahlke mentioned this issue May 28, 2024
@fpahlke
Copy link
Member Author

fpahlke commented May 28, 2024

Issue was fixed in branch dev/4.0.0

@fpahlke fpahlke closed this as completed May 28, 2024
@fpahlke fpahlke added the bug Something isn't working label Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant