Title: | Event Prediction for Time-to-Event Endpoints |
---|---|
Description: | Implements the hybrid framework for event prediction described in Fang & Zheng (2011, <doi:10.1016/j.cct.2011.05.013>). To estimate the survival function the event prediction is based on, a piecewise exponential hazard function is fit to the time-to-event data to infer the potential change points. Prior to the last identified change point, the survival function is estimated using Kaplan-Meier, and the tail after the change point is fit using piecewise exponential. |
Authors: | Kaspar Rufibach |
Maintainer: | Kaspar Rufibach <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.0.3 |
Built: | 2025-01-12 04:20:40 UTC |
Source: | https://github.com/cran/eventTrack |
Implements the hybrid framework for event prediction described in Fang & Zheng (2011). To estimate the survival function the event prediction is based on, a piecewise Exponential hazard function is fit to the time-to-event data to infer the potential change points. Prior to the last identified change point, the survival function is estimated using Kaplan-Meier, and the tail after the change point is fit using piecewise Exponential. The Weibull version described in Fang and Zheng (2011) is not implemented here.
An example has been presented in this talk: https://baselbiometrics.github.io/home/docs/talks/20160428/1_Rufibach.pdf.
Package: | eventTrack |
Type: | Package |
Version: | 1.0.3 |
Date: | 2023-08-03 |
License: | GPL (>=2) |
LazyLoad: | yes |
The functions in this package have been written by Kaspar Rufibach and are generally to be considered experimental.
Kaspar Rufibach (maintainer)
[email protected]
# -------------------------------------------------- # simulate data # -------------------------------------------------- set.seed(2021) n <- 600 time0 <- rexp(n, rate = log(2) / 20) cens <- rexp(n, rate = log(2) / 50) time <- pmin(time0, cens) event <- as.numeric(time0 < cens) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # compute hybrid estimate and predict timepoint # -------------------------------------------------- plot(survfit(Surv(time, event) ~ 1), mark = "", xlim = c(0, 200), ylim = c(0, 1), conf.int = FALSE, xaxs = "i", yaxs = "i", main = "estimated survival functions", xlab = "time", ylab = "survival probability", col = grey(0.75), lwd = 5) # how far out should we predict monthly number of events? future.units <- 15 tab <- matrix(NA, ncol = 2, nrow = future.units) tab[, 1] <- 1:nrow(tab) ts <- seq(0, 100, by = 0.01) # -------------------------------------------------- # starting from a piecewise Exponential hazard with # K = 5 change points, infer the last "significant" # change point # -------------------------------------------------- pe5 <- piecewiseExp_MLE(time = time, event = event, K = 5) pe5.tab <- piecewiseExp_test_changepoint(peMLE = pe5, alpha = 0.05) cp.select <- max(c(0, as.numeric(pe5.tab[, "established change point"])), na.rm = TRUE) # the function predictEvents takes as an argument any survival function # hybrid exponential with cp.select St1 <- function(t0, time, event, cp){ return(hybrid_Exponential(t0 = t0, time = time, event = event, changepoint = cp))} pe1 <- predictEvents(time = time, event = event, St = function(t0){St1(t0, time = time, event = event, cp.select)}, accrual_after_ccod, future.units = future.units) tab[, 2] <- pe1[, 2] lines(ts, St1(ts, time, event, cp.select), col = 2, lwd = 2) # -------------------------------------------------- # compute exact date when we see targeted number of events # for hybrid Exponential model, through linear interpolation # -------------------------------------------------- exactDatesFromMonths(predicted = tab, 450)
# -------------------------------------------------- # simulate data # -------------------------------------------------- set.seed(2021) n <- 600 time0 <- rexp(n, rate = log(2) / 20) cens <- rexp(n, rate = log(2) / 50) time <- pmin(time0, cens) event <- as.numeric(time0 < cens) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # compute hybrid estimate and predict timepoint # -------------------------------------------------- plot(survfit(Surv(time, event) ~ 1), mark = "", xlim = c(0, 200), ylim = c(0, 1), conf.int = FALSE, xaxs = "i", yaxs = "i", main = "estimated survival functions", xlab = "time", ylab = "survival probability", col = grey(0.75), lwd = 5) # how far out should we predict monthly number of events? future.units <- 15 tab <- matrix(NA, ncol = 2, nrow = future.units) tab[, 1] <- 1:nrow(tab) ts <- seq(0, 100, by = 0.01) # -------------------------------------------------- # starting from a piecewise Exponential hazard with # K = 5 change points, infer the last "significant" # change point # -------------------------------------------------- pe5 <- piecewiseExp_MLE(time = time, event = event, K = 5) pe5.tab <- piecewiseExp_test_changepoint(peMLE = pe5, alpha = 0.05) cp.select <- max(c(0, as.numeric(pe5.tab[, "established change point"])), na.rm = TRUE) # the function predictEvents takes as an argument any survival function # hybrid exponential with cp.select St1 <- function(t0, time, event, cp){ return(hybrid_Exponential(t0 = t0, time = time, event = event, changepoint = cp))} pe1 <- predictEvents(time = time, event = event, St = function(t0){St1(t0, time = time, event = event, cp.select)}, accrual_after_ccod, future.units = future.units) tab[, 2] <- pe1[, 2] lines(ts, St1(ts, time, event, cp.select), col = 2, lwd = 2) # -------------------------------------------------- # compute exact date when we see targeted number of events # for hybrid Exponential model, through linear interpolation # -------------------------------------------------- exactDatesFromMonths(predicted = tab, 450)
Bootstrap the predicted time when a given number of events is reached, based on the hybrid Exponential model.
bootstrapTimeToEvent(time, event, interim.gates, future.units, n, M0 = 1000, K0 = 5, alpha = 0.05, accrual, seed = 2014)
bootstrapTimeToEvent(time, event, interim.gates, future.units, n, M0 = 1000, K0 = 5, alpha = 0.05, accrual, seed = 2014)
time |
Event times. |
event |
Censoring indicator, 0 = censored, 1 = event. |
interim.gates |
Number of events for which confidence intervals should be computed. May be a vector. |
future.units |
Number of months for which predictions are to be made. |
n |
Size of the bootstrap samples to be drawn from |
M0 |
Number of bootstrap samples to be drawn. |
K0 |
Number of changepoints for the piecewise constant hazard. |
alpha |
Familywise error rate for the sequential test. |
accrual |
Vector of dates when future patients enter the study. As for the specification, assume 50 pts are to be recruited in Dec 2013 and 20pts in Jan 2014, then use |
seed |
Seed for generation of bootstrap samples. |
A list containing the following objects:
pe.MLEs |
Piecewise Exponential MLE object for each bootstrap sample, output of function |
pe.tabs |
Result of sequential test for each bootstrap sample, output of function |
changepoints |
For each bootstrap sample, changepoint as resulting from sequential test. |
estS |
Estimated survival function for each bootstrap sample. |
event.dates |
Event dates for each bootstrap sample, where columns relate to the number of events in |
Kaspar Rufibach (maintainer)
[email protected]
Rufibach, K. (2016). Event projection: quantify uncertainty and manage expectations of broader teams. Slides for talk given in Basel Biometric Section Seminar on 28th April 2016. https://baselbiometrics.github.io/home/docs/talks/20160428/1_Rufibach.pdf.
## Not run: # -------------------------------------------------- # simulate data for illustration # -------------------------------------------------- set.seed(2021) n <- 600 time <- rexp(n, rate = log(2) / 20) event <- sample(round(runif(n, 0, 1))) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # run bootstrap, for M0 = 3 only, for illustration # tune parameters for your own example # -------------------------------------------------- boot1 <- bootstrapTimeToEvent(time, event, interim.gates = c(330, 350), future.units = 50, n = length(time), M0 = 3, K0 = 5, alpha = 0.05, accrual = accrual_after_ccod, seed = 2014) # median of bootstrap samples: apply(boot1$event.dates, 2, median) ## End(Not run)
## Not run: # -------------------------------------------------- # simulate data for illustration # -------------------------------------------------- set.seed(2021) n <- 600 time <- rexp(n, rate = log(2) / 20) event <- sample(round(runif(n, 0, 1))) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # run bootstrap, for M0 = 3 only, for illustration # tune parameters for your own example # -------------------------------------------------- boot1 <- bootstrapTimeToEvent(time, event, interim.gates = c(330, 350), future.units = 50, n = length(time), M0 = 3, K0 = 5, alpha = 0.05, accrual = accrual_after_ccod, seed = 2014) # median of bootstrap samples: apply(boot1$event.dates, 2, median) ## End(Not run)
Generate bootstrap samples from a survival object, for right-censored data. See Efron (1981) and Akritas (1986) for details.
bootSurvivalSample(surv.obj, n, M = 1000)
bootSurvivalSample(surv.obj, n, M = 1000)
surv.obj |
|
n |
Size of the bootstrap samples to be drawn from |
M |
Number of bootstrap samples to be drawn. |
Matrix with bootstrap samples as columns.
Kaspar Rufibach (maintainer)
[email protected]
Akritas, M.G. (1986). Bootstrapping the Kaplan-Meier Estimator. JASA, 81, 1032-1038
Efron, B. (1981). Censored Data and the Bootstrap. JASA, 76, 312-319.
# -------------------------------------------------- # simulate data for illustration # -------------------------------------------------- set.seed(2021) n <- 600 time <- rexp(n, rate = log(2) / 20) event <- sample(round(runif(n, 0, 1))) # -------------------------------------------------- # draw 20 bootstrap samples of size 10 # -------------------------------------------------- bootSurvivalSample(surv.obj = Surv(time, event), n = 10, M = 20)
# -------------------------------------------------- # simulate data for illustration # -------------------------------------------------- set.seed(2021) n <- 600 time <- rexp(n, rate = log(2) / 20) event <- sample(round(runif(n, 0, 1))) # -------------------------------------------------- # draw 20 bootstrap samples of size 10 # -------------------------------------------------- bootSurvivalSample(surv.obj = Surv(time, event), n = 10, M = 20)
Based on monhtly number of events, compute exact day when the pre-specified number of events happens, using simple linear interpolation.
exactDatesFromMonths(predicted, nevent)
exactDatesFromMonths(predicted, nevent)
predicted |
|
nevent |
Number of targeted events. |
The exact date.
Kaspar Rufibach (maintainer)
[email protected]
This function estimates the values of the survival function as a hybrid of Kaplan-Meier for times smaller than the specified change point and an Exponential fit to the tail of the survival function. The Exponential tail fit is computed assuming a piecewise constant hazard with one change point.
hybrid_Exponential(t0, time = time, event = event, changepoint)
hybrid_Exponential(t0, time = time, event = event, changepoint)
t0 |
Value at which to compute value of survival function. Can be a vector. |
time |
Event times, censored or observed, in months. |
event |
Censoring indicator, 1 for event, 0 for censored. |
changepoint |
Pre-specified change point. |
A vector of the same dimension as t0
containing the values of the estimated survival function at t0
.
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
Rufibach, K. (2016). Event projection: quantify uncertainty and manage expectations of broader teams. Slides for talk given in Basel Biometric Section Seminar on 28th April 2016. https://baselbiometrics.github.io/home/docs/talks/20160428/1_Rufibach.pdf.
# -------------------------------------------------- # simulate data # -------------------------------------------------- set.seed(2021) n <- 600 time0 <- rexp(n, rate = log(2) / 20) cens <- rexp(n, rate = log(2) / 50) time <- pmin(time0, cens) event <- as.numeric(time0 < cens) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # compute hybrid estimate and predict timepoint # -------------------------------------------------- plot(survfit(Surv(time, event) ~ 1), mark = "", xlim = c(0, 200), ylim = c(0, 1), conf.int = FALSE, xaxs = "i", yaxs = "i", main = "estimated survival functions", xlab = "time", ylab = "survival probability", col = grey(0.75), lwd = 5) # how far out should we predict monthly number of events? future.units <- 15 tab <- matrix(NA, ncol = 2, nrow = future.units) tab[, 1] <- 1:nrow(tab) ts <- seq(0, 100, by = 0.01) # -------------------------------------------------- # starting from a piecewise Exponential hazard with # K = 5 change points, infer the last "significant" # change point # -------------------------------------------------- pe5 <- piecewiseExp_MLE(time = time, event = event, K = 5) pe5.tab <- piecewiseExp_test_changepoint(peMLE = pe5, alpha = 0.05) cp.select <- max(c(0, as.numeric(pe5.tab[, "established change point"])), na.rm = TRUE) # the function predictEvents takes as an argument any survival function # hybrid exponential with cp.select St1 <- function(t0, time, event, cp){ return(hybrid_Exponential(t0 = t0, time = time, event = event, changepoint = cp))} pe1 <- predictEvents(time = time, event = event, St = function(t0){St1(t0, time = time, event = event, cp.select)}, accrual_after_ccod, future.units = future.units) tab[, 2] <- pe1[, 2] lines(ts, St1(ts, time, event, cp.select), col = 2, lwd = 2) # -------------------------------------------------- # compute exact date when we see targeted number of events # for hybrid Exponential model, through linear interpolation # -------------------------------------------------- exactDatesFromMonths(predicted = tab, 450)
# -------------------------------------------------- # simulate data # -------------------------------------------------- set.seed(2021) n <- 600 time0 <- rexp(n, rate = log(2) / 20) cens <- rexp(n, rate = log(2) / 50) time <- pmin(time0, cens) event <- as.numeric(time0 < cens) accrual_after_ccod <- 1:(n - length(time)) / 30 # -------------------------------------------------- # compute hybrid estimate and predict timepoint # -------------------------------------------------- plot(survfit(Surv(time, event) ~ 1), mark = "", xlim = c(0, 200), ylim = c(0, 1), conf.int = FALSE, xaxs = "i", yaxs = "i", main = "estimated survival functions", xlab = "time", ylab = "survival probability", col = grey(0.75), lwd = 5) # how far out should we predict monthly number of events? future.units <- 15 tab <- matrix(NA, ncol = 2, nrow = future.units) tab[, 1] <- 1:nrow(tab) ts <- seq(0, 100, by = 0.01) # -------------------------------------------------- # starting from a piecewise Exponential hazard with # K = 5 change points, infer the last "significant" # change point # -------------------------------------------------- pe5 <- piecewiseExp_MLE(time = time, event = event, K = 5) pe5.tab <- piecewiseExp_test_changepoint(peMLE = pe5, alpha = 0.05) cp.select <- max(c(0, as.numeric(pe5.tab[, "established change point"])), na.rm = TRUE) # the function predictEvents takes as an argument any survival function # hybrid exponential with cp.select St1 <- function(t0, time, event, cp){ return(hybrid_Exponential(t0 = t0, time = time, event = event, changepoint = cp))} pe1 <- predictEvents(time = time, event = event, St = function(t0){St1(t0, time = time, event = event, cp.select)}, accrual_after_ccod, future.units = future.units) tab[, 2] <- pe1[, 2] lines(ts, St1(ts, time, event, cp.select), col = 2, lwd = 2) # -------------------------------------------------- # compute exact date when we see targeted number of events # for hybrid Exponential model, through linear interpolation # -------------------------------------------------- exactDatesFromMonths(predicted = tab, 450)
Compute value of Kaplan-Meier estimate at a given time .
kaplanMeier_at_t0(time, event, t0)
kaplanMeier_at_t0(time, event, t0)
time |
Event times, censored or observed. |
event |
Censoring indicator, 1 for event, 0 for censored. |
t0 |
Vector (or single number) of time points to compute confidence interval for. |
Matrix with values of Kaplan-Meier estimate at .
Kaspar Rufibach (maintainer)
[email protected]
# use Acute Myelogenous Leukemia survival data contained in package 'survival' time <- leukemia[, 1] status <- leukemia[, 2] tmp <- Surv(time, status) ~ 1 plot(survfit(tmp, conf.type = "none"), mark = "/", col = 1:2) kaplanMeier_at_t0(time, status, t0 = c(10, 25, 50))
# use Acute Myelogenous Leukemia survival data contained in package 'survival' time <- leukemia[, 1] status <- leukemia[, 2] tmp <- Surv(time, status) ~ 1 plot(survfit(tmp, conf.type = "none"), mark = "/", col = 1:2) kaplanMeier_at_t0(time, status, t0 = c(10, 25, 50))
For given change points , compute the profile maximum likelihood estimates
for the values of the piecewise constant hazard function in a piecewise Exponential survival model. Standard errors for these estimates are provided as well, based on standard maximum (profile) maximum likelihood theory.
lambda_j_Exp(tau, time, event)
lambda_j_Exp(tau, time, event)
tau |
Single number or vector with values of change points. |
time |
Event times, censored or observed, in months. |
event |
Censoring indicator, 1 for event, 0 for censored. |
A list containing the following elements:
aj |
The esimtates of the |
hess.diag |
The diagonal of the corresponding Hessian matrix. Note that the off-diagonal elements of the Hessian are all equal to 0. |
This function is not intended to be invoked by the end user.
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
This function estimates the values of the hazard function and the change points in a piecewise Exponential survival model. The number of change points needs to be pre-specified.
piecewiseExp_MLE(time, event, K)
piecewiseExp_MLE(time, event, K)
time |
Event times, censored or observed, in months. |
event |
Censoring indicator, 1 for event, 0 for censored. |
K |
Number of change points to be used in the model. |
A list containing the following objects:
tau |
The maximum likelihood estimates of the change points. |
lambda |
The maximum likelihood estimates of the value of the hazard function. |
lambda.SE |
The standard errors of |
K |
Number of change points to be used in the model. |
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
# see vignette
# see vignette
In a piecewise Exponential survival model, the estimates for the value of the hazard function can be given explicitly, see Fang and Su (2011), and can be computed using lambda_j_Exp
. The values of the change points can then be computed using the profile log-likelihood function. The function piecewiseExp_profile_loglik_tau
computes the value of this profile log-likelihood function and can be used together with optim
to compute the change points .
piecewiseExp_profile_loglik_tau(tau, time, event)
piecewiseExp_profile_loglik_tau(tau, time, event)
tau |
Single number or vector with values of change points. |
time |
Event times, censored or observed, in months. |
event |
Censoring indicator, 1 for event, 0 for censored. |
Value of the profile likelihood function.
This function is not intended to be invoked by the end user.
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
This function implements the Wald test described in Goodman et al (2011) and applied in Fang & Zheng (2011) to infer a change point in an estimated piecewise Exponential hazard function. Adjusts for sequential testing.
piecewiseExp_test_changepoint(peMLE, alpha = 0.05)
piecewiseExp_test_changepoint(peMLE, alpha = 0.05)
peMLE |
Piecewise Exponential hazard function estimate, as generated by |
alpha |
Overall significance level. Will be adjusted for sequential testing internally, see function output. |
A data.frame
containing the sequential test results.
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
# see vignette
# see vignette
Based on a specified survival function and potential future accrual, compute for each month in the future the number of expected events reached by then.
predictEvents(time, event, St, accrual, future.units = 50)
predictEvents(time, event, St, accrual, future.units = 50)
time |
Event times, censored or observed, in months. |
event |
Censoring indicator, 1 for event, 0 for censored. |
St |
Function that specifies the survival function to be used. |
accrual |
|
future.units |
Number of future months to compute prediction for. |
A data.frame
with the months and corresponding expected events.
Kaspar Rufibach (maintainer)
[email protected]
Fang, L., Zheng, S. (2011). A hybrid approach to predicting events in clinical trials with time-to-event outcomes. Contemp. Clin. Trials, 32, 755–759.
Goodman, M.S., Li, Y., Tiwari, R.C. (2011). Detecting multiple change points in piecewise constant hazard functions. J. Appl. Stat, 38(11), 2523–2532.
# see vignette
# see vignette
Based on a specified survival function and future accrual, compute for each month in the future the number of expected events reached by then, based on a fixed pre-specified survival function.
predictEventsUncond(St, accrual, future.units = 50)
predictEventsUncond(St, accrual, future.units = 50)
St |
Function that specifies the survival function to be used. |
accrual |
|
future.units |
Number of future months to compute prediction for. |
A data.frame
with the months and corresponding expected events.
Kaspar Rufibach (maintainer)
[email protected]
# compute date when 380 events are reached: nevent <- 380 n <- 800 accrual.month <- 50 accrual <- rep(seq(1, 16), each = accrual.month) rate <- log(2) / 12 St1 <- function(t0){ res <- 1 - pexp(t0, rate = rate) return(res) } pred1 <- predictEventsUncond(St = St1, accrual, future.units = 25) pred1 exactDatesFromMonths(predicted = pred1, nevent)
# compute date when 380 events are reached: nevent <- 380 n <- 800 accrual.month <- 50 accrual <- rep(seq(1, 16), each = accrual.month) rate <- log(2) / 12 St1 <- function(t0){ res <- 1 - pexp(t0, rate = rate) return(res) } pred1 <- predictEventsUncond(St = St1, accrual, future.units = 25) pred1 exactDatesFromMonths(predicted = pred1, nevent)