NICE DSU cross-reference
Reviewers navigate an appraisal by TSD number. This maps the three that govern survival extrapolation onto what the merlin family actually does, with the command for each in both languages: the released one where it exists, marked in build where it does not.
Nothing here reproduces the guidance. Each entry is our own description of what the document is for, next to the command that answers it — the citation travels, the published text stays where it was published.
Released merlin for Stata (2.5.0) fits the candidate families and predicts survival and restricted mean survival time past follow-up. merlin 3.0.0 for Stata, in build, adds an HTA layer on top: it fits the whole candidate set in one call, anchors the tail to background mortality, and hands the parameter draws to pendragon for the value layer or morgana for Bayesian posteriors. merlin for R v2, in build, does the same in R. One of us co-authored TSD 21.
Extrapolating survival from patient-level trial data
What it asks of an analysis
Fit the candidate parametric families to the same data, compare them on fit and on what each implies beyond the observed follow-up, and justify the one carried into the model rather than reporting a single curve.
What the merlin family does
In released merlin for Stata (2.5.0), fit each candidate family in turn — exponential, Weibull, Gompertz, generalised gamma, log-normal, log-logistic and a Royston–Parmar spline — store each fit, and set estimates stats beside what each one predicts past follow-up: survival and RMST out to the horizon. merlin 3.0.0 for Stata, in build, adds merlin grid, which fits the whole set in one call. merlin for R v2, in build, adds merlin_extrap_grid(), which returns one row per fit — log-likelihood, df, AIC, ΔAIC, BIC, landmark survival, RMST at the horizon, and whether it converged. The comparison becomes a table you can attach rather than a paragraph.
# merlin for R v2, in build
merlin_extrap_grid(
Surv(time, status) ~ trt, data = d,
families = c("exponential", "weibull", "gompertz",
"lognormal", "loglogistic", "ggamma", "rp"),
df = list(rp = c(3, 5, 7)),
landmarks = c(1, 5, 10), horizon = 50)
stset time, failure(status)
* each candidate family, stored, then set the fit statistics beside the tail
local fams exponential weibull gompertz lognormal loglogistic ggamma
foreach f of local fams {
quietly merlin (_t trt, family(`f', failure(_d)))
estimates store `f'
}
quietly merlin (_t trt, family(rp, df(3) failure(_d)))
estimates store rp3
estimates stats _all
* survival and RMST to 50 years for the treated arm, from each fit
range tt 0 50 101
foreach m in `fams' rp3 {
quietly estimates restore `m'
quietly predict s_`m', survival timevar(tt) at(trt 1)
quietly predict r_`m', rmst timevar(tt) at(trt 1)
}
Flexible methods, when the standard families will not fit
What it asks of an analysis
Where the standard set cannot follow the shape of the hazard — a turning point, a plateau, a cured fraction — use a model flexible enough that it can, and be explicit about what the flexibility does at the horizon.
What the merlin family does
Royston–Parmar splines are a family like any other here, so moving from Weibull to a spline is one argument rather than a different package; released merlin for Stata (2.5.0) fits them today. merlin 3.0.0 for Stata and merlin for R v2, both in build, add M-splines, the safer choice when the extrapolation is what matters: the constant-hazard tail gives a flat hazard past the last knot, where Weibull and Gompertz can explode or vanish. They also add mixture and non-mixture cure forms in the same syntax.
# merlin for R v2, in build
merlin(Surv(time, status) ~ trt,
family = "rp", df = 3, data = d)
# flat-hazard tail, for a long horizon
merlin(Surv(time, status) ~ trt,
family = "mspline", df = 4, data = d)
stset time, failure(status) merlin (_t trt, family(rp, df(3) failure(_d)))
Partitioned survival as a decision structure
What it asks of an analysis
The dominant oncology structure: progression-free and overall survival are modelled separately and the health states are read off as areas between the curves — which means the curves can cross and produce a negative state occupancy unless something stops them.
What the merlin family does
The explorer shows the structure and the curve-crossing failure directly, which is the quickest way to see why the assumption matters. For the analysis, fit the two curves in merlin and take occupancy, costs and the ICER through pendragon. A state-transition model fitted as a multi-state model avoids the crossing problem rather than checking for it.
# merlin for R v2, in build: the two curves, then out to the value layer merlin(Surv(pfs, prog) ~ trt, family = "rp", df = 3, data = d) merlin(Surv(os, died) ~ trt, family = "rp", df = 3, data = d)
stset pfs, failure(prog) stmerlin trt, distribution(rp) df(3) // progression-free estimates store pfs stset os, failure(died) stmerlin trt, distribution(rp) df(3) // overall survival estimates store os
What a reviewer asks for next
Background mortality. An extrapolated hazard that falls below the general population’s is a curve to reject. merlin 3.0.0 for Stata, in build, adds merlin extrap, which anchors the tail to a lifetable — across the whole time axis, or only past an attained age — and re-derives survival and RMST from the anchored hazard. merlin for R v2, in build, adds merlin_extrap() for the same.
Parameter uncertainty. merlin psa (Stata 3.0.0, in build) and merlin_extrap_psa() (R v2, in build) draw from the fitted parameter vector and replay each draw through the prediction, giving bands on survival, hazard and RMST. That same draw matrix is the hand-off to pendragon, so uncertainty in the survival model becomes uncertainty in the ICER rather than a separate assumption.
Reproducibility. What the checks cover, and what they do not, is set out on the test suite page; qualified builds for regulated use are on Validated.