ICH E9(R1) Estimand Framework for R
Every clinical trial submitted to FDA or EMA now requires a formally documented estimand. In practice, statisticians write these in Word documents — disconnected from the analysis code, impossible to validate, and guaranteed to drift from what is actually implemented.
estimandr closes that gap. It represents every component of an ICH
E9(R1) estimand as a structured, computable R object: population,
variable, intercurrent events (with all five strategies as first-class
types), population-level summary, and analysis method. It validates that
your analysis is consistent with your stated estimand, generates SAP
paragraph text automatically, and produces regulatory-ready submission
tables and reports.
remotes::install_github("repro-stats/estimandr")library(estimandr)
# 1. Define the five estimand components
pop <- est_population(
description = "Adults aged >= 18 with moderate-to-severe atopic dermatitis",
criteria = list(
inclusion = c("Age >= 18 years", "Baseline EASI >= 16", "Randomised"),
exclusion = c("Prior biologic therapy within 12 weeks")
),
flag_var = "ITTFL"
)
var <- est_variable(
name = "Change from baseline in EASI score at Week 16",
type = "continuous",
timepoint = "Week 16",
adam_dataset = "ADEFF",
adam_var = "CHG",
direction = "decrease",
scale = list(name = "EASI", range = c(0, 72), mcid = 6.6)
)
disc <- define_ice(
name = "Treatment discontinuation",
code = "DISC",
strategy = ice_hypothetical(
assumption = "Patients would have continued to respond had they remained on treatment",
estimation = "MMRM using all available pre-discontinuation data under MAR"
)
)
sm <- est_summary_measure(
type = "difference",
description = "Difference in LS mean change from baseline (Active - Placebo)"
)
an <- est_analysis(
method = "MMRM",
description = "MMRM with treatment, visit, treatment x visit, and baseline",
formula = "CHG ~ TRT01P * AVISIT + BASE + BASE:AVISIT",
software = "mmrm::mmrm()"
)
# 2. Build the estimand
primary <- define_estimand(
id = "primary",
label = "Primary efficacy estimand",
population = pop,
variable = var,
ices = list(discontinuation = disc),
summary_measure = sm,
analysis = an
)
# 3. Add a sensitivity estimand
sensitivity <- define_estimand(
id = "sensitivity_tp",
label = "Treatment policy sensitivity estimand",
role = "sensitivity",
population = pop,
variable = var,
ices = list(discontinuation = define_ice(
"Treatment discontinuation",
strategy = ice_treatment_policy("Real-world effect")
)),
summary_measure = sm,
analysis = an
)
tree <- estimand_tree(primary = primary, sensitivity = list(sensitivity))
# 4. Validate consistency
check_consistency(primary)
# 5. Generate outputs
cat(sap_text(primary)) # SAP paragraph
estimand_table(tree) # Regulatory submission table
estimand_report(tree, # Full HTML report
output = "outputs/estimand_spec.html",
study_id = "TRIAL-001",
sponsor = "Example Pharma Ltd"
)| Function | Purpose |
|---|---|
define_estimand() |
Define a complete estimand |
est_population() |
Specify the target population |
est_variable() |
Specify the outcome variable |
define_ice() |
Define an intercurrent event |
ice_treatment_policy() |
Treatment policy ICE strategy |
ice_hypothetical() |
Hypothetical ICE strategy |
ice_composite() |
Composite ICE strategy |
ice_while_on_treatment() |
While-on-treatment ICE strategy |
ice_principal_stratum() |
Principal stratum ICE strategy |
est_summary_measure() |
Population-level summary measure |
est_analysis() |
Statistical analysis method |
estimand_tree() |
Primary/secondary/sensitivity hierarchy |
check_consistency() |
Validate estimand-analysis alignment |
sap_text() |
Generate SAP paragraph text |
estimand_table() |
Regulatory submission table |
estimand_report() |
Full HTML estimand report |
estimand_template() |
Built-in templates for common scenarios |
Pre-built templates for six common trial scenarios:
estimand_templates() # list all available templates
estimand_template("superiority_continuous")
estimand_template("non_inferiority_binary")
estimand_template("time_to_event")
estimand_template("oncology_os")
estimand_template("rwe_effectiveness")estimandr is designed to pair with bayprior for Bayesian estimand
analyses. Define your estimand first, then link it to a bayprior
elicitation for the primary analysis:
library(bayprior)
# elicit prior, then reference it alongside your estimand- ICH E9(R1) Addendum on Estimands and Sensitivity Analysis (November 2019)
- FDA Guidance: E9(R1) Statistical Principles for Clinical Trials (May 2021)
- EMA Reflection Paper on Estimands (March 2020)
MIT © ReproStats
