The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Classic and Batched Stepped-Wedge Designs

Amanda Li

A batched stepped-wedge design (BSWD) allows clusters to begin trial participation in batches with staggered initiation times. Consequently, some cluster-periods are outside the observation window. A dash means that no outcome is observed; it is not a Control observation.

Batch labels may be supplied explicitly or inferred from first observed calendar periods. Version 0.4.1 distinguishes calendar time, shared time on trial, and separate batch-specific time.

Exact SWD and BSWD schedules

library(stepwedgepower)

swd <- sw_batched_design(
  c(5L, 5L),
  rbind(
    `Group 1` = c("0", "1", "1+2", "1+2"),
    `Group 2` = c("0", "0", "1", "1+2")
  ),
  batch = c("Batch 1", "Batch 1")
)

bswd <- sw_batched_design(
  c(2L, 2L, 3L, 3L),
  rbind(
    `Group 1` = c("0", "1", "1+2", "1+2", "-"),
    `Group 2` = c("0", "0", "1", "1+2", "-"),
    `Group 3` = c("-", "0", "1", "1+2", "1+2"),
    `Group 4` = c("-", "0", "0", "1", "1+2")
  ),
  batch = c("Batch 1", "Batch 1", "Batch 2", "Batch 2")
)

swd
#> <sw_batched_design>
#>   type: classic SWD; 1 batch(es), 2 sequence(s), 4 calendar period(s)
#>    batch start delay_from_first gap_from_previous sequences clusters
#>  Batch 1     1                0                 0         2       10
#>   observed cluster-periods: 40 of 40
#>         Period 1 Period 2 Period 3 Period 4
#> Group 1 Control  A        A+B      A+B     
#> Group 2 Control  Control  A        A+B
bswd
#> <sw_batched_design>
#>   type: BSWD; 2 batch(es), 4 sequence(s), 5 calendar period(s)
#>    batch start delay_from_first gap_from_previous sequences clusters
#>  Batch 1     1                0                 0         2        4
#>  Batch 2     2                1                 1         2        6
#>   observed cluster-periods: 40 of 50
#>         Period 1 Period 2 Period 3 Period 4 Period 5
#> Group 1 Control  A        A+B      A+B      -       
#> Group 2 Control  Control  A        A+B      -       
#> Group 3 -        Control  A        A+B      A+B     
#> Group 4 -        Control  Control  A        A+B

The BSWD has two four-period batches, with the second batch starting one calendar period later. The object records calendar time and time on trial.

bswd$batch_start_period
#> Batch 1 Batch 2 
#>       1       2
bswd$batch_delay
#> Batch 1 Batch 2 
#>       0       1
bswd$batch_gap_from_previous
#> Batch 1 Batch 2 
#>       0       1
bswd$time_on_trial
#>         Period 1 Period 2 Period 3 Period 4 Period 5
#> Group 1        1        2        3        4        5
#> Group 2        1        2        3        4        5
#> Group 3       NA        1        2        3        4
#> Group 4       NA        1        2        3        4

Assumptions and resources

Because B is not delivered alone, its component coefficient is the incremental A+B-versus-A effect when the interaction is omitted.

assumptions <- sw_batched_assumptions(
  baseline_prob = 0.15,
  treatment_or_a = 1.35,
  treatment_or_b = 1.25,
  interaction_mode = "none",
  icc = 0.05,
  n_per_cluster_period = 25L,
  time_model = "calendar",
  time_effects = log(seq(1.00, 1.08, length.out = 5))
)

rbind(
  SWD = component_resource_summary(swd, assumptions),
  BSWD = component_resource_summary(bswd, assumptions)
)
#>      n_sequences n_clusters n_periods calendar_sequence_periods
#> SWD            2         10         4                         8
#> BSWD           4         10         5                        20
#>      observed_sequence_periods missing_sequence_periods
#> SWD                          8                        0
#> BSWD                        16                        4
#>      calendar_cluster_periods observed_cluster_periods missing_cluster_periods
#> SWD                        40                       40                       0
#> BSWD                       50                       40                      10
#>      observed_fraction total_individual_observations
#> SWD                1.0                          1000
#> BSWD               0.8                          1000

Both designs contain 40 observed clinic-periods and 1,000 observations. The BSWD spans 50 possible clinic-periods, of which 10 are structurally unobserved.

Audit the three time models

contrasts <- c("A_vs_control", "AB_vs_A", "AB_vs_control")

calendar_audit <- audit_batched_design(
  bswd, assumptions, "calendar", contrasts, include_interaction = FALSE
)
trial_time_audit <- audit_batched_design(
  bswd, assumptions, "time_on_trial", contrasts,
  include_interaction = FALSE
)
separate_audit <- audit_batched_design(
  bswd, assumptions, "separate", contrasts, include_interaction = FALSE
)

calendar_audit
#> <sw_batched_audit>
#>   time model: calendar 
#>   Calendar-period effects are shared across batches; this assumes a common secular trend on the calendar scale.
#>   fixed-effect rank: 7 of 7 (full)
#>   observed cluster-periods: 40 of 50; structurally unobserved: 10
#>   batches:
#>    batch start_period delay_from_first gap_from_previous end_period sequences
#>  Batch 1            1                0                 0          4         2
#>  Batch 2            2                1                 1          5         2
#>  clusters
#>         4
#>         6
#>   requested contrasts:
#>       contrast          label estimable
#>   A_vs_control   A vs Control      TRUE
#>        AB_vs_A       A+B vs A      TRUE
#>  AB_vs_control A+B vs Control      TRUE
#>   no structural cautions detected
trial_time_audit
#> <sw_batched_audit>
#>   time model: time_on_trial 
#>   Time-on-trial effects are shared across batches and indexed from each batch's initiation.
#>   fixed-effect rank: 6 of 6 (full)
#>   observed cluster-periods: 40 of 50; structurally unobserved: 10
#>   batches:
#>    batch start_period delay_from_first gap_from_previous end_period sequences
#>  Batch 1            1                0                 0          4         2
#>  Batch 2            2                1                 1          5         2
#>  clusters
#>         4
#>         6
#>   requested contrasts:
#>       contrast          label estimable
#>   A_vs_control   A vs Control      TRUE
#>        AB_vs_A       A+B vs A      TRUE
#>  AB_vs_control A+B vs Control      TRUE
#>   no structural cautions detected
separate_audit
#> <sw_batched_audit>
#>   time model: separate 
#>   Each batch has its own categorical time-on-trial profile, allowing batch-specific secular effects.
#>   fixed-effect rank: 10 of 10 (full)
#>   observed cluster-periods: 40 of 50; structurally unobserved: 10
#>   batches:
#>    batch start_period delay_from_first gap_from_previous end_period sequences
#>  Batch 1            1                0                 0          4         2
#>  Batch 2            2                1                 1          5         2
#>  clusters
#>         4
#>         6
#>   requested contrasts:
#>       contrast          label estimable
#>   A_vs_control   A vs Control      TRUE
#>        AB_vs_A       A+B vs A      TRUE
#>  AB_vs_control A+B vs Control      TRUE
#>   no structural cautions detected

Simulate and fit one trial

one_trial <- simulate_batched_swcrt(bswd, assumptions, seed = 123)
one_fit <- fit_batched_model(
  one_trial,
  time_model = "calendar",
  contrasts = contrasts,
  include_interaction = FALSE,
  multiplicity = "holm"
)
one_fit$contrasts
#>        contrast          label  estimate std_error  z_value    p_value
#> 1  A_vs_control   A vs Control 0.4891446 0.3301932 1.481389 0.13850294
#> 2       AB_vs_A       A+B vs A 0.3579656 0.2996250 1.194712 0.23219962
#> 3 AB_vs_control A+B vs Control 0.8471101 0.4787949 1.769255 0.07685134
#>      conf_low conf_high odds_ratio p_adjusted reject reject_adjusted
#> 1 -0.15802220 1.1363114   1.630921  0.1385029  FALSE           FALSE
#> 2 -0.22928867 0.9452198   1.430416         NA  FALSE              NA
#> 3 -0.09131052 1.7855308   2.332895         NA  FALSE              NA

Power comparisons

comparison <- compare_swd_bswd(
  swd, bswd, assumptions,
  nsim = 5000,
  contrasts = contrasts,
  include_interaction = FALSE,
  multiplicity = "holm",
  multiplicity_family = contrasts,
  nAGQ = 1,
  n_cores = 4,
  seed = 2026,
  warn_on_design = FALSE
)

comparison$resource_check
comparison$comparison
comparison$fit_diagnostics

model_comparison <- compare_batched_time_models(
  bswd, assumptions,
  nsim = 5000,
  contrasts = contrasts,
  include_interaction = FALSE,
  multiplicity = "holm",
  multiplicity_family = contrasts,
  nAGQ = 1,
  n_cores = 4,
  seed = 2026,
  warn_on_design = FALSE
)
model_comparison$comparison

The simulation output retains conditional and failure-aware power, Monte Carlo uncertainty, bias, coverage, hard GLMM errors, nonconvergence, non-finite contrasts, singular fits, and successful fits.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.