Predict a future biomarker value from fitted sub-models, for a single biomarker
Source:R/dynamicPredictionBio.R
dynamicPredictionBio.RdInternal single-biomarker engine behind
predictLongitudinal – call predictLongitudinal()
directly instead (it dispatches here automatically when bio_i
names exactly one biomarker, and to dynamicPredictionBioAll
otherwise). Kept as a separate internal function – rather than folded
into predictLongitudinal() – because predictPlot
and checkBandcountConvergence also call it directly for a
single biomarker at a time.
Companion to predictRisk, using the same fitted
longitudinal (longitudinalSub) and survival
(survivalSub) sub-models, but instead of an event-risk
probability this returns a predictive density for a future value of one
chosen biomarker (bio_i) at prediction_time + horizon,
conditional on the subject's observed longitudinal history up to
prediction_time and on survival (being event-free) through
prediction_time + horizon, the time the biomarker is predicted at.
The density (Y_density, evaluated over a candidate-value grid
Y_all) is obtained by integrating the biomarker's predictive
distribution against the survival sub-model's hazard, using the subject's
empirical-Bayes random-effects update from their observed history; its
mode (Y_predict) is reported as the point prediction. As in
predictRisk, the survival-side integrals are
evaluated on numerical grids controlled by bandcount2, and the
density itself is evaluated on a grid controlled by bandcount3;
see Details.
bio_i may refer to either a continuous or an
ordinal biomarker (see longitudinalSubCopula);
for an ordinal bio_i, Y_all and Y_predict hold
integer category codes (1:K, in the fitted factor's
levels()/threshold order) rather than a numeric grid, with
Y_all additionally carrying a "category_labels" attribute
giving the matching level-label strings; bandcount3 is ignored in
that case, since the candidate grid is fixed at the biomarker's category
count (see Details).
The prediction is conditional on the longitudinal history observed up to
prediction_time: rows of data_predict_all whose
time_variable is later than prediction_time are dropped,
with a warning, before predicting.
Usage
dynamicPredictionBio(
bio_i,
data_predict_all,
long_fit_all,
survival_fit_all,
prediction_time,
horizon,
time_variable,
survival_variable_all,
survival_trans_function,
bandcount2 = "auto",
bandcount3 = "auto"
)Arguments
- bio_i
Biomarker used to do prediction. May be continuous or ordinal (see Details).
- data_predict_all
This involves a collection of
data.frameobjects for dynamic prediction, each corresponding to a distinct longitudinal outcome. These data frames should contain the variables specified inlong_sub_fixedandlong_sub_random. Utilizing a list structure allows for the incorporation of multiple longitudinal outcomes, each potentially following different measurement protocols. In instances where all longitudinal outcomes are recorded at identical time points across patients, a singulardata.frameobject may be used in alist. Alternatively, a single baredata.frame(not wrapped in a list) may be supplied directly; it is then reused for every longitudinal outcome. It is presumed that each data frame is structured in a long format.- long_fit_all
Outputs from the model fitting process using the
nlmepackage, encompassing the results and parameters obtained from the analysis.- survival_fit_all
Results and parameters generated from the model fitting procedure, utilizing the
coxphfunction. These outputs include the comprehensive findings and variables derived from the analysis.- prediction_time
Time used to make the prediction
- horizon
Prediction horizon
- time_variable
The name of time variable in linear mixed model.
- survival_variable_all
The name of the transformed time-to-event outcomes variable.
- survival_trans_function
The transformation function used for time-to-event outcomes, in the order of
survival_variable_all.- bandcount2
The number of grid points spanning
[prediction_time + horizon, upper_bound], whereupper_boundis set internally as the earliest time by which every at-risk patient's model-based probability of still being event-free (given event-free atprediction_time + horizon) has dropped below1e-4; this approximates integrating out to infinity for the denominator that normalizes the predicted density. A wider follow-up range needs a largerbandcount2to keep the grid spacing comparable. Defaults to"auto"(see Details).- bandcount3
The number of points in the candidate-biomarker-value grid (
Y_all) used to build the predicted density (Y_density) and locate its mode (Y_predict). This controls the resolution of the density curve, not a time integral; increase it if the density looks jagged orY_predictjumps erratically between nearby grid points. Defaults to"auto"(see Details). Ignored whenbio_iis ordinal (see Details) – that candidate grid is always the biomarker's fixed category count.
Value
An object of class "predictLongitudinal.BJM", a named list with elements:
- Y_predict
A vector, one entry per at-risk patient (named by patient id), giving the MAP (most likely) predicted value of biomarker
bio_iatprediction_time + horizon. For an ordinalbio_i, this is an integer category code (see Details), not a raw value.- Y_density
A probability matrix whose rows correspond to the candidate biomarker values in
Y_alland whose columns correspond to individual patients; each entry is the dynamically predicted density of the biomarker taking that value. For an ordinalbio_i, each row is instead that category's predicted probability.- Y_all
The grid of candidate biomarker values used to build
Y_density. For an ordinalbio_i, this is the vector of integer category codes1:K, carrying a"category_labels"attribute with the matching level-label strings (see Details).
Details
There is no universal correct value for bandcount2/bandcount3:
as a practical check, double both and confirm the results barely change;
if they do, keep doubling. By default (bandcount2 = "auto",
bandcount3 = "auto"), this doubling check is done for you:
starting from small built-in values, both are doubled together, and the
result is compared to the previous round, until the largest relative
change in Y_predict drops below 1%, or 2 doublings have been
tried (so at most 3 calls' worth of work). If it still has not converged
by then, a warning reports this and the result at the largest value
tried is returned anyway (not an error), so this never silently loops
for an unbounded amount of time. Pass an explicit number for either
argument to skip auto-tuning it and use a fixed value instead (as in
previous package versions), or call checkBandcountConvergence()
directly for more control over the tolerance and doubling count. See
also vignette("BJM-intro", package = "BJM") for a worked example.