---
title: "06. Machine Learning with Random Survival Forests"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{06. Machine Learning with Random Survival Forests}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5
)
has_rfsrc <- requireNamespace("randomForestSRC", quietly = TRUE)
has_ggplot2 <- requireNamespace("ggplot2", quietly = TRUE)
```

## Introduction

While classical models like the Cox Proportional Hazards model are highly interpretable, they rely on strict assumptions of linear, additive effects. Random Survival Forests (RSF) overcome this by building a non-linear ensemble of decision trees. 

In this tutorial, we will demonstrate how to run a standalone RSF using `SuperSurv`'s unified wrappers, evaluate its individual performance, and finally include it in a Super Learner ensemble.

## 1. Prepare the Data

This teaching example uses 200 sampled patients, 50 trees, and three
cross-validation folds to keep it quick to run. These settings illustrate the
workflow rather than provide a full-data performance comparison.

```{r setup, message=FALSE, warning=FALSE}
library(SuperSurv)
library(survival)

data("metabric", package = "SuperSurv")
set.seed(123)
metabric <- metabric[sample(seq_len(nrow(metabric)), 200), ]

train_idx <- sample(1:nrow(metabric), 0.7 * nrow(metabric))
train <- metabric[train_idx, ]
test  <- metabric[-train_idx, ]

X_tr <- train[, grep("^x", names(metabric))]
X_te <- test[, grep("^x", names(metabric))]

new.times <- seq(50, 200, by = 25) 
```

## 2. Standalone Machine Learning

`SuperSurv` provides unified wrappers that automatically handle the training and standardization of survival probabilities. You can use these completely independent of the Super Learner ensemble.

```{r standalone-rf, eval=requireNamespace("randomForestSRC", quietly = TRUE)}
# 1. Fit the standalone wrapper
rf_standalone <- surv.rfsrc(
  time = train$duration,
  event = train$event,
  X = X_tr,
  new.times = new.times,
  ntree = 50
)

# 2. Extract the fitted model object and prediction matrix
rf_fit <- rf_standalone[["fit"]]
rf_pred_matrix <- rf_standalone[["pred"]]
```

Because our plotting functions are universally compatible, we can plot individual patient curves directly from this standalone matrix:

Plotting examples run only when both `randomForestSRC` and the optional `ggplot2` package are installed.


```{r plot-standalone, fig.align='center', eval=has_rfsrc && has_ggplot2}
# Plot the first 3 patients in our training set
plot_predict(preds = rf_pred_matrix, eval_times = new.times, patient_idx = 1:3)
```

## 3. Evaluating the Standalone Model

We can also pass this standalone model directly into our evaluation suite to test its performance on new data. 

```{r eval-standalone, fig.align='center', fig.height=4, fig.width= 9, eval=has_rfsrc && has_ggplot2}
# The function automatically detects this is a single model and plots it!
plot_benchmark(
  object = rf_fit,
  newdata = X_te,
  time = test$duration,
  event = test$event,
  eval_times = new.times
)
```



```{r}

# plot_calibration(
#   object   = rf_fit,
#   newdata  = X_te,
#   time     = test$duration,
#   event    = test$event,
#   eval_time = 150,
#   bins     = 2
# )

```




## 4. Train the Benchmark Ensemble

While the standalone RSF is powerful, we can objectively evaluate if it outperforms classical models by putting them together in a `SuperSurv` ensemble. 

```{r fit-models, results='hide', message=FALSE, warning=FALSE, eval=requireNamespace("randomForestSRC", quietly = TRUE)}
rf_library <- create_grid("surv.rfsrc", list(ntree = 50))
my_library <- c("surv.coxph", "surv.weibull", rf_library)

fit_supersurv <- SuperSurv(
  time = train$duration,
  event = train$event,
  X = X_tr,
  newdata = X_te,
  new.times = new.times,
  event.library = my_library,
  cens.library = c("surv.coxph"), 
  control = list(
    saveFitLibrary = TRUE,
    event.t.grid = seq(0, max(train$duration[train$event == 1]), length.out = 30),
    cens.t.grid = seq(0, max(train$duration[train$event == 0]), length.out = 30)
  ),
  verbose = FALSE,
  nFolds = 3
)
```

## 5. Visualizing Ensemble vs. Base Learners

When we pass the `SuperSurv` ensemble into the exact same benchmark function, it automatically unpacks the library and plots the ensemble against all its constituent models.



```{r plot-ensemble-benchmark, fig.align='center', fig.height=9, eval=has_rfsrc && has_ggplot2}
plot_benchmark(
  object = fit_supersurv,
  newdata = X_te,
  time = test$duration,
  event = test$event,
  eval_times = new.times
)
```

Including flexible learners broadens the candidate library and may improve adaptation to nonlinear or non-proportional-hazards structure. Their contribution is determined by the selected cross-validated objective and should be evaluated on held-out data.




