GST hex sticker. A small forest plot with the global test marked in red.

Totality of Evidence in Minutes

Interactive Apps for GST Power and
Publication-Ready Forest Plots

Originally developed at Sarepta Therapeutics · Maintained by Xiao Ni

xni7.github.io/gst

The two headaches

The design headache

Several endpoints, correlated, with different effect sizes. Power depends on all of them — and on the test.

The reporting headache

Every change to a header or an interval colour means another round of plotting code.

p <- forest_data() |>
  forest_plot(
    ci_color = "#008A99",
    display_cols = c(
      "Group", "forest",
      "est+ci", "p"))
# edit, re-run, re-export

Thumbnail of a multi-endpoint forest plot.

The two apps

Plan it. Power it. Publish it.
GST Power App (Plan it · Power it)
  • Size studies analytically from effects and correlations.
  • See when combining endpoints helps or hurts.
  • No repeated-trial simulation script for each scenario.
Forest Plot App (Publish it)
  • Flexible journal-style formatting.
  • No patient-level data (summary statistics only).
  • Export figures, data and R code to rebuild the plot.

Model-based analytical calculations support exploration. Simulation can still assess calibration.

Methods in {gst}

What is a GST?
A global statistical test assesses a joint signal across endpoints.Note: a significant global test does not mean every endpoint improved.
The weights
Equal-weight OLS or inverse-correlation GLS (O’Brien, 1984).
The cutoff
Degrees-of-freedom approximations: Dallow, O’Brien, Logan–Tamhane.
O’Brien Plus (GST_Any)
Power for the joint test and at least one individual endpoint being significant.

Also in {gst}: Wei–Lachin; gst::obt(), an O’Brien-based truncated closed-testing routine (Luo et al., 2025) — not in today’s demos. DOI: 10.1002/pst.70010

{gst} package structure

gst/ v0.3.1 · one R package behind both apps
├── R/ Package functions: obrien(), power & sample size, forest_plot(), forest_reprex(), Shiny modules
├── apps/ gst-power/ and forestplot/: deployment entry points (app.R + manifest.json)
├── inst/www/ App assets: CSS and logo
├── man/ Function reference (.Rd)
├── vignettes/ obrien · gst_power_app · forestplot · reference
├── tests/ testthat unit tests
├── pkgdown/ Documentation-site assets
├── DESCRIPTION Package metadata and dependencies
├── NAMESPACE Exported functions
└── app.R Local launcher: gst_power_app()

Docs: https://xni7.github.io/gst/ · Code: https://github.com/xni7/gst

Three designs, different power

Design Effects (δ₁, δ₂) Correlation ρ OES GST power Best individual
Balanced (0.4, 0.4) 0.3 0.50 94% 80%
Redundant (0.4, 0.4) 0.9 0.41 82% 80%
Uneven (0.5, 0.1) 0.8 0.32 60% 94%

Two endpoints, n = 100 per arm, two-sided α = .05, O’Brien OLS, Dallow df = 198. OES from gst::get_oes(); GST power is two-sample t power at the OES. Rounded as in the app; bold marks the higher power in each row. Best individual is a planning benchmark, not a multiplicity-adjusted selection rule.

Operational effect size (OES)

OES = Σδ √(ΣΣρij) Sum of individual effects Entries of the correlation matrix
Operational effect size Largest single effect

For equal-weight OLS, at the same n and degrees of freedom, the global test beats the best single endpoint when OES exceeds the largest single effect.

O’Brien OLS operational effect size, gst::get_oes(): 1ᵀδ / √(1ᵀR1). The three designs: 0.50 vs 0.40, 0.41 vs 0.40, 0.32 vs 0.50. A planning comparison, not permission to pick the test after seeing the data.

Sizing the study

Single endpoint
Effect 0.4 → about 100 per arm for 80% power.
GST, balanced design
Effects (0.4, 0.4), ρ = 0.3 → 65 per arm for 80% power.

✓ Calculated analytically with noncentral t distributions — no simulation grid needed to explore.

Sample-size widget from the gst power-app vignette. Effects 0.5 and 0.5, correlation 0.5, target power 0.8, Dallow, OLS. The curve crosses about 49 per arm.

Widget screenshot from the gst vignette: a different example — effects (0.5, 0.5), ρ = 0.5 → 49 per arm.

65 per arm from gst::get_n() in gst 0.3.1 (power 80.2%); single endpoint 80.4% at n = 100 (pwr::pwr.t2n.test). Two-sided α = .05, Dallow df.

Combining can help or dilute

Stouffer p-value plane. Cases 1 and 2 share one point at p = .08 and .08. Case 3 is a red triangle near the top left. Boundaries for correlation 0.3 (green, solid), 0.9 (amber, dashed), and 0.8 (red, dotted); the case 1 gain region is shaded.

Correlation-adjusted Stouffer
Case (p₁, p₂) ρ Combined p
1 · Helps (.08, .08) .3 .0261
2 · Misses .05 (.08, .08) .9 .0627
3 · Dilutes best (.00051, .48) .8 .0259
1 → 2: the point did not move; the .05 boundary did. Case 2 is still below .08.
3: below .05, but weaker than its best single p (.00051).

Hypothetical p-values; separate from O’Brien power. gst’s Stouffer: one-sided z, Nyholt effective-test scaling (gst:::p_combine). Curves: combined p = .05 boundaries. Shaded: case 1 gain. Axes use sqrt(p) spacing.

The forest plot

Input

Summary statistics (estimate, CI, p) via table, paste or CSV template. No patient-level data.

Forest plot of three endpoints with overall, 4–5 year, and 6–7 year rows. Only the starred 6–7 year row of endpoint 3 is red (inverted).

Formatting

Edit CI colours, column widths and headers interactively.

Transformations

Invert reverses the plotted direction; printed numbers stay as entered.

Standardize shows standard-error units; printed estimates and nominal p-values unchanged.

Built-in demo values from {gst}, not clinical results. Starred row: plotted direction reversed; printed estimate unchanged. Review against target-journal requirements.

From the app to R

Interactive edits (the app)

In the app

R Code Save Code Copy

Exports

CSV / RDS for data · PNG / JPEG for figures

Reproducible R (the script)
library(gst)
p <- forest_data() |>
  forest_plot(
    ci_color = "#008A99",
    ci_color_inverted = "#C44B49",
    display_cols = c(
      "Group", "forest", "est+ci", "p"))
writeLines(forest_reprex(p),
           "forest-reproduce.R")

Compact workflow shown. The companion export rpharma-forest-example.R (gst 0.3.1) reproduces forest-plot.png; slide 9 shows a display copy of it.

Acknowledgements

Package authors and contributors
Lerong Li
Anthony Pileggi
Xiaolong Luo
Stephen Knapp
Preston Burns
Sarepta Therapeutics
Original development and support
Open-source community
R package maintainers and R/Pharma organizers

Plan it. Power it. Publish it.

{gst} takes you from trial design to the forest plot — size your GST study with the Power App, report it with the Forest Plot App.

Totality of evidence, in minutes.

Apps: xni7-gstpower.share.connect.posit.cloud · xni7-gstmakeforestplot.share.connect.posit.cloud
Source: github.com/xni7/gst · xni7.github.io/gst · Slides: xni7.github.io/talks/rpharma2026-gst/ · Video: Talk video (9.5 min)