This vignette describes the practical contract used by
SuperSurv() to call survival learners and screening
methods. The goal is to make small extensions possible without changing
the ensemble algorithm.
Learners are supplied to SuperSurv() by name:
During cross-validation and final fitting, SuperSurv()
looks up each name as an R function and calls it directly. A custom
learner can therefore live in a user script, package, or analysis file,
as long as it is available in the R session before calling
SuperSurv().
A survival learner wrapper should have this shape:
my.surv.learner <- function(time, event, X, newdata, new.times,
obsWeights, id, ...) {
# fit model on X, time, event
# predict survival probabilities for newdata at new.times
list(pred = pred_matrix, fit = fitted_object)
}The required arguments are:
| Argument | Meaning |
|---|---|
time |
numeric observed follow-up time |
event |
event indicator, with 1 for the event and
0 for censoring |
X |
training covariates as a data frame |
newdata |
covariates for prediction as a data frame |
new.times |
time grid where survival probabilities are needed |
obsWeights |
observation weights, possibly NULL |
id |
optional cluster or subject identifier, possibly
NULL |
... |
learner-specific tuning parameters |
The returned pred must be a numeric matrix with:
newdata,new.times,0 and 1.If possible, each row should be non-increasing across time. Wrappers
commonly clamp small numerical drift back into [0, 1].
The returned fit can be any object needed for future
prediction. When control = list(saveFitLibrary = TRUE),
SuperSurv() stores these fitted objects and
predict.SuperSurv() later calls predict() on
them.
SuperSurv validates this contract at each cross-validation fold,
during the full-data refit, and when predicting on new data. If a custom
learner returns the wrong object type, omits pred or a
required saved fit, returns a matrix with the wrong
dimensions, or produces non-finite values or probabilities outside
[0, 1], the error identifies the learner, fitting stage,
and expected shape. Screener outputs are similarly required to be
complete logical vectors with one entry per input feature and at least
one selected feature.
If a custom learner should support prediction on new data after
fitting, store a classed object in fit and define a
matching S3 prediction method:
This method should return the same type of matrix as the wrapper’s
pred component: nrow(newdata) rows by
length(new.times) columns.
Some survival learners directly return survival curves. In that case,
the wrapper usually interpolates the native prediction grid to
new.times.
Other learners return a risk score or linear predictor. Those wrappers need one extra calibration step:
newdata,S(t | x) = exp(-exp(eta(x)) H0(t)).Several built-in Cox-style wrappers follow this pattern. The internal
helper safe_breslow_step() is used by some package
wrappers, but it is not exported; custom wrappers should either use a
standard package prediction method that already returns survival curves
or implement their own baseline-hazard calibration carefully.
The following adapters use the same wrapper contract and remain optional:
| Wrapper | Backend | Prediction route | Observation weights |
|---|---|---|---|
surv.mboost |
mboost |
risk score plus weighted baseline-hazard calibration | supported |
surv.flexsurvspline |
flexsurv |
native flexible-parametric survival curve | supported |
surv.flexsurvreg |
flexsurv |
native parametric survival curve, including generalized gamma | supported |
surv.survPen |
survPen |
native penalized smooth hazard survival curve | uniform only |
surv.grf |
grf |
native generalized random-forest survival curve | supported |
surv.deepsurv |
survivalmodels and Python pycox |
native Cox neural-network survival curve | uniform only |
surv.deephit |
survivalmodels and Python pycox |
native discrete-time neural-network survival curve | uniform only |
surv.coxtime |
survivalmodels and Python pycox |
native neural survival curve with time-dependent effects | uniform only |
The DeepSurv, DeepHit, and Cox-Time adapters remain experimental;
local smoke tests do not establish portability across R/Python
configurations. Their backend spans R and Python. They require
survivalmodels, reticulate, and importable
Python modules torch, torchtuples, and
pycox. SuperSurv never installs or changes a Python
environment automatically. These adapters stop with an explicit error
when the backend is unavailable or when nonuniform
obsWeights are supplied; silently ignoring case weights
would change the requested fit.
Python fitted objects can be reused within the active R/Python
session, but plain saveRDS() is not a portable way to store
them. Prediction after restoring an invalid Python reference gives an
explicit error. Native neural curves are mapped as right-continuous
steps to requested times; extending the grid past observed follow-up
does not establish reliable extrapolation.
All of these wrappers can be tuned through create_grid()
in the same way as other learners. Test a small direct wrapper fit
before launching cross-validation, especially for a Python-backed
learner.
surv.survPen uses a smooth baseline log hazard and
linear feature effects by default. Supply a one-sided formula for
nonlinear or time-varying effects, using .supersurv_time
for follow-up time. For example:
# X contains x1 and x2. Use screen.all if naming features explicitly.
smooth_fit <- surv.survPen(
time, event, X, newdata, new.times,
formula = ~ tensor(.supersurv_time, x1, df = c(4, 4)) + x2
)The smooth-model adapter rejects nonuniform observation weights
because its backend does not expose case-weighted fitting. It is
restricted to overall single-event, right-censored survival, not
relative survival or left truncation. The quadrature order
n.legendre controls numerical integration accuracy.
The following learner ignores covariates and fits a weighted
Kaplan-Meier curve. It is intentionally simple, but it satisfies the
full SuperSurv() learner contract.
my.surv.km <- function(time, event, X, newdata, new.times,
obsWeights = NULL, id = NULL, ...) {
if (is.null(obsWeights)) {
obsWeights <- rep(1, length(time))
}
fit_km <- survival::survfit(
survival::Surv(time, event) ~ 1,
weights = obsWeights
)
step_surv <- stats::stepfun(fit_km$time, c(1, fit_km$surv), right = FALSE)
surv_probs <- step_surv(new.times)
pred <- matrix(
surv_probs,
nrow = nrow(newdata),
ncol = length(new.times),
byrow = TRUE
)
fit <- list(object = fit_km)
class(fit) <- "my.surv.km"
list(pred = pred, fit = fit)
}
predict.my.surv.km <- function(object, newdata, new.times, ...) {
fit_km <- object$object
step_surv <- stats::stepfun(fit_km$time, c(1, fit_km$surv), right = FALSE)
surv_probs <- step_surv(new.times)
matrix(
surv_probs,
nrow = nrow(newdata),
ncol = length(new.times),
byrow = TRUE
)
}You can test the wrapper outside the ensemble before adding it to a library:
data("metabric", package = "SuperSurv")
dat <- metabric[1:40, ]
x_cols <- grep("^x", names(dat))[1:3]
X <- dat[, x_cols, drop = FALSE]
times <- seq(50, 150, by = 50)
wrapper_out <- my.surv.km(
time = dat$duration,
event = dat$event,
X = X,
newdata = X[1:5, , drop = FALSE],
new.times = times
)
dim(wrapper_out[["pred"]])
#> [1] 5 3
dim(predict(wrapper_out[["fit"]], newdata = X[1:5, , drop = FALSE], new.times = times))
#> [1] 5 3The custom learner can be mixed with built-in learners:
Screening methods are also supplied by name. A screener should accept
the same basic training inputs and return a logical vector with one
value per column of X:
my.screen.first3 <- function(time, event, X, obsWeights = NULL, id = NULL, ...) {
keep <- rep(FALSE, ncol(X))
keep[seq_len(min(3, ncol(X)))] <- TRUE
names(keep) <- names(X)
keep
}Use a list entry to pair a learner with one or more screeners:
Before using a custom wrapper in a large ensemble, check:
pred is a numeric matrix with dimensions
nrow(newdata) by length(new.times),[0, 1],predict(wrapper_out[["fit"]], newdata, new.times)
returns the same shape when saved fits are needed,obsWeights = NULL and
id = NULL,requireNamespace(),X.These rules are deliberately small. They allow new learners to be added without changing the SuperSurv ensemble machinery.