Skip to contents

This example shows a regression workflow for predicting gestational age from multimodal pregnancy data using caretMultimodal. The workflow covers downloading and preprocessing seven paired omics assays, training modality-specific elastic net models and a stacked ensemble under leave-one-subject-out cross-validation, and evaluating performance with Spearman correlation, contribution plots, and ablation analysis.

Load and preprocess the data

# Download the data
url <- "https://nalab.stanford.edu/wp-content/uploads/termpregnancymultiomics.zip"
buf <- curl::curl_fetch_memory(url)$content
load(archive::archive_read(rawConnection(buf), file = "termpregnancymultiomics/Data.Rda"))

# Name the input data
names(InputData) <- c("Cellfree RNA", "Plasma Luminex", "Serum Luminex", "Microbiome", "Immune System", "Metabolomics", "Plasma Somalogic")

# Correct nonstandard feature names
InputData <- lapply(InputData, function(df) {
  df <- as.data.frame(df)
  names(df) <- make.names(iconv(names(df), from = "latin1", to = "UTF-8", sub = ""), unique = TRUE)
  df
})

names(InputData$Metabolomics) <- paste("Metabolomic Feature", seq_along(names(InputData$Metabolomics)))

# Remove postpartum samples
postpartum_indices <- which(featureweeks < 0)
InputData <- lapply(InputData, function(x) x[-postpartum_indices, ])
featurepatients <- featurepatients[-postpartum_indices]
featureweeks <- featureweeks[-postpartum_indices]

Train models with caretMultimodal

set.seed(42L)

# Define hyperparameter tuning grid
alphas <- seq(0, 1, 0.1)
lambdas <- seq(0, 12, by = 0.5)
tuneGrid <- expand.grid(alpha = alphas, lambda = lambdas)

# Set up leave-one-subject out cross validation
loso_folds <- lapply(unique(featurepatients), function(p) {
  which(featurepatients != p)  # indices of all samples NOT in this patient
})
 
trControl <- caret::trainControl(
  method = "cv",
  index = loso_folds,
  savePredictions = "final",
  summaryFunction = caret::defaultSummary
)
 
# Train the base models
pregnancy_models <- caretMultimodal::caret_list(
  data_list = InputData,
  target = featureweeks,
  method = "glmnet",
  tuneGrid = tuneGrid,
  trControl = trControl,
  trim = FALSE
)
 
# Train the ensemble model
pregnancy_stack <- caretMultimodal::caret_stack(
  pregnancy_models,
  method = "glmnet",
  tuneGrid = tuneGrid,
  trControl = trControl
)

Evaluate and Interpret

Model performance

summary(pregnancy_stack)
##               model method alpha lambda     RMSE  Rsquared      MAE   RMSESD
##              <char> <char> <num>  <num>    <num>     <num>    <num>    <num>
## 1:     Cellfree RNA glmnet   0.5    4.5 7.514323 0.6728265 6.094657 2.316190
## 2:   Plasma Luminex glmnet   0.7    1.0 6.264192 0.7268943 5.029513 1.921326
## 3:    Serum Luminex glmnet   1.0    2.0 7.988148 0.6981922 6.677682 1.668235
## 4:       Microbiome glmnet   0.2   11.5 8.130642 0.4181656 6.648128 1.991066
## 5:    Immune System glmnet   0.1    1.5 4.559213 0.9013736 3.970832 1.973698
## 6:     Metabolomics glmnet   0.1    0.5 2.965703 0.9640824 2.513691 1.675753
## 7: Plasma Somalogic glmnet   1.0    1.0 3.444065 0.8865887 2.659584 2.118518
## 8:         ensemble glmnet   0.0    0.5 2.758618 0.9772290 2.305836 1.388231
##    RsquaredSD    MAESD
##         <num>    <num>
## 1: 0.28378405 1.820931
## 2: 0.30932681 1.738192
## 3: 0.27331008 1.482994
## 4: 0.31921014 1.713364
## 5: 0.23586666 1.745224
## 6: 0.04075627 1.390273
## 7: 0.24339738 1.232796
## 8: 0.02707345 1.176519

Note: Performance metrics returned by summary.caret_stack() are the resampling summaries generated by caret and may differ from metrics calculated from pooled out-of-fold predictions. See compute_metric.caret_stack() for calculating metrics directly from pooled out-of-fold predictions.

metric_fun <- function(preds, target) {
  sqrt(mean((preds - target)^2))
}

caretMultimodal::compute_metric(
  pregnancy_stack,
  metric_fun = metric_fun,
  metric_name = "RMSE"
)
##               Model     RMSE
##              <char>    <num>
## 1:         ensemble 3.065251
## 2:     Metabolomics 3.397811
## 3: Plasma Somalogic 4.001478
## 4:    Immune System 4.856087
## 5:   Plasma Luminex 6.516770
## 6:     Cellfree RNA 7.891113
## 7:    Serum Luminex 8.210961
## 8:       Microbiome 8.512699
caretMultimodal::plot_metric(
  pregnancy_stack,
  metric_fun = metric_fun,
  metric_name = "RMSE"
)

Base model and feature contributions

caretMultimodal::compute_model_contributions(pregnancy_stack)
##               Model Relative Contribution
##              <char>                 <num>
## 1:     Metabolomics             25.285246
## 2: Plasma Somalogic             24.874757
## 3:    Serum Luminex             15.742591
## 4:       Microbiome             15.616400
## 5:    Immune System              9.385203
## 6:   Plasma Luminex              6.109482
## 7:     Cellfree RNA              2.986322
caretMultimodal::plot_model_contributions(pregnancy_stack)

caretMultimodal::compute_feature_contributions(pregnancy_stack)
##                Model                  Feature Relative Contribution
##               <char>                   <char>                 <num>
##  1: Plasma Somalogic                 SLC25A18            18.0723947
##  2:    Serum Luminex            serum.EOTAXIN            14.4946102
##  3:       Microbiome      Saliva_Anaeroglobus             9.0073678
##  4:       Microbiome      Stool_Actinomyces.2             3.3268629
##  5:   Plasma Luminex           plasma.EOTAXIN             2.7806292
##  6:    Immune System                   intMCs             2.5522895
##  7: Plasma Somalogic                      F11             1.9678954
##  8:       Microbiome             Stool_NA.168             1.5943148
##  9: Plasma Somalogic                     KLK7             1.2901194
## 10:    Serum Luminex               serum.MCP1             1.2479806
## 11:     Metabolomics Metabolomic Feature 1515             1.1777251
## 12:     Cellfree RNA                     E2F6             1.0780740
## 13:    Immune System    Tbet.CD4.Tcells_naive             1.0451590
## 14:     Metabolomics Metabolomic Feature 2545             0.9796715
## 15:   Plasma Luminex               plasma.MIG             0.9552108
## 16:     Metabolomics  Metabolomic Feature 286             0.9410198
## 17:   Plasma Luminex              plasma.TGFA             0.9403460
## 18: Plasma Somalogic                     CHKB             0.9087291
## 19:     Metabolomics Metabolomic Feature 2626             0.8667525
## 20: Plasma Somalogic                    CNTN4             0.8663147
##                Model                  Feature Relative Contribution
##               <char>                   <char>                 <num>
caretMultimodal::plot_feature_contributions(pregnancy_stack)

Ablation analysis

# Forward ablation
caretMultimodal::compute_ablation(
  pregnancy_stack, 
  metric_fun = metric_fun, 
  metric_name = "RMSE"
)
##                 Row Ablation_1 Ablation_2 Ablation_3 Ablation_4 Ablation_5
##              <char>      <num>      <num>      <num>      <num>      <num>
## 1:     Cellfree RNA   2.986322         NA         NA         NA         NA
## 2:   Plasma Luminex   6.109482   6.001124         NA         NA         NA
## 3:    Serum Luminex  15.742591  16.698123  17.887264  22.000382  28.973279
## 4:       Microbiome  15.616400  16.130522  20.676324  16.183171         NA
## 5:    Immune System   9.385203   9.842819  11.468707         NA         NA
## 6:     Metabolomics  25.285246  26.126223  25.407053  32.831343  37.965888
## 7: Plasma Somalogic  24.874757  25.201190  24.560652  28.985104  33.060834
## 8:             RMSE   3.065251   2.996606   2.974001   3.017194   2.973132
##    Ablation_6 Ablation_7
##         <num>      <num>
## 1:         NA         NA
## 2:         NA         NA
## 3:         NA         NA
## 4:         NA         NA
## 5:         NA         NA
## 6:  52.323708 100.000000
## 7:  47.676292         NA
## 8:   3.184959   3.397811
caretMultimodal::plot_ablation(
  pregnancy_stack, 
  metric_fun = metric_fun, 
  metric_name = "RMSE"
)

# Reverse ablation
caretMultimodal::compute_ablation(
  pregnancy_stack, 
  metric_fun = metric_fun, 
  metric_name = "RMSE", 
  reverse = TRUE
)
##                 Row Ablation_1 Ablation_2 Ablation_3 Ablation_4 Ablation_5
##              <char>      <num>      <num>      <num>      <num>      <num>
## 1:     Cellfree RNA   2.986322   8.181560   7.695312  33.165760  40.263147
## 2:   Plasma Luminex   6.109482   7.077252  10.012191  66.834240         NA
## 3:    Serum Luminex  15.742591   8.352738   0.000000   0.000000  35.039780
## 4:       Microbiome  15.616400  24.712844  14.023535   0.000000  24.697073
## 5:    Immune System   9.385203  15.843630  68.268962         NA         NA
## 6:     Metabolomics  25.285246         NA         NA         NA         NA
## 7: Plasma Somalogic  24.874757  35.831976         NA         NA         NA
## 8:             RMSE   3.065251   3.658108   5.053631   6.563378   8.024488
##    Ablation_6 Ablation_7
##         <num>      <num>
## 1:         NA         NA
## 2:         NA         NA
## 3:  60.882182         NA
## 4:  39.117818 100.000000
## 5:         NA         NA
## 6:         NA         NA
## 7:         NA         NA
## 8:   8.448777   8.512699
caretMultimodal::plot_ablation(
  pregnancy_stack, 
  metric_fun = metric_fun, 
  metric_name = "RMSE", 
  reverse = TRUE
)