Returns one row per basis, because the three numbers a clinical prediction paper reports are not interchangeable and presenting one as another is the commonest way a published model flatters itself.
Arguments
- model
A
mm_model().- cohort
The cohort, for held-out scoring and the split. Its development rows must be the ones the model was fitted on.
- B
Bootstrap resamples.
- full_process
Also run the full-process bootstrap. Off by default because it refits
Btimes and costs accordingly.
Details
The bases are:
apparent, the model scored on the rows it was fitted to. Always optimistic, reported so that the optimism is visible. Its calibration is withheld rather than printed: scoring an unpenalized fit on its own rows forces the intercept to zero and the slope to one, so those two numbers describe the estimator and not the model.
bootstrap, fixed model, from
rms::validate(). Each resample refits the same formula, which corrects the optimism of estimating coefficients. It does not repeat imputation, selection, tuning or interaction screening, so it is not validation of a modelling strategy that includes those steps. The specification requires that distinction be made rather than assumed, and this column is where it is made. It does draw whole clusters when the project declares one, by passingclusterthrough torms::predab.resample(), and it reports how many resamples actually fitted, because rms keeps only those and says so only when asked.held out, rows the project's split reserved. Scored once.
bootstrap, full process, with
full_process = TRUE. Refits the whole strategy inside every resample, so the optimism corrected includes the data-dependent choices the fixed-model bootstrap inherits. Seemm_full_process()for what it does and does not re-execute.
Incomplete rows
Every basis is judged on the rows that can be judged, and the exclusions are
reported rather than absorbed. mm_evaluable() is the one rule, the same one
mm_model() fitted under, so the sample a model is measured against is the
sample it was estimated on; validating against a cohort whose development
rows do not match the fit is an error rather than a quietly different
number. The bootstraps resample the fitted cohort, never the supplied frame.
A missing predictor and a missing outcome are counted apart, with the overlap separately, because they are different exclusions and because taking a missing outcome for a non-event leaves the event count unchanged while inflating the denominator.
Rows are selected first and mm_predict() is then called at its own default
of on_missing = "stop", so the complete-case path is explicit here and
prediction keeps refusing to put a number on an incomplete record.
This is complete-case analysis. It is a computational strategy and not a
guarantee: the estimates assume the exclusions are uninformative, which is an
assumption rather than a finding, and the note column says so whenever any
row was excluded. Multiple imputation is not in this package yet.
The scaled Brier column
Every basis is scaled against one reference, the Brier of a model
predicting the development cohort's prevalence for everyone, reported as
brier_ref. Scaling each basis by its own prevalence is the conventional
within-sample skill score, and a column of those is a set of scores against
different references, which is a comparison a reader will make down the
column and should not. Where a basis has a prevalence of its own, the
note gives the value its own reference would produce.
Examples
cohort <- mm_synthetic_cohort(n = 300, seed = 1)
project <- mm_project("uds", "event", c("age", "bmi", "sex"),
cohort = cohort, split_var = "year",
split_at = 2023)
fit <- mm_model(project, cohort, nonlinear = "age")
#> Warning: the development sample may be too small for this model: 190 rows is 1928 short of the 2118 a model of 5 parameter(s) needs at 8.4% prevalence for 90% shrinkage. Coefficients from a sample this size are expected to be overfitted; see the adequacy element of the returned object.
mm_validate(fit, cohort, B = 25)
#> <mm_validation> full-process: FALSE
#> columns: n, events/non-events, c-statistic with its 95% DeLong interval where one is estimable
#> apparent n=190 16/174 C=0.6257 (0.4652-0.7862)
#> bootstrap, fixed model n=190 16/174 C=0.5010 no interval, see note slope=0.3818
#> held out n=110 12/98 C=0.3844 (0.2074-0.5613) slope=-1.1131
#> Fixed-model bootstrap only. Not validation of a strategy.