This vignette illustrates the component engine introduced in version 0.4.0. The schedule contains four assigned treatment states: Control, A, B, and A+B. Unlike a cumulative rollout, a component may be withdrawn and later restarted. The data-generating model can retain a prespecified residual component effect after withdrawal.
The following example contains both
Control -> A -> A+B -> B and
Control -> B -> A+B paths. Numeric state codes are 0
for Control, 1 for A, 2 for B, and 3 for A+B.
library(stepwedgepower)
state <- rbind(
S1 = c(0, 0, 1, 1, 3, 3, 2, 2, 2, 2),
S2 = c(0, 0, 0, 1, 1, 3, 3, 2, 2, 2),
S3 = c(0, 0, 2, 2, 3, 3, 3, 3, 3, 3),
S4 = c(0, 0, 0, 2, 2, 3, 3, 3, 3, 3),
S5 = c(0, 0, 1, 1, 1, 3, 3, 3, 3, 3),
S6 = c(0, 0, 2, 2, 2, 3, 3, 3, 3, 3),
S7 = c(0, 0, 0, 0, 0, 1, 1, 3, 2, 2),
S8 = c(0, 0, 0, 0, 0, 2, 2, 3, 1, 1)
)
design <- sw_component_design(
clusters_per_sequence = rep(5L, 8),
state = state
)
design
#> <sw_component_design>
#> 8 sequences, 10 periods, 40 clusters total
#> clusters per sequence: 5, 5, 5, 5, 5, 5, 5, 5
#> states present: Control, A, B, A+B
#> withdrawals: A = 3, B = 1; restarts: A = 0, B = 0
#> Period 1 Period 2 Period 3 Period 4 Period 5 Period 6 Period 7 Period 8
#> S1 Control Control A A A+B A+B B B
#> S2 Control Control Control A A A+B A+B B
#> S3 Control Control B B A+B A+B A+B A+B
#> S4 Control Control Control B B A+B A+B A+B
#> S5 Control Control A A A A+B A+B A+B
#> S6 Control Control B B B A+B A+B A+B
#> S7 Control Control Control Control Control A A A+B
#> S8 Control Control Control Control Control B B A+B
#> Period 9 Period 10
#> S1 B B
#> S2 B B
#> S3 A+B A+B
#> S4 A+B A+B
#> S5 A+B A+B
#> S6 A+B A+B
#> S7 B B
#> S8 A AThe schedule is represented internally by separate binary assignment matrices for A and B. It is not treated as an ordinal 0–3 dose.
assumptions <- sw_component_assumptions(
baseline_prob = 0.15,
treatment_or_a = 1.35,
treatment_or_b = 1.25,
interaction_or = 1.10,
delay_a = 1L,
delay_b = 1L,
carryover_periods_a = 2L,
carryover_periods_b = 2L,
carryover_weights_a = c(0.60, 0.30),
carryover_weights_b = c(0.50, 0.25),
restart_rule_a = "reset",
restart_rule_b = "reset",
interaction_mode = "effective_overlap",
icc = 0.05,
period_effects = log(seq(1.00, 1.10, length.out = ncol(state))),
n_per_cluster_period = 25L
)
assumptions
#> <sw_component_assumptions>
#> A vs Control: OR = 1.350; wash-in = 1 period(s)
#> B vs Control: OR = 1.250; wash-in = 1 period(s)
#> A-by-B interaction OR = 1.100; mode = effective_overlap
#> A carryover weights: 0.600, 0.300
#> B carryover weights: 0.500, 0.250
#> restart rules: A = reset; B = reset
#> cluster_sd = 0.416 (latent ICC = 0.050)
#> baseline probabilities: 0.150
#> period effects (log-odds): 0.000, 0.011, 0.022, 0.033, 0.043, 0.054, 0.065, 0.075, 0.085, 0.095A one-period delay means that a component first contributes its full
effect in the second consecutive assigned period. After A is withdrawn,
60 percent and 30 percent of its full log-odds effect persist for two
periods. With interaction_mode = "effective_overlap", the
interaction weight is the product of the effective A and B weights, so
interaction carryover is possible while both effective components
overlap.
audit <- audit_component_design(design, assumptions)
audit
#> <sw_component_audit>
#> fixed-effect rank: 13 of 13 (full)
#> requested contrasts:
#> contrast label estimable
#> A_vs_control A vs Control TRUE
#> B_vs_control B vs Control TRUE
#> AB_vs_control A+B vs Control TRUE
#> AB_vs_A A+B vs A TRUE
#> AB_vs_B A+B vs B TRUE
#> interaction A-by-B interaction TRUE
#> withdrawals: A = 3, B = 1; restarts: A = 0, B = 0
#> no structural cautions detectedThe audit checks the rank of the intended fixed-effect model and formal estimability of six standard contrasts. It also reports concurrent state comparisons and notes any withdrawal for which immediate washout has been assumed.
one_trial <- simulate_component_swcrt(design, assumptions, seed = 123)
fit <- fit_component_model(
one_trial,
multiplicity = "holm",
multiplicity_family = c("A_vs_control", "B_vs_control", "interaction")
)
fit$contrasts
#> contrast label estimate std_error z_value p_value
#> 1 A_vs_control A vs Control 0.2816765 0.12624300 2.231224 2.566626e-02
#> 2 B_vs_control B vs Control 0.2481387 0.13065989 1.899119 5.754882e-02
#> 3 AB_vs_control A+B vs Control 0.7497071 0.16120678 4.650593 3.309825e-06
#> 4 AB_vs_A A+B vs A 0.4680306 0.12204716 3.834834 1.256492e-04
#> 5 AB_vs_B A+B vs B 0.5015684 0.09744443 5.147225 2.643682e-07
#> 6 interaction A-by-B interaction 0.2198919 0.14437878 1.523021 1.277535e-01
#> conf_low conf_high odds_ratio p_adjusted reject reject_adjusted
#> 1 0.034244740 0.5291082 1.325350 0.07699879 TRUE FALSE
#> 2 -0.007949991 0.5042274 1.281638 0.11509765 FALSE FALSE
#> 3 0.433747592 1.0656666 2.116380 NA TRUE NA
#> 4 0.228822557 0.7072387 1.596846 NA TRUE NA
#> 5 0.310580825 0.6925560 1.651309 NA TRUE NA
#> 6 -0.063085297 0.5028691 1.245942 0.12775345 FALSE FALSEThe standard contrasts are:
The interaction coefficient is the departure from additivity on the log-odds scale. A B-only state is required to separate the B main effect from the interaction when the interaction is fitted.
A short exploratory run is shown below. A final analysis should use a
simulation count chosen from a prespecified Monte Carlo precision target
and should also run the global-null design through
type1_component_swcrt().
power <- power_component_swcrt(
design,
assumptions,
nsim = 5000,
multiplicity = "holm",
multiplicity_family = c("A_vs_control", "B_vs_control", "interaction"),
nAGQ = 1,
n_cores = 4,
seed = 2026
)
summary(power)
type1 <- type1_component_swcrt(
design,
assumptions,
nsim = 5000,
multiplicity = "holm",
multiplicity_family = c("A_vs_control", "B_vs_control", "interaction"),
nAGQ = 1,
n_cores = 4,
seed = 22026
)
summary(type1)Both conditional power and failure-aware power are reported. Their difference shows how much the apparent operating performance depends on excluding failed or non-estimable analyses.