STC Methodology
Comprehensive methodological guidance for conducting rigorous Simulated Treatment Comparisons following NICE DSU TSD 18.
When to Use This Skill
- •Deciding between STC and MAIC
- •Selecting effect modifiers for STC model
- •Understanding the covariate centering approach
- •Implementing Bayesian STC
- •Reviewing STC code or results
Fundamental Concept
Outcome Regression vs Propensity Weighting
STC Approach:
- •Fit outcome regression model in IPD
- •Include treatment and treatment-covariate interactions
- •Predict treatment effect at external population covariate values
- •Model-based adjustment for population differences
MAIC Approach:
- •Reweight IPD to match external population
- •Analyze weighted data as if from external population
- •Design-based adjustment
Key Equation (Binary Outcome)
code
logit(P(Y=1)) = β₀ + β_trt × Treatment + β_X × X + β_trt:X × Treatment × X Where: - β_trt: Treatment effect when X = 0 - β_X: Effect of covariate X on outcome - β_trt:X: Treatment-covariate interaction (effect modification) For anchored STC: 1. Center X on external population mean: X_centered = X - X_external 2. Fit model with centered X 3. β_trt now represents treatment effect at external population values
Assumptions
Conditional Constancy of Relative Effects
Same as MAIC:
- •Relative treatment effect is constant across populations after adjusting for effect modifiers
- •Requires all effect modifiers included in model
Model Specification
Additional assumption vs MAIC:
- •Outcome model must be correctly specified
- •Includes functional form of covariate effects
- •Includes correct interactions
code
Trade-off: ├── If model correct → STC more efficient than MAIC ├── If model wrong → STC may be biased └── MAIC doesn't require outcome model specification
Effect Modifier Selection
What is an Effect Modifier?
A covariate that interacts with treatment effect:
- •Treatment effect differs at different covariate values
- •Shows significant treatment × covariate interaction
- •Has biological plausibility for interaction
Selection Strategy
code
Effect Modifier Identification:
├── 1. Clinical/Biological Rationale
│ - Published literature on effect modification
│ - Mechanism of action considerations
│ - Expert clinical input
│
├── 2. Statistical Evidence (from IPD)
│ - Interaction terms in regression
│ - Subgroup analyses
│ - Use α = 0.10 (underpowered for interactions)
│
├── 3. Availability in AgD
│ - Must have summary statistics
│ - Means for continuous, proportions for binary
│
└── 4. Imbalance Between Populations
- Focus on covariates that differ
- Balanced covariates less important
Using identify_effect_modifiers()
r
em_result <- identify_effect_modifiers(
data = ipd_data,
outcome_var = "response",
treatment_var = "treatment",
candidate_covariates = c("age", "sex", "biomarker", "stage"),
alpha = 0.10
)
# Returns:
# - Interaction p-values
# - Interaction coefficients
# - Recommended effect modifiers
Covariate Centering
Why Center Covariates?
code
Without centering (X = raw values): - β_trt = treatment effect when ALL covariates = 0 - This may be meaningless (e.g., age = 0) With centering (X_centered = X - X_external): - β_trt = treatment effect when X = X_external - This is the effect in external trial population - Exactly what we need for ITC
Centering Process
r
# For continuous covariate age_centered <- age - agd_mean_age # For binary covariate male_centered <- male - agd_prop_male # Result: mean of centered covariate = (IPD mean - AgD mean) # When evaluated at X_centered = 0, we get AgD population
Including vs Excluding Main Effects
With interactions:
r
# Model: Y ~ treatment + X_centered + treatment:X_centered # β_trt: effect at X = X_external # β_trt:X: how effect changes with X
Main effects typically included even if not "significant":
- •Required for proper interpretation of interactions
- •Follows statistical best practice
- •Model hierarchically well-formulated
Anchored vs Unanchored STC
Anchored STC
code
Setup: - IPD trial: A vs Common (C) - AgD trial: B vs Common (C) Steps: 1. Center covariates on AgD population 2. Fit: logit(Y) ~ Treatment + X_centered + Treatment:X_centered 3. Extract β_A (A vs C at AgD population) 4. Calculate d_BC from AgD (B vs C) 5. Indirect: d_AB = β_A - d_BC
Unanchored STC
code
Setup: - IPD trial: Treatment A only (or A vs something) - AgD: Single-arm Treatment B Caution: Same issues as unanchored MAIC - Must adjust for ALL prognostic factors - Assumes absolute effects transportable - Strong assumptions - use as sensitivity only
STC vs MAIC Comparison
Theoretical Comparison
| Aspect | STC | MAIC |
|---|---|---|
| Method | Outcome regression | Propensity weighting |
| Efficiency | Higher (if model correct) | Lower (ESS reduction) |
| Model dependence | Higher | Lower |
| Continuous covariates | Natural | May need categorization |
| Extrapolation | Possible (with caution) | Limited to overlap |
| Diagnostic | Model fit, residuals | ESS, weight distribution |
When to Prefer STC
- •Model specification confidence is high
- •Continuous covariates to adjust for
- •Want to leverage regression framework
- •MAIC gives very low ESS
- •Interested in Bayesian framework
When to Prefer MAIC
- •Uncertain about outcome model
- •Want design-based approach
- •Good overlap in covariate distributions
- •Acceptable ESS achieved
Best Practice: Both as Sensitivity
r
# Run both methods stc_result <- anchored_stc_binary(...) maic_result <- maic_anchored(...) # Compare results # If similar: increased confidence # If different: investigate why
Bayesian STC
Advantages
- •Natural uncertainty quantification
- •Prior information incorporation
- •Posterior predictive checks
- •Sensitivity to prior specification
Prior Selection
r
# Treatment effect prior prior_normal(0, 10) # Weakly informative # Covariate effects prior_normal(0, 5) # Interactions (typically smaller) prior_normal(0, 2) # Sensitivity analysis with different priors
Implementation
r
bayes_result <- bayesian_anchored_stc_binary(
ipd_data = ipd,
agd_data = agd,
outcome_var = "response",
treatment_var = "treatment",
covariates = c("age", "sex"),
priors = list(
treatment = prior_normal(0, 10),
covariates = prior_normal(0, 5),
interactions = prior_normal(0, 2)
),
n_iter = 10000,
n_warmup = 2000,
seed = 12345
)
Reporting Requirements
Methods
- • Justification for STC (vs MAIC, vs nothing)
- • Effect modifier selection process
- • Covariates included with rationale
- • Model specification (link function, interactions)
- • Centering approach explained
- • Frequentist vs Bayesian justification
- • Prior specification (if Bayesian)
Results
- • Model coefficients with CIs
- • Treatment effect at external population
- • Comparison with unadjusted estimate
- • Model diagnostics
- • Sensitivity analyses (including vs MAIC)
Common Pitfalls
1. Forgetting to Center Covariates
- •Treatment coefficient won't have correct interpretation
- •Will estimate effect at covariate = 0, not external population
2. Omitting Interactions
- •Defeats purpose of STC
- •Must include treatment × covariate interactions
3. Including Too Many Covariates
- •Model overfitting
- •Unstable estimates
- •Focus on effect modifiers only
4. Ignoring Model Diagnostics
- •Check residuals
- •Assess model fit
- •Validate assumptions (linearity, etc.)
5. Not Comparing to MAIC
- •Both methods should give similar answers
- •Differences indicate model issues
- •Always run as sensitivity
Quick Reference Code
r
library(stc)
# 1. Identify effect modifiers
em <- identify_effect_modifiers(
data = ipd,
outcome_var = "response",
treatment_var = "treatment",
candidate_covariates = c("age", "sex", "biomarker"),
alpha = 0.10
)
# 2. Run anchored STC (frequentist)
result <- anchored_stc_binary(
ipd_data = ipd,
agd_data = list(
n_total_A = 150, n_total_C = 150,
n_events_A = 45, n_events_C = 60,
covariates = list(
age = list(mean = 62),
sex = list(prop = 0.55)
)
),
outcome_var = "response",
treatment_var = "treatment",
covariates = c("age", "sex"),
reference_arm = "A",
include_interactions = TRUE,
robust_se = TRUE
)
# 3. View results
print(result)
summary(result)
# 4. Access specific effects
result$treatment_effect_BC # Indirect comparison
result$treatment_effect_AB # Direct from IPD
result$treatment_effect_AC # From AgD
# 5. Bayesian sensitivity
bayes_result <- bayesian_anchored_stc_binary(
...,
priors = list(
treatment = prior_normal(0, 10)
)
)
Resources
- •NICE DSU TSD 18: Population-adjusted indirect comparisons
- •Phillippo et al. (2018): Methods for STC
- •Ishak et al. (2015): Simulation studies
- •stc package documentation