9  Module 4: Survival and Prognostic Modeling — Quick Start

Using survival::veteran, this chapter demonstrates the official PrognosiX pattern: keep survival time and event status aligned with the predictor matrix, create a prognosis object, select features, train a survival model, evaluate risk predictions, and retain the fitted workflow for later validation or deployment.

9.1 Data Requirements

Store survival time and event status as metadata aligned row by row with predictors. Confirm event coding, time units, censoring definitions, and sample alignment before modeling. Performance estimates must come from data not used to select features, tune the learner, or define the risk cutoff.

9.2 Step-by-step Workflow

The workflow is divided into short stages. Each note explains the purpose, key checks, and interpretation limits. Generated plots and tables appear directly below their code chunk.

9.2.1 Environment and Global Setup

This section performs the core analysis for this stage. Review the output immediately below the code and confirm that data ranges, assumptions, and results match the study design.

Code
# =============================================================================
# icare Package Vignette — Module 4: Survival / Prognosis Modeling (PrognosiX)
# QUICK-START EDITION
# =============================================================================
# Audience : Users who want a trained, evaluated survival model without
#            extensive benchmarking, SHAP, or DCA.
# Dataset  : Replace Section 1
#            with your own data.frame (time, status, predictors).
# For advanced features (tuning, benchmarking, DCA, Shiny app), see
# "04_module4_survival_ADVANCED.R".
# =============================================================================

# ---------------------------------------------------------------------------

9.2.2 0 Setup

This section defines seeds, output paths, and dependencies. Update these paths and key parameters first when using new data.

Code
# 0. Setup
# ---------------------------------------------------------------------------
set.seed(2025)
OUTPUT_DIR <- "./PrognosiX_QuickStart_Output"
dir.create(OUTPUT_DIR, showWarnings = FALSE, recursive = TRUE)

# Load required packages
library(icare)
library(mlr3)
library(mlr3proba)
library(survival)
library(survminer)
library(ggplot2)

# ---------------------------------------------------------------------------

9.2.3 1 Data Preparation

This section imports and organizes the data. Check dimensions and variable coding before continuing.

Code
# 1. Data Preparation
# ---------------------------------------------------------------------------
# Use the built-in veteran dataset
veteran <- survival::veteran
veteran$celltype <- as.character(veteran$celltype)

# Separate features and metadata (time, status, clinical variables)
# For this example, we treat all columns except "time" and "status" as features.
# We move time/status to info.data via CreateStatObject.
stat <- CreateStatObject(
  raw.data = veteran,
  clean.data = veteran,
  group_col = NULL,   # no explicit grouping needed
  na.action = "allow"
)
Data prepared for raw.data 
Data prepared for clean.data 
Final info.data prepared.
Stat object created.
Code
# Impute missing values (median/mode) before conversion
stat <- stat_miss_processed(stat, impute_method = "median_mode")
Using simple median and mode imputation method...
Cleaned data saved to: /tmp/RtmphXMMgE/icare_output/StatObject/Data/clean_data.csv 
Imputation completed successfully.
Updating 'Stat' object...
- 'clean.data' slot updated.
- 'process.info' slot updated.
Code
# Convert to PrognosiX – this automatically moves time/status to info.data
# and keeps only numeric features in clean.data.
prog <- Stat_to_PrognosiX(stat, "time", "status",
                          na_action = "omit",
                          min_events = 10,
                          verbose = TRUE)
PrognosiX object created successfully.
  Features: 5 | Samples: 137
  Metadata columns: 3 (including time/status)
Code
# ---------------------------------------------------------------------------

9.2.4 2 Feature Selection (Univariate + LASSO)

This section selects candidate features. Consider effect size, stability, and biological plausibility rather than a single significance measure.

Code
# 2. Feature Selection (Univariate + LASSO)
# ---------------------------------------------------------------------------
feat_sel <- surv_feature_selection_multi(
  object = prog,
  methods = c("uni_cox", "lasso"),
  p_threshold = 0.1,
  combine = "union",
  verbose = TRUE
)

[Multi-Feature Selection] Methods: uni_cox, lasso 
  - Running univariate Cox (p < 0.1 )...
  - Running glmnet family (LASSO, Ridge, Elastic Net)...

[Combined] UNION selection: 1 features out of 5
Code
selected_feats <- feat_sel$selected
cat("Selected features:", paste(selected_feats, collapse = ", "), "\n")
Selected features: karno 
Code
# Store selected features in the PrognosiX object
prog@survival.var <- list(selected = selected_feats)

# ---------------------------------------------------------------------------

9.2.5 3 Train/Validation Split

This section creates training and validation sets. Restrict feature selection and parameter estimation to the training set to prevent leakage.

Code
# 3. Train/Validation Split
# ---------------------------------------------------------------------------
task_full <- surv_extract_task(prog)$select(selected_feats)
n <- task_full$nrow
train_idx <- sample(n, floor(0.7 * n))
val_idx   <- setdiff(seq_len(n), train_idx)

train_task <- task_full$clone()$filter(train_idx)
val_task   <- task_full$clone()$filter(val_idx)

# ---------------------------------------------------------------------------

9.2.6 4 Train a Survival Random Forest

This section trains and compares candidate models. Select models using discrimination, calibration, stability, and clinical utility together.

Code
# 4. Train a Survival Random Forest
# ---------------------------------------------------------------------------
learner <- surv_get_learner("surv.ranger", train_task)
learner$train(train_task)

# ---------------------------------------------------------------------------

9.2.7 5 Evaluate on Validation Set

This section performs the core analysis for this stage. Review the output immediately below the code and confirm that data ranges, assumptions, and results match the study design.

Code
# 5. Evaluate on Validation Set
# ---------------------------------------------------------------------------
# Apparent (training) performance
train_perf <- surv_evaluate_model(learner, train_task)
cat("Training C-index:", round(train_perf$surv.cindex, 4), "\n")
Training C-index: 0.6903 
Code
# Validation performance
val_pred <- learner$predict(val_task)
val_cindex <- val_pred$score(msr("surv.cindex"))
cat("Validation C-index:", round(val_cindex, 4), "\n")
Validation C-index: 0.6702 
Code
# ---------------------------------------------------------------------------

9.2.8 6 Risk Stratification and Kaplan‑Meier Plot

This section creates exploratory and reporting figures. Plots appear below the code and are also saved as vector PDFs for publication.

Code
# 6. Risk Stratification and Kaplan‑Meier Plot
# ---------------------------------------------------------------------------
# Determine optimal cutoff from training set
pred_train <- learner$predict(train_task)
cut_df <- as.data.frame(train_task$data())
cut_df$risk <- pred_train$crank
cutoff <- median(cut_df$risk, na.rm = TRUE)

# Plot KM curves on the training set (median split)
km_median <- surv_plot_risk_km(learner, train_task, "median", risk_table = TRUE)
print(km_median)

# Apply the same cutoff to the validation set
km_val <- surv_plot_risk_km(learner, val_task,
                            cutoff_method = "custom",
                            custom_cutoffs = cutoff,
                            risk_table = TRUE)
print(km_val)

# Save KM plots
print(km_median)


print(km_val)


# ---------------------------------------------------------------------------

9.2.9 7 Predict Risk for New Patients

This section applies the trained object to new samples. Validate input format, performance, and target population in an independent cohort before deployment.

Code
# 7. Predict Risk for New Patients
# ---------------------------------------------------------------------------
# Store the best model in prog@best.model for deployment
prog@best.model <- list(
  learner_id = "surv.ranger",
  learner = learner,
  features = selected_feats,
  cutoff = cutoff,
  decision_type = "binary",
  train_cols = train_task$feature_names
)

# Example new patient data
new_patients <- data.frame(
  celltype = c("smallcell", "adeno"),
  karno    = c(60, 80),
  diagtime = c(10, 5),
  row.names = c("patient_1", "patient_2")
)

risk_scores <- predict_prognosix(prog, new_patients, impute = TRUE)
pred_groups <- predict_risk_groups(prog, new_patients,
                                   cutoff_method = "median",
                                   return_scores = TRUE)

cat("Risk scores:\n"); print(risk_scores)
Risk scores:
[1] 56.54591 59.19651
Code
cat("Risk groups:\n"); print(pred_groups)
Risk groups:
   SampleID risk_group risk_score
1 patient_1   Low Risk   56.54591
2 patient_2   Low Risk   59.19651
Code
# ---------------------------------------------------------------------------

9.2.10 8 Save the Model

This section trains and compares candidate models. Select models using discrimination, calibration, stability, and clinical utility together.

Code
# 8. Save the Model
# ---------------------------------------------------------------------------
saveRDS(prog, file = file.path(OUTPUT_DIR, "prog_obj_quickstart.rds"))

cat("\n========================================\n")

========================================
Code
cat("Quick‑start pipeline completed successfully!\n")
Quick‑start pipeline completed successfully!
Code
cat("Validation C-index:", round(val_cindex, 4), "\n")
Validation C-index: 0.6702 
Code
cat("Results saved in:", OUTPUT_DIR, "\n")
Results saved in: ./PrognosiX_QuickStart_Output 
Code
cat("========================================\n")
========================================