Title: Tipping Point Analysis for Survival Endpoints
Version: 1.1
Description: Implements tipping point sensitivity analysis for time-to-event endpoints under different missing data scenarios, as described in Oodally et al. (2025) <doi:10.48550/arXiv.2506.19988>. Supports both model-based and model-free imputation, multiple imputation workflows, plausibility assessment and visualizations. Enables robust assessment for regulatory and exploratory analyses.
License: GPL (≥ 3)
Encoding: UTF-8
RoxygenNote: 7.3.2
Depends: R (≥ 3.5)
LazyData: true
Imports: MASS, ggplot2, survival, dplyr, stats, utils, knitr, purrr, rmarkdown
VignetteBuilder: knitr
Suggests: testthat (≥ 3.0.0)
Config/testthat/edition: 3
NeedsCompilation: no
Packaged: 2025-12-16 12:58:53 UTC; oodalaj1
Author: Ajmal Oodally ORCID iD [cre, aut], Craig Wang ORCID iD [aut], Zheng Li ORCID iD [ctb]
Maintainer: Ajmal Oodally <ajmal.oodally@novartis.com>
Repository: CRAN
Date/Publication: 2025-12-19 20:10:02 UTC

tipse: Tipping Point Analysis for Survival Endpoints

Description

Implements tipping point sensitivity analysis for time-to-event endpoints under different missing data scenarios, as described in Oodally et al. (2025) doi:10.48550/arXiv.2506.19988. Supports both model-based and model-free imputation, multiple imputation workflows, plausibility assessment and visualizations. Enables robust assessment for regulatory and exploratory analyses.

Author(s)

Maintainer: Ajmal Oodally ajmal.oodally@novartis.com (ORCID)

Authors:

Other contributors:


Assess Clinical Plausibility of Imputation Results

Description

This function facilitate the evaluation of clinical plausibility at the tipping point. It provides a text summary comparing event rates, follow-up duration, or hazard ratios between treatment arms depending on the imputation method and arm specified.

Usage

assess_plausibility(tipse, verbose = TRUE)

Arguments

tipse

A tipse object returned by one of tipping_point_model_free or tipping_point_model_based.

verbose

Logical. If TRUE, prints assessment details.

Value

A character string summarizing the key information to facilitate clinical plausibility assessment based on the imputation scenario.

Examples

cox1 <- survival::coxph(Surv(AVAL, EVENT) ~ TRT01P, data = codebreak200)
result <- tipping_point_model_free(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  cox_fit = cox1,
  method = "random sampling"
)

assess_plausibility(result)


Average Kaplan-Meier Curves Across Multiple Imputed Datasets

Description

Takes a list of multiply imputed datasets corresponding to a single tipping point parameter and pools the Kaplan-Meier survival curves for a given treatment arm. Uses log(-log) transformation and Rubin's rules for pooling across imputations.

Usage

average_km(km_data, arm, conf_level = 0.95)

Arguments

km_data

List of data frames, each containing one multiply imputed dataset for a tipping point. Each data frame must contain columns AVAL, EVENT, TRT01P, iter, and a tipping point parameter depending on the method.

arm

Character string specifying the treatment arm to pool (must match TRT01P levels).

conf_level

Numeric. Confidence level for CIs (default = 0.95).

Value

A data frame with the following columns:

time

Time points of the KM curve.

survival_comb

Pooled survival probability at each time point.

survival_lcl_comb

Lower 95% confidence limit of pooled survival.

survival_ucl_comb

Upper 95% confidence limit of pooled survival.

stderr

Standard error of the pooled log-log transformed estimate.


Patient level data from dummy trial

Description

Based on re-constructed Kaplan-Meier plot from CodeBreak 200 trial (de Langen et al., 2023)

Usage

codebreak200

Format

A data frame with 345 rows and 5 columns:

SUBJID

Dummy patient ID

TRT01P

Treatment arm (Sotorasib or Docetaxel)

AVAL

PFS time in days

EVENT

Indicator for PFS event

CNSRRS

Censoring reason (Early dropout or Other)

MAXAVAL

Maximum potential survival time, duration between randomization to data cut-off

Source

De Langen, A.J., Johnson, M.L., Mazieres, J., Dingemans, A.M.C., Mountzios, G., Pless, M., Wolf, J., Schuler, M., Lena, H., Skoulidis, F. and Yoneshima, Y., 2023. Sotorasib versus docetaxel for previously treated non-small-cell lung cancer with KRASG12C mutation: a randomised, open-label, phase 3 trial. The Lancet, 401(10378), pp.733-746.


Patient level data from dummy trial

Description

Based on re-constructed Kaplan-Meier plot from ExteNET trial (Martin et al., 2017)

Usage

extenet

Format

A data frame with 2840 rows and 5 columns:

SUBJID

Dummy patient ID

TRT01P

Treatment arm (Neratinib or placebo)

AVAL

iDFS time in days

EVENT

Indicator for iDFS event

CNSRRS

Censoring reason (Lost to follow-up or Other)

MAXAVAL

Maximum potential survival time, duration between randomization to data cut-off

Source

Martin, M., Holmes, F.A., Ejlertsen, B., Delaloge, S., Moy, B., Iwata, H., von Minckwitz, G., Chia, S.K., Mansi, J., Barrios, C.H. and Gnant, M., 2017. Neratinib after trastuzumab-based adjuvant therapy in HER2-positive breast cancer (ExteNET): 5-year analysis of a randomised, double-blind, placebo-controlled, phase 3 trial. The lancet oncology, 18(12), pp.1688-1700.


Fit parametric model for selected subjects

Description

Fit parametric model for selected subjects

Usage

fit_model(dat, reason, impute, imputation_model = c("weibull", "exponential"))

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

a string specifying the censoring reasons which require imputation. It must be one of the reasons from column CNSRRS.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from column TRT01P.

imputation_model

a string specifying the parametric distribution used for imputation, can be "Weibull" or "exponential".

Details

The data.frame contains original columns, plus the following columns appended:

AVAL4 Placeholder column to keep imputed survival times
EVENT4 Placeholder column to keep imputed events
impute Flag indicating whether the subject was selected for imputation
a Shape parameter, equal to 1 if exponential
b Scale parameter
cdf Cumulative distribution function
... Some temporary columns

Value

data.frame with flags and fitted model parameters to be used for imputation


Model-free imputation via deterministic sampling

Description

patients will be assigned deterministically an event time at the time of censoring or extend the censoring time to the potential maximum follow-up of each patient.

Usage

impute_deterministic(dat, reason, impute, npts, J, seed)

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

a string specifying the censoring reasons which require imputation. It must be one of the reasons from variable CNSRRS.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from variable TRT01P, the first level of TRT01P is considered as the control arm.

npts

number of patients to be imputed

J

numeric indicating number of imputations.

seed

Integer. Random seed for reproducibility.

Details

patients will be assigned deterministically an event time at the time of censoring or extend the censoring time to the potential maximum follow-up of each patient.

Value

a list of data.frame from each imputation with imputed AVAL and EVENT, where original variables are kept as AVAL and EVENT.


Model-based imputation from parametric distributions

Description

Impute data with Weibull or exponential distribution conditional on follow-up time

Usage

impute_model(
  dat,
  reason,
  impute,
  imputation_model = c("weibull", "exponential"),
  alpha,
  J,
  seed = 12345
)

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

a string specifying the censoring reasons which require imputation. It must be one of the reasons from variable CNSRRS.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from variable TRT01P, the first level of TRT01P is considered as the control arm.

imputation_model

a string specifying the parametric distribution used for imputation, can be "Weibull" or "exponential".

alpha

hazard inflation (if treatment arm is imputed) or deflation (if control arm is imputed) rate

J

numeric indicating number of imputations.

seed

Integer. Random seed for reproducibility.

Details

First fit model based on the data without dropout. And then impute the the survival outcome based on exponential or Weibull distribution for those who dropped out.

Value

a list of data.frame from each imputation with imputed AVAL and EVENT, where original variables are kept as AVALo and EVENTo.


Model-free imputation via random sampling

Description

randomly sample from the percentile of best or worst patients (ordered by their observed times regardless of event or censoring) who do not require imputation.

Usage

impute_random(dat, reason, impute, percentile, J, seed = 12345)

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

a string specifying the censoring reasons which require imputation. It must be one of the reasons from variable CNSRRS.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from variable TRT01P, the first level of TRT01P is considered as the control arm.

percentile

numeric between 1 and 100, indicating the best (or worst) percentile of subjects to sample from.

J

numeric indicating number of imputations.

seed

Integer. Random seed for reproducibility.

Details

We define two sets of subjects to sample from depending on the impute argument:

  1. Worst percentile of observations from treatment arm \forall i \in N \mid \min\{T_i, C_i\} \leq F_{\min\{T_i, C_i\}}^{-1}(\kappa) . This set includes all indices i where the minimum of T_i (event time) and C_i (censoring time) is less than or equal to the \kappa-th percentile of its distribution.

  2. Best percentile of observations control arm \forall i \in N \mid \min\{T_i, C_i\} \geq F_{\min\{T_i, C_i\}}^{-1}(\kappa) . This set includes all indices i where the minimum of T_i and C_i is greater than or equal to the \kappa-th percentile of its distribution.

where F(\cdot) denotes the cumulative distribution function (CDF) of the observed times and F^{-1}(\kappa) is the inverse CDF (quantile function) at percentile \kappa.

Value

a list of data.frame from each imputation with imputed AVAL and EVENT, where original variables are kept as AVALo and EVENTo.


Plot Pooled Kaplan–Meier Curves from Model-Free Tipping Point Analysis

Description

Visualizes averaged (pooled) Kaplan-Meier survival curves across multiple tipping point parameters, highlighting the tipping point where the upper CL of the hazard ratio crosses 1.

Usage

## S3 method for class 'tipse'
plot(x, type = c("Kaplan-Meier", "Tipping Point"), ...)

Arguments

x

An S3 object of class "tipse" returned from tipping_point_model_free or tipping_point_model_based.

type

Type of plot, either "Kaplan-Meier" or "Tipping Point".

...

Additional arguments not used.

Details

Value

A ggplot2 object displaying pooled Kaplan–Meier curves.

References

Marshall, A., Altman, D.G., Holder, R.L. et al. Combining estimates of interest in prognostic modelling studies after multiple imputation: current practice and guidelines. BMC Med Res Methodol 9, 57 (2009). https://doi.org/10.1186/1471-2288-9-57

Examples

cox1 <- survival::coxph(Surv(AVAL, EVENT) ~ TRT01P, data = codebreak200)
result <- tipping_point_model_based(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  imputation_model = "weibull",
  J = 10,
  tipping_range = seq(0.1, 1, by = 0.05),
  cox_fit = cox1,
  verbose = TRUE,
  seed = 12345
)
plot(result, type = "Kaplan-Meier")
plot(result, type = "Tipping Point")

Plot Pooled Kaplan–Meier Curves from Model-Free Tipping Point Analysis

Description

Visualizes averaged (pooled) Kaplan-Meier survival curves across multiple tipping point parameters, highlighting the tipping point where the upper CL of the hazard ratio crosses 1.

Usage

plot_km(tipse)

Arguments

tipse

An S3 object of class "tipse" returned from tipping_point_model_free() or tipping_point_model_based().

Value

A ggplot2 object displaying pooled Kaplan-Meier curves, with:


Plot Model-Free Tipping Point Results

Description

Visualizes the hazard ratios and confidence intervals across tipping point parameters from model-free analyses, for both random sampling (% best event times) and deterministic sampling (number of patients event-free at DCO). Highlights the tipping point where the upper confidence interval crosses 1.

Usage

plot_tp(tipse)

Arguments

tipse

An S3 object of class "tipse" returned from tipping_point_model_free() or tipping_point_model_based().

Value

A ggplot2 object showing HR and CI across tipping point parameters.


Pooling results using Rubin's Rule

Description

Pooling results from multiple imputations using Rubin's Rule

Usage

pool_results(dat, cox.fit, conf.level = 0.95)

Arguments

dat

a list of data.frames from multiple imputation using one alpha or kappa parameter

cox.fit

a coxph object which is used to compute HRs for each imputed datasets

conf.level

confidence level for the returned confidence interval, default to be 0.95.

Details

The Rubin's rule is applied to the Cox PH model results across imputed datasets as:

  1. Compute pooled HR:

    \bar{HR}_\lambda = \exp\Bigg(\frac{1}{M} \sum_{m=1}^{M} \log(HR_m)\Bigg)

  2. Compute pooled variance:

    \bar{\sigma}_\lambda^2 = \frac{1}{M} \sum_{m=1}^{M} \sigma_m^2 + \frac{1 + \frac{1}{M}}{M-1} \sum_{m=1}^{M} \big(\log(HR_m) - \overline{\log(HR_\lambda)}\big)^2

  3. Compute CI:

    \bar{HR}_\lambda \times \exp\big(\pm z_{\alpha/2} \sqrt{\bar{\sigma}_\lambda^2}\big)

Value

a data.frame of pooled hazard ratio and confidence interval estimate using Rubin's Rule


Print method for plausibility assessment

Description

Print method for plausibility assessment

Usage

## S3 method for class 'plausibility_assessment'
print(x, ...)

Arguments

x

An object of class "plausibility_assessment"

...

Further arguments passed to or from other methods.


Summarize Tipping Point Results (ARD Format)

Description

Creates a concise, analysis-results dataset (ARD) from a tipping point analysis. Identifies the tipping point parameter where the upper CL of the hazard ratio crosses 1 and summarizes key metrics.

Usage

## S3 method for class 'tipse'
summary(object, ...)

Arguments

object

A tipse object returned by tipping_point_model_free or tipping_point_model_based.

...

Additional arguments not used.

Value

A data frame summarizing:

Examples

cox1 <- survival::coxph(Surv(AVAL, EVENT) ~ TRT01P, data = codebreak200)
result <- tipping_point_model_based(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  imputation_model = "weibull",
  J = 10,
  tipping_range = seq(0.1, 1, by = 0.05),
  cox_fit = cox1,
  verbose = TRUE,
  seed = 12345
)
summary(result)

Tipping Point Analysis (Model-Based)

Description

Performs a model-based tipping point analysis on time-to-event data by repeatedly imputing censored observations under varying assumptions. The model-based framework assumes that censored patients have a multiple of hazard fitted via a parametric survival model compared to the rest of patients in the same arm (Akinson et al, 2019).

Usage

tipping_point_model_based(
  dat,
  reason,
  impute,
  imputation_model = "weibull",
  J = 10,
  tipping_range = seq(0.05, 1, by = 0.05),
  cox_fit = NULL,
  verbose = FALSE,
  seed = 12345
)

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

Vector specifying censoring reasons to be imputed.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from variable TRT01P, the first level of TRT01P is considered as the control arm.

imputation_model

used to fit model to observed data (should be "Weibull" or "exponential")

J

numeric indicating number of imputations.

tipping_range

Numeric vector. Hazard inflation (>1) for treatment arm imputation or deflation (<1) range for control arm imputation.

cox_fit

A Cox model that will be used to calculate HRs on imputed datasets. In case of inclusion of stratification factors or covariates, conditional HR will be used.

verbose

Logical. If TRUE, prints progress and analysis details.

seed

Integer. Random seed for reproducibility.

Details

The model-based tipping point analysis provides a reproducible and intuitive framework for exploring the robustness of treatment effects in time-to-event (survival) endpoints when censoring may differ between study arms.

A parametric survival model is fitted using maximum likelihood. This function applies a hazard deflation on control arm or hazard inflation on treatment arm, and impute survival times based on the parametric model with additional sampling of the parameters from a multivariate normal distribution. This imputation procedure is iterated across a range of tipping point parameters tipping_range. For each parameter value:

  1. Multiple imputed datasets are generated (J replicates), where censored observations in the selected arm are reassigned event times according to the imputation method.

  2. A Cox proportional hazards model is fitted to each imputed dataset.

  3. Model estimates are pooled using Rubin’s rules to obtain a combined hazard ratio and confidence interval for that tipping point parameter.

The process yields a series of results showing how the treatment effect changes as increasingly conservative or optimistic assumptions are made about censored observations. The tipping point is defined as the smallest value (hazard inflation) or biggest value (hazard deflation) of the sensitivity parameter for which the upper bound of the hazard ratio confidence interval crosses 1 - i.e., where the apparent treatment benefit is lost.

Value

A tipse object containing:

original data

Input argument from 'data'.

imputation_results

A data frame of combined pooled model results across tipping points

original_HR

The original hazard ratio.

reason_to_impute

Input argument from 'reason'.

arm_to_impute

Input argument from 'impute'.

method_to_impute

Input argument from 'method'.

imputation_data

A list of imputed datasets for each tipping point value.

References

Atkinson, A., Kenward, M. G., Clayton, T., & Carpenter, J. R. (2019). Reference‐based sensitivity analysis for time‐to‐event data. Pharmaceutical statistics, 18(6), 645-658.

Examples

cox1 <- survival::coxph(Surv(AVAL, EVENT) ~ TRT01P, data = codebreak200)
result <- tipping_point_model_based(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  imputation_model = "weibull",
  J = 10,
  tipping_range = seq(0.1, 1, by = 0.05),
  cox_fit = cox1,
  verbose = TRUE,
  seed = 12345
)

Tipping Point Analysis (Model-Free)

Description

Performs a model-free tipping point analysis on time-to-event data by repeatedly imputing censored observations under varying assumptions. The model-free framework assumes that censored patients share similar survival behavior with those from whom they are sampled, without fitting any parametric survival model.

Usage

tipping_point_model_free(
  dat,
  reason,
  impute,
  J = 10,
  tipping_range = seq(5, 95, by = 5),
  cox_fit = NULL,
  verbose = FALSE,
  method = c("random sampling", "deterministic sampling"),
  seed = 12345
)

Arguments

dat

data.frame containing at least 5 columns: TRT01P (treatment arm as factor), AVAL (survival time), EVENT (event indicator), CNSRRS (censoring reason) and MAXAVAL (maximum potential survival time, duration between randomization to data cut-off)

reason

Vector specifying censoring reasons to be imputed.

impute

a string specifying the treatment arm(s) which require imputation. It must be one of the arms from variable TRT01P, the first level of TRT01P is considered as the control arm.

J

numeric indicating number of imputations.

tipping_range

Numeric vector. Percentiles to use when method = "random sampling". Number of patients to impute when method = "deterministic sampling".

cox_fit

A Cox model that will be used to calculate HRs on imputed datasets. In case of inclusion of stratification factors or covariates, conditional HR will be used.

verbose

Logical. If TRUE, prints progress and analysis details.

method

Character. Either "random sampling" or "deterministic sampling".

seed

Integer. Random seed for reproducibility.

Details

The model-free tipping point analysis provides a reproducible and intuitive framework for exploring the robustness of treatment effects in time-to-event (survival) endpoints when censoring may differ between study arms.

Two sampling modes are supported:

This function iteratively applies the random- or deterministic-sampling imputation procedure across a range of tipping point parameters tipping_range. For each parameter value:

  1. Multiple imputed datasets are generated (J replicates), where censored observations in the selected arm are replaced by sampled or reassigned event times according to the imputation method.

  2. A Cox proportional hazards model is fitted to each imputed dataset.

  3. Model estimates are pooled using Rubin’s rules to obtain a combined hazard ratio and confidence interval for that tipping point parameter.

The process yields a series of results showing how the treatment effect changes as increasingly conservative or optimistic assumptions are made about censored observations. The tipping point is defined as the smallest value of the sensitivity parameter (percentile or number of imputed patients) for which the upper bound of the hazard ratio confidence interval crosses 1 - i.e., where the apparent treatment benefit is lost.

Value

A tipse object containing:

original data

Input argument from 'data'.

imputation_results

A data frame of combined pooled model results across tipping points

original_HR

The original hazard ratio.

reason_to_impute

Input argument from 'reason'.

arm_to_impute

Input argument from 'impute'.

method_to_impute

Input argument from 'method'.

imputation_data

A list of imputed datasets for each tipping point value.

Examples

cox1 <- survival::coxph(Surv(AVAL, EVENT) ~ TRT01P, data = codebreak200)
result <- tipping_point_model_free(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  J = 10,
  tipping_range = seq(5, 95, by = 5),
  cox_fit = cox1,
  verbose = TRUE,
  method = "random sampling",
  seed = 12345
)

result2 <- tipping_point_model_free(
  dat = codebreak200,
  reason = "Early dropout",
  impute = "docetaxel",
  J = 10,
  tipping_range = seq(1, 21, by = 2),
  cox_fit = cox1,
  verbose = TRUE,
  method = "deterministic sampling",
  seed = 12345
)