group-sequential-methods — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited group-sequential-methods (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A group sequential design allows for:
Information fraction at analysis k:
I_k / I_K = (events at analysis k) / (total planned events)For time-to-event trials, information ≈ number of events.
The key constraint is that the design controls the overall Type I error at the planned alpha level. Spending functions define cumulative alpha spending over information time, and boundaries are derived using the joint distribution of sequential test statistics. They are not obtained by simply assigning independent nominal alpha levels to each look.
Properties:
Formula:
α*(t) = 2 - 2Φ(z_{α/2} / √t)When to Use:
Properties:
Formula:
α*(t) = α × log(1 + (e-1)t)When to Use:
Properties:
Formula:
α*(t) = α × (1 - e^{-γt}) / (1 - e^{-γ})When to Use:
| Function | Early Spending | Final Power | Early Stopping |
|---|---|---|---|
| OBF | Low | High | Difficult |
| Pocock | High | Lower | Easier |
| HSD(γ=-4) | Low | High | Difficult |
| HSD(γ=1) | High | Lower | Easier |
Similar to alpha-spending, but for Type II error:
β*(t) = spending function × β# Interim Analysis 1
ia1_cut <- create_cut(
planned_calendar_time = 20, # Minimum 20 months
target_event_overall = 100, # Target 100 events
max_extension_for_target_event = 24, # Wait up to 24 months for events
min_n_overall = 200, # At least 200 enrolled
min_followup = 12 # 12 months minimum follow-up
)
# Interim Analysis 2
ia2_cut <- create_cut(
planned_calendar_time = 32,
target_event_overall = 200,
max_extension_for_target_event = 34,
min_time_after_previous_analysis = 10 # At least 10 months after IA1
)
# Final Analysis
fa_cut <- create_cut(
planned_calendar_time = 45,
target_event_overall = 350
)library(simtrial)
library(gsDesign2)
# Define enrollment
enroll_rate <- define_enroll_rate(
duration = c(4, 12),
rate = c(10, 30)
)
# Define failure rates
fail_rate <- define_fail_rate(
duration = c(3, 100),
fail_rate = log(2)/9,
hr = c(1, 0.6),
dropout_rate = 0.001
)
# Run simulation
results <- sim_gs_n(
n_sim = 1000,
sample_size = 400,
enroll_rate = enroll_rate,
fail_rate = fail_rate,
test = wlr,
cut = list(ia1 = ia1_cut, ia2 = ia2_cut, fa = fa_cut),
weight = fh(rho = 0, gamma = 0)
)library(gsDesign2)
# Design with gsDesign2
design <- gs_design_ahr(
enroll_rate = define_enroll_rate(duration = c(4, 12), rate = c(10, 30)),
fail_rate = define_fail_rate(
duration = c(3, 100),
fail_rate = log(2)/9,
hr = c(1, 0.6),
dropout_rate = 0.001
),
alpha = 0.025,
beta = 0.1,
analysis_time = c(24, 36, 48),
upper = gs_spending_bound,
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025),
lower = gs_spending_bound,
lpar = list(sf = gsDesign::sfHSD, param = -4, total_spend = 0.1)
) |> to_integer()
# Simulate with design object
sim_results <- sim_gs_n(
n_sim = 1000,
sample_size = max(design$analysis$n),
enroll_rate = design$enroll_rate,
fail_rate = design$fail_rate,
test = wlr,
cut = NULL, # Auto-generated from design
original_design = design,
weight = fh(rho = 0, gamma = 0)
)When using original_design, sim_gs_n() can compute updated bounds:
# Results include planned and updated bounds
results <- sim_gs_n(
# ... parameters ...
original_design = design,
ia_alpha_spending = "min_planned_actual", # Conservative
fa_alpha_spending = "full_alpha" # Spend full alpha at FA
)
# Output includes:
# - planned_upper_bound, planned_lower_bound
# - updated_upper_bound, updated_lower_boundAlpha Spending Options:
| ia_alpha_spending | Description |
|---|---|
| "min_planned_actual" | Conservative: min of planned and actual |
| "actual" | Spend based on actual information |
| fa_alpha_spending | Description |
|---|---|
| "full_alpha" | Spend remaining alpha at final |
| "info_frac" | Spend based on information fraction |
# Different tests at each analysis
ia1_test <- create_test(wlr, weight = fh(rho = 0, gamma = 0))
ia2_test <- create_test(wlr, weight = fh(rho = 0, gamma = 0.5))
fa_test <- create_test(wlr, weight = mb(delay = 6, w_max = Inf))
results <- sim_gs_n(
n_sim = 1000,
sample_size = 400,
enroll_rate = enroll_rate,
fail_rate = fail_rate,
test = list(ia1 = ia1_test, ia2 = ia2_test, fa = fa_test),
cut = list(ia1 = ia1_cut, ia2 = ia2_cut, fa = fa_cut)
)# IA at 50% information, FA at 100%
ia_cut <- create_cut(target_event_overall = 150) # 50%
fa_cut <- create_cut(target_event_overall = 300) # 100%
sim_gs_n(
n_sim = 1000,
sample_size = 400,
test = wlr,
cut = list(ia = ia_cut, fa = fa_cut),
weight = fh(0, 0)
)# Standard 33%, 67%, 100% information
ia1_cut <- create_cut(target_event_overall = 100)
ia2_cut <- create_cut(target_event_overall = 200)
fa_cut <- create_cut(target_event_overall = 300)# Events-based but with minimum calendar time
ia_cut <- create_cut(
target_event_overall = 150,
planned_calendar_time = 18, # At least 18 months
max_extension_for_target_event = 24 # Max 24 months
)# Summarize simulation results
results_summary <- results |>
group_by(analysis) |>
summarise(
mean_events = mean(event),
mean_z = mean(z),
power = mean(z < qnorm(0.025)), # One-sided
.groups = "drop"
)plan("multisession") for large simulations~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.