This is an R Markdown
Notebook. When you execute code within the notebook, the results appear
beneath the code.
Try executing this chunk by clicking the Run button within
the chunk or by placing your cursor inside it and pressing
Cmd+Shift+Enter.
Add a new chunk by clicking the Insert Chunk button on the
toolbar or by pressing Cmd+Option+I.
When you save the notebook, an HTML file containing the code and
output will be saved alongside it (click the Preview button or
press Cmd+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the
editor. Consequently, unlike Knit, Preview does not
run any R code chunks. Instead, the output of the chunk when it was last
run in the editor is displayed.
# Load required packages
library(randomForestSRC)
library(survival)
library(dplyr)
library(parallel)
# Assuming your data frame is named 'data' with:
# - time: survival time
# - status: event indicator (1=event, 0=censored)
# - other columns: predictor variables
# Example data preparation (replace with your actual data)
# data <- your_dataframe
# Make sure it has columns: time, status, and predictor variables
Random Grid Generation (200 Combinations) The code samples 200 random
hyperparameter combinations based on the ranges specified in your
Eye-Risk project document
set.seed(123)
# Generate 200 random hyperparameter combinations
n_combinations <- 200
hyper_grid <- data.frame(
ntree = sample(800:1500, n_combinations, replace = TRUE),
mtry = sample(1:10, n_combinations, replace = TRUE),
nodesize = sample(1:10, n_combinations, replace = TRUE),
nsplit = sample(5:15, n_combinations, replace = TRUE)
) %>%
distinct()
# Display default values mentioned in document (ntree=1000, mtry=6, nodesize=3, nsplit=10)
cat("Default values: ntree=1000, mtry=6, nodesize=3, nsplit=10\n")
cat("Number of unique combinations:", nrow(hyper_grid), "\n")
5-Fold Cross-Validation Stratified on Events This stratifies the
folds to ensure balanced event distribution across folds
# Create stratified folds based on event status
set.seed(456)
# Stratify on number of events
create_stratified_folds <- function(data, k = 5) {
events <- which(data$status == 1)
censored <- which(data$status == 0)
# Create folds for events and censored separately
event_folds <- split(sample(events), 1:k)
censored_folds <- split(sample(censored), 1:k)
# Combine to create balanced folds
folds <- lapply(1:k, function(i) {
c(event_folds[[i]], censored_folds[[i]])
})
return(folds)
}
folds <- create_stratified_folds(data, k = 5)
# Verify stratification
cat("Events per fold:\n")
sapply(folds, function(fold) sum(data$status[fold]))
Cross-Validation Function with C-Index The performance metric is
Harrell’s C-index, calculated as 1 - err.rate in randomForestSRC
# Function to evaluate one hyperparameter combination
cv_evaluate <- function(params, data, folds) {
c_indices <- numeric(length(folds))
for (i in seq_along(folds)) {
# Split data
test_idx <- folds[[i]]
train_data <- data[-test_idx, ]
test_data <- data[test_idx, ]
# Fit Random Survival Forest
rsf_model <- rfsrc(
Surv(time, status) ~ .,
data = train_data,
ntree = params$ntree,
mtry = params$mtry,
nodesize = params$nodesize,
nsplit = params$nsplit,
splitrule = "logrank",
importance = "none",
seed = -1
)
# Predict on test set
rsf_pred <- predict(rsf_model, newdata = test_data)
# Calculate C-index (1 - error rate)
c_indices[i] <- 1 - rsf_pred$err.rate[rsf_pred$ntree]
}
# Return mean C-index across folds
return(mean(c_indices, na.rm = TRUE))
}
Grid Search with Parallel Processing Using parallel processing as
mentioned in your document (parallelMap):
# Setup parallel processing
n_cores <- detectCores() - 1
cl <- makeCluster(n_cores)
clusterEvalQ(cl, {
library(randomForestSRC)
library(survival)
})
clusterExport(cl, c("data", "folds", "cv_evaluate"))
# Perform grid search
cat("Starting grid search with", nrow(hyper_grid), "combinations...\n")
cv_results <- parLapply(cl, 1:nrow(hyper_grid), function(i) {
c_index <- cv_evaluate(hyper_grid[i, ], data, folds)
return(data.frame(
combo_id = i,
ntree = hyper_grid$ntree[i],
mtry = hyper_grid$mtry[i],
nodesize = hyper_grid$nodesize[i],
nsplit = hyper_grid$nsplit[i],
mean_c_index = c_index
))
})
stopCluster(cl)
# Combine results
results_df <- do.call(rbind, cv_results) %>%
arrange(desc(mean_c_index))
# Display top 10 combinations
head(results_df, 10)
Final Model with Best Hyperparameters Train the final model using the
best hyperparameters on the full dataset:
# Extract best hyperparameters
best_params <- results_df[1, ]
cat("\nBest hyperparameters:\n")
print(best_params)
# Train final model on full dataset
final_model <- rfsrc(
Surv(time, status) ~ .,
data = data,
ntree = best_params$ntree,
mtry = best_params$mtry,
nodesize = best_params$nodesize,
nsplit = best_params$nsplit,
splitrule = "logrank",
importance = "permute", # Get variable importance
seed = 123
)
# Model summary
print(final_model)
# Variable importance
var_imp <- data.frame(
variable = names(final_model$importance),
importance = final_model$importance
) %>%
arrange(desc(importance))
print(var_imp)
# Final C-index on training data
final_c_index <- 1 - final_model$err.rate[final_model$ntree]
cat("\nFinal C-index:", round(final_c_index, 4), "\n")
Prediction on New Data To make predictions on new observations
# Example: predict on new data
# new_data <- your_new_dataframe
# predictions <- predict(final_model, newdata = new_data)
# Extract predicted mortality (cumulative hazard)
# predicted_risk <- predictions$predicted
# Extract survival probabilities at specific times
# survival_prob <- predictions$survival
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuIAoKVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLgoKYGBge3J9CiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcwpsaWJyYXJ5KHJhbmRvbUZvcmVzdFNSQykKbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeShkcGx5cikKbGlicmFyeShwYXJhbGxlbCkKCiMgQXNzdW1pbmcgeW91ciBkYXRhIGZyYW1lIGlzIG5hbWVkICdkYXRhJyB3aXRoOgojIC0gdGltZTogc3Vydml2YWwgdGltZQojIC0gc3RhdHVzOiBldmVudCBpbmRpY2F0b3IgKDE9ZXZlbnQsIDA9Y2Vuc29yZWQpCiMgLSBvdGhlciBjb2x1bW5zOiBwcmVkaWN0b3IgdmFyaWFibGVzCgojIEV4YW1wbGUgZGF0YSBwcmVwYXJhdGlvbiAocmVwbGFjZSB3aXRoIHlvdXIgYWN0dWFsIGRhdGEpCiMgZGF0YSA8LSB5b3VyX2RhdGFmcmFtZQojIE1ha2Ugc3VyZSBpdCBoYXMgY29sdW1uczogdGltZSwgc3RhdHVzLCBhbmQgcHJlZGljdG9yIHZhcmlhYmxlcwoKYGBgCgpSYW5kb20gR3JpZCBHZW5lcmF0aW9uICgyMDAgQ29tYmluYXRpb25zKQpUaGUgY29kZSBzYW1wbGVzIDIwMCByYW5kb20gaHlwZXJwYXJhbWV0ZXIgY29tYmluYXRpb25zIGJhc2VkIG9uIHRoZSByYW5nZXMgc3BlY2lmaWVkIGluIHlvdXIgRXllLVJpc2sgcHJvamVjdCBkb2N1bWVudAoKCmBgYHtyfQpzZXQuc2VlZCgxMjMpCgojIEdlbmVyYXRlIDIwMCByYW5kb20gaHlwZXJwYXJhbWV0ZXIgY29tYmluYXRpb25zCm5fY29tYmluYXRpb25zIDwtIDIwMAoKaHlwZXJfZ3JpZCA8LSBkYXRhLmZyYW1lKAogIG50cmVlICAgID0gc2FtcGxlKDgwMDoxNTAwLCBuX2NvbWJpbmF0aW9ucywgcmVwbGFjZSA9IFRSVUUpLAogIG10cnkgICAgID0gc2FtcGxlKDE6MTAsIG5fY29tYmluYXRpb25zLCByZXBsYWNlID0gVFJVRSksCiAgbm9kZXNpemUgPSBzYW1wbGUoMToxMCwgbl9jb21iaW5hdGlvbnMsIHJlcGxhY2UgPSBUUlVFKSwKICBuc3BsaXQgICA9IHNhbXBsZSg1OjE1LCBuX2NvbWJpbmF0aW9ucywgcmVwbGFjZSA9IFRSVUUpCikgJT4lCiAgZGlzdGluY3QoKQoKIyBEaXNwbGF5IGRlZmF1bHQgdmFsdWVzIG1lbnRpb25lZCBpbiBkb2N1bWVudCAobnRyZWU9MTAwMCwgbXRyeT02LCBub2Rlc2l6ZT0zLCBuc3BsaXQ9MTApCmNhdCgiRGVmYXVsdCB2YWx1ZXM6IG50cmVlPTEwMDAsIG10cnk9Niwgbm9kZXNpemU9MywgbnNwbGl0PTEwXG4iKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgY29tYmluYXRpb25zOiIsIG5yb3coaHlwZXJfZ3JpZCksICJcbiIpCgpgYGAKCjUtRm9sZCBDcm9zcy1WYWxpZGF0aW9uIFN0cmF0aWZpZWQgb24gRXZlbnRzClRoaXMgc3RyYXRpZmllcyB0aGUgZm9sZHMgdG8gZW5zdXJlIGJhbGFuY2VkIGV2ZW50IGRpc3RyaWJ1dGlvbiBhY3Jvc3MgZm9sZHMKCmBgYHtyfQojIENyZWF0ZSBzdHJhdGlmaWVkIGZvbGRzIGJhc2VkIG9uIGV2ZW50IHN0YXR1cwpzZXQuc2VlZCg0NTYpCgojIFN0cmF0aWZ5IG9uIG51bWJlciBvZiBldmVudHMKY3JlYXRlX3N0cmF0aWZpZWRfZm9sZHMgPC0gZnVuY3Rpb24oZGF0YSwgayA9IDUpIHsKICBldmVudHMgPC0gd2hpY2goZGF0YSRzdGF0dXMgPT0gMSkKICBjZW5zb3JlZCA8LSB3aGljaChkYXRhJHN0YXR1cyA9PSAwKQogIAogICMgQ3JlYXRlIGZvbGRzIGZvciBldmVudHMgYW5kIGNlbnNvcmVkIHNlcGFyYXRlbHkKICBldmVudF9mb2xkcyA8LSBzcGxpdChzYW1wbGUoZXZlbnRzKSwgMTprKQogIGNlbnNvcmVkX2ZvbGRzIDwtIHNwbGl0KHNhbXBsZShjZW5zb3JlZCksIDE6aykKICAKICAjIENvbWJpbmUgdG8gY3JlYXRlIGJhbGFuY2VkIGZvbGRzCiAgZm9sZHMgPC0gbGFwcGx5KDE6aywgZnVuY3Rpb24oaSkgewogICAgYyhldmVudF9mb2xkc1tbaV1dLCBjZW5zb3JlZF9mb2xkc1tbaV1dKQogIH0pCiAgCiAgcmV0dXJuKGZvbGRzKQp9Cgpmb2xkcyA8LSBjcmVhdGVfc3RyYXRpZmllZF9mb2xkcyhkYXRhLCBrID0gNSkKCiMgVmVyaWZ5IHN0cmF0aWZpY2F0aW9uCmNhdCgiRXZlbnRzIHBlciBmb2xkOlxuIikKc2FwcGx5KGZvbGRzLCBmdW5jdGlvbihmb2xkKSBzdW0oZGF0YSRzdGF0dXNbZm9sZF0pKQoKYGBgCgpDcm9zcy1WYWxpZGF0aW9uIEZ1bmN0aW9uIHdpdGggQy1JbmRleApUaGUgcGVyZm9ybWFuY2UgbWV0cmljIGlzIEhhcnJlbGwncyBDLWluZGV4LCBjYWxjdWxhdGVkIGFzIDEgLSBlcnIucmF0ZSBpbiByYW5kb21Gb3Jlc3RTUkMKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIGV2YWx1YXRlIG9uZSBoeXBlcnBhcmFtZXRlciBjb21iaW5hdGlvbgpjdl9ldmFsdWF0ZSA8LSBmdW5jdGlvbihwYXJhbXMsIGRhdGEsIGZvbGRzKSB7CiAgCiAgY19pbmRpY2VzIDwtIG51bWVyaWMobGVuZ3RoKGZvbGRzKSkKICAKICBmb3IgKGkgaW4gc2VxX2Fsb25nKGZvbGRzKSkgewogICAgCiAgICAjIFNwbGl0IGRhdGEKICAgIHRlc3RfaWR4IDwtIGZvbGRzW1tpXV0KICAgIHRyYWluX2RhdGEgPC0gZGF0YVstdGVzdF9pZHgsIF0KICAgIHRlc3RfZGF0YSA8LSBkYXRhW3Rlc3RfaWR4LCBdCiAgICAKICAgICMgRml0IFJhbmRvbSBTdXJ2aXZhbCBGb3Jlc3QKICAgIHJzZl9tb2RlbCA8LSByZnNyYygKICAgICAgU3Vydih0aW1lLCBzdGF0dXMpIH4gLiwKICAgICAgZGF0YSA9IHRyYWluX2RhdGEsCiAgICAgIG50cmVlID0gcGFyYW1zJG50cmVlLAogICAgICBtdHJ5ID0gcGFyYW1zJG10cnksCiAgICAgIG5vZGVzaXplID0gcGFyYW1zJG5vZGVzaXplLAogICAgICBuc3BsaXQgPSBwYXJhbXMkbnNwbGl0LAogICAgICBzcGxpdHJ1bGUgPSAibG9ncmFuayIsCiAgICAgIGltcG9ydGFuY2UgPSAibm9uZSIsCiAgICAgIHNlZWQgPSAtMQogICAgKQogICAgCiAgICAjIFByZWRpY3Qgb24gdGVzdCBzZXQKICAgIHJzZl9wcmVkIDwtIHByZWRpY3QocnNmX21vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhKQogICAgCiAgICAjIENhbGN1bGF0ZSBDLWluZGV4ICgxIC0gZXJyb3IgcmF0ZSkKICAgIGNfaW5kaWNlc1tpXSA8LSAxIC0gcnNmX3ByZWQkZXJyLnJhdGVbcnNmX3ByZWQkbnRyZWVdCiAgfQogIAogICMgUmV0dXJuIG1lYW4gQy1pbmRleCBhY3Jvc3MgZm9sZHMKICByZXR1cm4obWVhbihjX2luZGljZXMsIG5hLnJtID0gVFJVRSkpCn0KCmBgYAoKR3JpZCBTZWFyY2ggd2l0aCBQYXJhbGxlbCBQcm9jZXNzaW5nClVzaW5nIHBhcmFsbGVsIHByb2Nlc3NpbmcgYXMgbWVudGlvbmVkIGluIHlvdXIgZG9jdW1lbnQgKHBhcmFsbGVsTWFwKToKCmBgYHtyfQojIFNldHVwIHBhcmFsbGVsIHByb2Nlc3NpbmcKbl9jb3JlcyA8LSBkZXRlY3RDb3JlcygpIC0gMQpjbCA8LSBtYWtlQ2x1c3RlcihuX2NvcmVzKQpjbHVzdGVyRXZhbFEoY2wsIHsKICBsaWJyYXJ5KHJhbmRvbUZvcmVzdFNSQykKICBsaWJyYXJ5KHN1cnZpdmFsKQp9KQpjbHVzdGVyRXhwb3J0KGNsLCBjKCJkYXRhIiwgImZvbGRzIiwgImN2X2V2YWx1YXRlIikpCgojIFBlcmZvcm0gZ3JpZCBzZWFyY2gKY2F0KCJTdGFydGluZyBncmlkIHNlYXJjaCB3aXRoIiwgbnJvdyhoeXBlcl9ncmlkKSwgImNvbWJpbmF0aW9ucy4uLlxuIikKCmN2X3Jlc3VsdHMgPC0gcGFyTGFwcGx5KGNsLCAxOm5yb3coaHlwZXJfZ3JpZCksIGZ1bmN0aW9uKGkpIHsKICBjX2luZGV4IDwtIGN2X2V2YWx1YXRlKGh5cGVyX2dyaWRbaSwgXSwgZGF0YSwgZm9sZHMpCiAgcmV0dXJuKGRhdGEuZnJhbWUoCiAgICBjb21ib19pZCA9IGksCiAgICBudHJlZSA9IGh5cGVyX2dyaWQkbnRyZWVbaV0sCiAgICBtdHJ5ID0gaHlwZXJfZ3JpZCRtdHJ5W2ldLAogICAgbm9kZXNpemUgPSBoeXBlcl9ncmlkJG5vZGVzaXplW2ldLAogICAgbnNwbGl0ID0gaHlwZXJfZ3JpZCRuc3BsaXRbaV0sCiAgICBtZWFuX2NfaW5kZXggPSBjX2luZGV4CiAgKSkKfSkKCnN0b3BDbHVzdGVyKGNsKQoKIyBDb21iaW5lIHJlc3VsdHMKcmVzdWx0c19kZiA8LSBkby5jYWxsKHJiaW5kLCBjdl9yZXN1bHRzKSAlPiUKICBhcnJhbmdlKGRlc2MobWVhbl9jX2luZGV4KSkKCiMgRGlzcGxheSB0b3AgMTAgY29tYmluYXRpb25zCmhlYWQocmVzdWx0c19kZiwgMTApCgpgYGAKCkZpbmFsIE1vZGVsIHdpdGggQmVzdCBIeXBlcnBhcmFtZXRlcnMKVHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IGh5cGVycGFyYW1ldGVycyBvbiB0aGUgZnVsbCBkYXRhc2V0OgoKYGBge3J9CiMgRXh0cmFjdCBiZXN0IGh5cGVycGFyYW1ldGVycwpiZXN0X3BhcmFtcyA8LSByZXN1bHRzX2RmWzEsIF0KCmNhdCgiXG5CZXN0IGh5cGVycGFyYW1ldGVyczpcbiIpCnByaW50KGJlc3RfcGFyYW1zKQoKIyBUcmFpbiBmaW5hbCBtb2RlbCBvbiBmdWxsIGRhdGFzZXQKZmluYWxfbW9kZWwgPC0gcmZzcmMoCiAgU3Vydih0aW1lLCBzdGF0dXMpIH4gLiwKICBkYXRhID0gZGF0YSwKICBudHJlZSA9IGJlc3RfcGFyYW1zJG50cmVlLAogIG10cnkgPSBiZXN0X3BhcmFtcyRtdHJ5LAogIG5vZGVzaXplID0gYmVzdF9wYXJhbXMkbm9kZXNpemUsCiAgbnNwbGl0ID0gYmVzdF9wYXJhbXMkbnNwbGl0LAogIHNwbGl0cnVsZSA9ICJsb2dyYW5rIiwKICBpbXBvcnRhbmNlID0gInBlcm11dGUiLCAgIyBHZXQgdmFyaWFibGUgaW1wb3J0YW5jZQogIHNlZWQgPSAxMjMKKQoKIyBNb2RlbCBzdW1tYXJ5CnByaW50KGZpbmFsX21vZGVsKQoKIyBWYXJpYWJsZSBpbXBvcnRhbmNlCnZhcl9pbXAgPC0gZGF0YS5mcmFtZSgKICB2YXJpYWJsZSA9IG5hbWVzKGZpbmFsX21vZGVsJGltcG9ydGFuY2UpLAogIGltcG9ydGFuY2UgPSBmaW5hbF9tb2RlbCRpbXBvcnRhbmNlCikgJT4lCiAgYXJyYW5nZShkZXNjKGltcG9ydGFuY2UpKQoKcHJpbnQodmFyX2ltcCkKCiMgRmluYWwgQy1pbmRleCBvbiB0cmFpbmluZyBkYXRhCmZpbmFsX2NfaW5kZXggPC0gMSAtIGZpbmFsX21vZGVsJGVyci5yYXRlW2ZpbmFsX21vZGVsJG50cmVlXQpjYXQoIlxuRmluYWwgQy1pbmRleDoiLCByb3VuZChmaW5hbF9jX2luZGV4LCA0KSwgIlxuIikKCmBgYAoKUHJlZGljdGlvbiBvbiBOZXcgRGF0YQpUbyBtYWtlIHByZWRpY3Rpb25zIG9uIG5ldyBvYnNlcnZhdGlvbnMKCmBgYHtyfQojIEV4YW1wbGU6IHByZWRpY3Qgb24gbmV3IGRhdGEKIyBuZXdfZGF0YSA8LSB5b3VyX25ld19kYXRhZnJhbWUKCiMgcHJlZGljdGlvbnMgPC0gcHJlZGljdChmaW5hbF9tb2RlbCwgbmV3ZGF0YSA9IG5ld19kYXRhKQoKIyBFeHRyYWN0IHByZWRpY3RlZCBtb3J0YWxpdHkgKGN1bXVsYXRpdmUgaGF6YXJkKQojIHByZWRpY3RlZF9yaXNrIDwtIHByZWRpY3Rpb25zJHByZWRpY3RlZAoKIyBFeHRyYWN0IHN1cnZpdmFsIHByb2JhYmlsaXRpZXMgYXQgc3BlY2lmaWMgdGltZXMKIyBzdXJ2aXZhbF9wcm9iIDwtIHByZWRpY3Rpb25zJHN1cnZpdmFsCgpgYGAKCgo=