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.
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+BThe 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 4Because 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 1000Both designs contain 40 observed clinic-periods and 1,000 observations. The BSWD spans 50 possible clinic-periods, of which 10 are structurally unobserved.
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 detectedone_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 NAcomparison <- 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$comparisonThe 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.