Skip to contents

This vignette demonstrates how to use impart to conduct interim and final analyses in group sequential or information monitored designs. This builds upon the terminology and concepts in the vignettes on study design and information monitoring. To see all available vignettes in impart, use the vignettes command:

vignette(package = "impart")
Title Item

Setting Up a Monitored Design:

The design parameters in this vignette will be the same as in the previous vignettes:

# Universal Study Design Parameters
minimum_difference <- 5 # Effect Size: Difference in Means of 5 or greater
alpha <- 0.05 # Type I Error Rate
power <- 0.9 # Statistical Power
test_sides <- 2 # Direction of Alternatives

# Determine information required to achieve desired power at fixed error rate
information_single_stage <-
  impart::required_information_single_stage(
    delta = minimum_difference,
    alpha = alpha,
    power = power
  )

# Group Sequential Design Parameters
information_rates <-
  c(0.50, 0.75, 1.00) # Analyses at 50%, 75%, and 100% of the Total Information
type_of_design <- "asOF" # O'Brien-Fleming Alpha Spending
type_beta_spending <- "bsOF" # O'Brien-Fleming Beta Spending

# Set up group sequential testing procedure
trial_design <-
  rpact::getDesignGroupSequential(
    alpha = alpha,
    beta = 1 - power,
    sided = 2,
    informationRates = information_rates,
    typeOfDesign = type_of_design,
    typeBetaSpending = type_beta_spending,
    bindingFutility = FALSE
  )

# Inflate information level to account for multiple testing
information_adaptive <-
  impart::required_information_sequential(
    information_single_stage = information_single_stage,
    trial_design = trial_design
  )

# Initialize the monitored design
monitored_design <-
  initialize_monitored_design(
    trial_design = trial_design,
    null_value = 0,
    maximum_sample_size = 280,
    information_target = information_adaptive,
    orthogonalize = TRUE,
    rng_seed_analysis = 54321
  )

The data used in this example will also be the same: impart::example_1. Specifically, the data has been reverted to particular points in the study at which the information thresholds have been met:

  • example_1_ia_1: Data for Interim Analysis 1
  • example_1_ia_2: Data for Interim Analysis 2
  • example_1_final: Data for Final Analysis
# Data for Interim Analysis 1
example_1_ia_1 <- impart::example_1_ia_1

head(example_1_ia_1)
#>   .id        x_1        x_2        x_3        x_4 tx       .e .r_1     .t_1
#> 1   1  2.0742970  0.1971432 -0.8425884  0.2794844  0  2.24846    1 25.01538
#> 2   2  0.2165473 -0.7384296  0.1315016 -1.2419134  1 11.05565    0 55.05565
#> 3   3  0.8294726  0.4997821  1.6932555 -0.4063889  0 16.96591    0 60.96591
#> 4   4 -1.0206893 -0.2189937 -1.7719120  0.1936013  1 25.13396    1 59.84544
#> 5   5 -0.0417332  0.9282685  0.8078133  0.9317145  0 50.07301    1 75.94952
#> 6   6  0.7275778  1.1756811  0.0226265 -0.2556343  1 50.93935    1 80.29181
#>        y_1 .r_2      .t_2       y_2 .r_3      .t_3      y_3 .r_4     .t_4
#> 1 1.591873    1  56.62636 -4.535711    1  98.51499 13.98543    1 133.0050
#> 2       NA    0  85.05565        NA    0 115.05565       NA    0 145.0556
#> 3       NA    0  90.96591        NA    0 120.96591       NA    0 150.9659
#> 4 1.212620    1  81.58577 -4.533776    1 127.78659 11.17615    1 154.3419
#> 5 8.655326    1 111.21967  6.970372    1 143.00338 17.62329    1 181.4162
#> 6 6.902055    1 114.96781 17.381316    1 151.34249 -2.42570    1 177.3846
#>         y_4
#> 1 -1.320242
#> 2        NA
#> 3        NA
#> 4 -6.629545
#> 5  9.126240
#> 6  3.549977

Interim Analysis 1

Before conducting the first interim analysis, the information level should be above the pre-specified threshold: the smoothed trajectory can be used to mitigate random variation in the information level.

Analysts will need to specify estimation_function, the function used to compute the estimate, and estimation_arguments, a list of arguments aside from the data needed for this computation. Estimators may have a variance correction factor that can be computed from the analysis parameters: this can be specified using the correction_function argument.

For the standardization estimator computed by standardization(), the corresponding small-sample variance correction is standardization_df_adjust_tsiatis_2008(): this adjusts the variance according to the sample size in each arm and the number of parameters in each regression model. Note: the number of bootstraps used is decreased for the purposes of lower computational time.

# Obtain time of last event
last_event <-
  example_1_ia_1[, c(".e", ".t_1", ".t_2", ".t_3", ".t_4")] |>
  unlist() |>
  max(na.rm = TRUE) |>
  ceiling()

example_1_ia_1_prepared <-
  prepare_monitored_study_data(
    data = example_1_ia_1,
    study_time = last_event,
    id_variable = ".id",
    covariates_variables = c("x_1", "x_2", "x_3", "x_4"),
    enrollment_time_variable = ".e",
    treatment_variable = "tx",
    outcome_variables = c("y_1", "y_2", "y_3", "y_4"),
    outcome_time_variables = c(".t_1", ".t_2", ".t_3", ".t_4"), 
    # Observe missingness 1 week after target study visit
    observe_missing_times = c(30, 60, 90, 120) + 7
  )

data_ia_1_trajectory <- 
  information_trajectory(
    prepared_data = example_1_ia_1_prepared,
    monitored_design = monitored_design,
    estimation_function = standardization,
    estimation_arguments =
      list(
        estimand = "difference",
        outcome_formula_control = y_4 ~ x_1 + x_2 + x_3 + x_4,
        outcome_formula_treatment = y_4 ~ x_1 + x_2 + x_3 + x_4,
        family = gaussian,
        treatment_column = "tx"
      ),
    correction_function = standardization_df_adjust_tsiatis_2008,
    orthogonalize = TRUE,
    n_min = 50,
    n_increment = 3,
    rng_seed = 23456,
    control = impart::monitored_analysis_control(n_bootstrap = 1000)
  )

data_ia_1_trajectory
#>     event     time count_total count_complete count_events information
#> 565   y_4 551.8682          70             50           NA   0.1380683
#> 566   y_4 555.9573          71             50           NA   0.1339839
#> 572   y_4 595.6751          77             53           NA   0.1425721
#> 575   y_4 630.7461          80             56           NA   0.1482429
#> 578   y_4 634.3194          83             59           NA   0.1711911
#> 581   y_4 658.6659          86             62           NA   0.1696971
#> 584   y_4 662.6718          89             65           NA   0.1665782
#> 585   y_4 673.9776          90             65           NA   0.1633559
#> 589   y_4 701.1256          94             68           NA   0.1805331
#> 592   y_4 717.5104          97             71           NA   0.2098995
#> 595   y_4 753.7782         100             74           NA   0.2179450
#> 598   y_4 758.3298         103             77           NA   0.2021433
#> 602   y_4 778.4102         107             80           NA   0.2070840
#> 605   y_4 781.6646         110             83           NA   0.2181654
#>     information_lag_1 information_change information_pct_change
#> 565                NA                 NA                     NA
#> 566         0.1380683       -0.004084420             -3.0484414
#> 572         0.1339839        0.008588275              6.0238099
#> 575         0.1425721        0.005670759              3.8253158
#> 578         0.1482429        0.022948176             13.4050071
#> 581         0.1711911       -0.001493985             -0.8803831
#> 584         0.1696971       -0.003118900             -1.8723338
#> 585         0.1665782       -0.003222324             -1.9725794
#> 589         0.1633559        0.017177195              9.5147089
#> 592         0.1805331        0.029366413             13.9907033
#> 595         0.2098995        0.008045561              3.6915549
#> 598         0.2179450       -0.015801736             -7.8170963
#> 602         0.2021433        0.004940738              2.3858613
#> 605         0.2070840        0.011081351              5.0793349
#>     information_fraction
#> 565            0.3028952
#> 566            0.2939348
#> 572            0.3127758
#> 575            0.3252163
#> 578            0.3755602
#> 581            0.3722827
#> 584            0.3654404
#> 585            0.3583713
#> 589            0.3960547
#> 592            0.4604790
#> 595            0.4781294
#> 598            0.4434634
#> 602            0.4543024
#> 605            0.4786128

Once the trajectory has been computed, it can be smoothed and plotted:

plot(
  information ~ count_complete,
  data = data_ia_1_trajectory
)

abline(
  lm(
    formula = information ~ count_complete,
    data = data_ia_1_trajectory
  ),
  lty = 1
)

# Requires `deming` package
abline(
  deming::theilsen(
    formula = information ~ count_complete,
    data = data_ia_1_trajectory
  ),
  lty = 3
)

abline(
  h = monitored_design$original_design$information_thresholds,
  lty = 2
)

Once the information level has been confirmed to be above the threshold, conducting the analysis is similar to computing information:

interim_analysis_1 <-
  monitored_analysis(
    data = example_1_ia_1,
    monitored_design = monitored_design,
    estimation_function = standardization,
    estimation_arguments = 
      list(
        estimand = "difference",
        outcome_formula_control = y_4 ~ x_1 + x_2 + x_3 + x_4,
        outcome_formula_treatment = y_4 ~ x_1 + x_2 + x_3 + x_4,
        family = gaussian,
        treatment_column = "tx"
      ),
    correction_function = standardization_df_adjust_tsiatis_2008,
    control = impart::monitored_analysis_control(n_bootstrap = 1000)
  )

interim_analysis_1$interim_analysis_1$decision
#> [1] "Continue"
interim_analysis_1$interim_analysis_1$decision_data
#>   test_statistic efficacy  futility
#> 1       2.044012 2.930993 0.4236436
#> 2             NA 2.361025 1.2803287
#> 3             NA 2.014278        NA
interim_analysis_1$interim_analysis_1$information_fraction_orthogonal
#>           estimates
#> estimates 0.5095383

Interim Analysis 2

All subsequent analyses are identical in syntax: a new dataset is provided, and the result of the previous analysis is passed using the monitored_design argument.

example_1_ia_2 <- impart::example_1_ia_2

interim_analysis_2 <-
  monitored_analysis(
    data = example_1_ia_2,
    monitored_design = interim_analysis_1,
    estimation_function = standardization,
    estimation_arguments = 
      list(
        estimand = "difference",
        outcome_formula_control = y_4 ~ x_1 + x_2 + x_3 + x_4,
        outcome_formula_treatment = y_4 ~ x_1 + x_2 + x_3 + x_4,
        family = gaussian,
        treatment_column = "tx"
      ),
    correction_function = standardization_df_adjust_tsiatis_2008,
    control = impart::monitored_analysis_control(n_bootstrap = 1000)
  )

interim_analysis_2$interim_analysis_2$decision
#> [1] "Continue"
interim_analysis_2$interim_analysis_2$decision_data
#>   test_statistic efficacy  futility
#> 1       2.044012 2.930993 0.4311741
#> 2       2.190967 2.281878 1.4392127
#> 3             NA 2.025798        NA
interim_analysis_2$interim_analysis_2$information_fraction_orthogonal
#> [1] 0.5095383 0.7921346

Final Analysis

The syntax is identical for the final analysis:

example_1_final <- impart::example_1_final

final_analysis <-
  monitored_analysis(
    data = example_1_final,
    monitored_design = interim_analysis_2,
    estimation_function = standardization,
    estimation_arguments = 
      list(
        estimand = "difference",
        outcome_formula_control = y_4 ~ x_1 + x_2 + x_3 + x_4,
        outcome_formula_treatment = y_4 ~ x_1 + x_2 + x_3 + x_4,
        family = gaussian,
        treatment_column = "tx"
      ),
    correction_function = standardization_df_adjust_tsiatis_2008,
    control = impart::monitored_analysis_control(n_bootstrap = 1000)
  )

final_analysis$final_analysis$decision
#> [1] "Efficacy: Upper"
final_analysis$final_analysis$decision_data
#>   test_statistic efficacy futility
#> 1       2.044012 2.930993 0.271853
#> 2       2.190967 2.281878 1.199716
#> 3       2.199126 2.048468       NA
final_analysis$final_analysis$information_fraction_orthogonal
#> [1] 0.5095383 0.7921346 1.0956921