First, download the latest version from Bioconductor (Rohart et al. 2017):
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("mixOmics")
The mixOmics package should directly import the
following packages:
igraph, rgl, ellipse, corpcor, RColorBrewer, plyr, parallel, dplyr, tidyr, reshape2, methods, matrixStats, rARPACK, gridExtra.
For Apple mac users: if you are unable to install
the imported package rgl, you will need to install the XQuartz software first.
library(mixOmics)
Check that there is no error when loading the package, especially for
the rgl library (see above).
The examples in this workshop use data that are already part of the
package. To upload your own data, check first that your working
directory is set, then read your data from a .txt or
.csv format, either by using File > Import
Dataset in RStudio or via one of these command lines:
# from csv file
data <- read.csv("your_data.csv", row.names = 1, header = TRUE)
# from txt file
data <- read.table("your_data.txt", header = TRUE)
For more details about the arguments used to modify those functions,
type ?read.csv or ?read.table in the R
console.
mixOmicsEach analysis should follow this workflow:
Then use your critical thinking and additional functions and visual tools to make sense of your data!
For instance, for Principal Components Analysis, we first load the data:
data(nutrimouse) #load data
X <- nutrimouse$gene #store data in object called X
Then use the following steps:
MyResult.pca <- pca(X) # 1 Run the method PCA on X
plotIndiv(MyResult.pca) # 2 Plot the samples from a PCA result
plotVar(MyResult.pca) # 3 Plot the variables from a PCA result
This is only a first quick-start. The package proposes several methods to perform variable selection and integration.
Following our example here, sparse PCA can be
applied to select the top 5 variables contributing to each of the two
components in PCA. The user specifies the number of variables to
selected on each component, for example, here 5 variables are selected
on each of the first two components (keepX=c(5,5)):
MyResult.spca <- spca(X, keepX=c(5,5)) # 1 Run the method
plotIndiv(MyResult.spca) # 2 Plot the samples
plotVar(MyResult.spca) # 3 Plot the variables
You can see know that we have considerably reduced the number of
genes in the plotVar correlation circle plot.
The Small Round Blue Cell Tumours (SRBCT) data set from (Khan et al. 2001)
includes the expression levels of 2,308 genes measured on 63 samples.
The samples are divided into four classes: 8 Burkitt Lymphoma (BL), 23
Ewing Sarcoma (EWS), 12 neuroblastoma (NB), and 20 rhabdomyosarcoma
(RMS). The data are directly available in a processed and normalised
format from the mixOmics package and contains the
following:
$gene: A data frame with 63 rows and 2,308 columns.
These are the expression levels of 2,308 genes in 63 subjects,
$class: A vector containing the class of tumour for
each individual (4 classes in total),
$gene.name: A data frame with 2,308 rows and 2
columns containing further information on the genes.
More details can be found in ?srbct.
We first load the data from the package and store the gene expression data in a \(\boldsymbol X\) object.
library(mixOmics)
data(srbct)
X <- srbct$gene
dim(X) # check dimension
PCA is a useful tool to explore the gene expression data and to
assess sample similarities between tumour types (information available
in srbct$class). Remember that PCA is an unsupervised
approach, but we can colour the samples by their tumour subtype to
assist in interpreting the PCA. Here we center (default argument) and
scale the data:
pca.srbct <- pca(X, ncomp = 10, scale = TRUE)
pca.srbct
plot(pca.srbct)
# simple version
plotIndiv(pca.srbct,
pch = 1, # pch is to show symbols
title = 'SRBCT, PCA comp 1 - 2')
# advanced version
plotIndiv(pca.srbct,
group = srbct$class, # asking to color according to class of tumour
ind.names = FALSE, # not showing the sample names
legend = TRUE,
title = 'SRBCT, PCA comp 1 - 2')
Run the code above, line by line.
How many principal components would you retain, and why.
How is a sample plot obtained in PCA? What does it represent?
Interpret the sample plot outputs (the simple version first, then the advanced version, as shown in the code). What is the major source of variation in the data?
We have already loaded the SRBCT gene expression data and stored in \(\boldsymbol X\). We need to store the factor indicating the sample class membership in \(\boldsymbol Y\)
Y <- srbct$class
length(Y)
We run a PLS-DA model that includes three components:
plsda.srbct <- plsda(X,Y, ncomp = 3)
plotIndiv(plsda.srbct, ind.names = FALSE, legend=TRUE,
comp=c(1,2),
title = 'PLS-DA on SRBCT comp 1-2')
plotLoadings(plsda.srbct,
contrib = 'max', # max = which class has maximum mean (median) expression (vs. min)
method = 'mean', # choose either mean or median
comp = 1,
ndisplay = 50)
plotVar(plsda.srbct, cutoff = 0.7) # variable plot (correlation circle plot)
biplot(plsda.srbct, cutoff = 0.7) # we set a correlation cutoff to only show the most contributing variables
boxplot(X[, 'g1932']~ Y)
Let’s run all the command lines above and discuss about the following points:
Interpretation of the sample plot. What do we notice regarding the amount of explained variance per component, compared to PCA. Is this expected? Let’s also produce the sample plot for component 1 vs. 3.
Loadings plot: what does each bar represent? Let’s change the
parameters contrib = 'max' to contrib = 'min'.
Let’s now change the parameter comp = 1 to
comp = 2. What do we observe? Now, let’s interpret the
loading plot in combination with the sample plot. Does the loading plot
make sense in light of this additional information?
Interpretation of the variable plot (correlation circle plot, refer to slides).
Interpretation of the biplot.
As a follow-up to the biplot interpretation, let’s focus on one gene and plot a boxplot with respect to the tumour subtype group (as shown in code). What is the link between how the gene is located on the biplot, and its expression levels per sample group?
We run similar command lines, but this time we use sparse PLS-DA that will enable us to select the top discriminant variables.
ⓘ Sparse means zeroes in the loading vectors
Sparse stands for loading vectors that are sparse (include zeroes) because of we use lasso regularisation in the method (Lê Cao, Boitard, and Besse 2011) so that only a subset of variables are used to calculate the components in the linear combination.
The variable selection process is done within PLS-DA. For this, we need to specify the number of variables to select per component.
splsda.srbct <- splsda(X,Y, ncomp = 3,
keepX = c(20, 10, 10) # select 20 genes on the first component, 10 on the second, and 10 on the third component
)
plotIndiv(splsda.srbct, ind.names = FALSE, legend=TRUE,
comp=c(1,2),
title = 'PLS-DA on SRBCT comp 1-2')
plotLoadings(splsda.srbct,
contrib = 'max', # max = which class has maximum mean (median) expression (vs. min)
method = 'mean', # choose either mean or median
comp = 1)
plotVar(splsda.srbct) # variable plot (correlation circle plot)
selectVar(splsda.srbct) #extracts the list of variables selected, and their loading weights
Run the code above, line by line.
What are the differences that you observe across all outputs, compared to the full PLS-DA run previously (i.e with no variable selection).
Plot a sample plot for component 1 vs 3, what do you observe? Also plot the loadings for component 3 for more insights.
Change the values of the keepX argument, do you
notice large changes?
Conclude on the advantages / disadvantages of using sparse PLS-DA for variable selection.
Sorry! we currently have a bug with the biplot that we need to fix!
DIABLO is a a method to integrate multiple data sets while explaining their relationship with a categorical outcome variable. DIABLO stands for Data Integration Analysis for Biomarker discovery using Latent variable approaches for Omics studies (Singh et al. 2019). It can also be referred to as Multiblock (s)PLS-DA.
ⓘ We need the same samples across all omics
One pre-requisite of DIABLO is that the same samples / individuals should be measured across all omics. This is because we are looking for correlation / covariance between the data sets by calculating the covariance between components (all of length \(N\), the number of samples).
Human breast cancer is a heterogeneous disease in terms of molecular alterations, cellular composition, and clinical outcome. Breast tumours can be classified into several subtypes, according to their levels of mRNA expression (Sørlie et al. 2001). Here we consider a subset of data generated by The Cancer Genome Atlas Network (Cancer Genome Atlas Network et al. 2012). For the package, data were normalised, and then drastically prefiltered for illustrative purposes.
The data were divided into a training set with a subset of 150 samples from the mRNA, miRNA and proteomics data, and a test set including 70 samples, but only with mRNA and miRNA data (the proteomics data are missing). The aim of this integrative analysis is to identify a highly correlated multi-omics signature discriminating the breast cancer subtypes Basal, Her2 and LumA.
The breast.TCGA (more details can be found in
?breast.TCGA) is a list containing training and test sets
of omics data data.train and data.test which
include:
$miRNA: A data frame with 150 (70) rows and 184 columns
in the training (test) data set for the miRNA expression levels,$mRNA: A data frame with 150 (70) rows and 520 columns
in the training (test) data set for the mRNA expression levels,$protein: A data frame with 150 rows and 142 columns in
the training data set for the protein abundance (there are no proteomics
in the test set),$subtype: A factor indicating the breast cancer
subtypes in the training (for 150 samples) and test sets (for 70
samples).This case study covers an interesting scenario where one omic data set is missing in the test set, but because the method generates a set of components per training data set, we can still assess the prediction or performance evaluation using majority or weighted prediction vote.
We will integrate the expression levels of miRNA, mRNA and the abundance of proteins while discriminating the subtypes of breast cancer, then predict the subtypes of the samples in the test set.
Each omics data matrix is stored into a list of matrices \(\boldsymbol X\). Each data frame in \(\boldsymbol X\) should be named as
we will match these names with the keepX parameter for
variable selection. A factor indicating the class membership of each
sample is stored in \(\boldsymbol
Y\).
data(breast.TCGA)
# Extract training data and name each data frame
# Store as list
X <- list(mRNA = breast.TCGA$data.train$mrna,
miRNA = breast.TCGA$data.train$mirna,
protein = breast.TCGA$data.train$protein)
# Outcome
Y <- breast.TCGA$data.train$subtype
summary(Y)
## Basal Her2 LumA
## 45 30 75
In DIABLO, we need to specify a design matrix that indicates what is the level of correlation we wish to extract between datasets. In the example below, we ask for a small amount of correlation between these data sets.
design <- matrix(0.1, ncol = length(X), nrow = length(X),
dimnames = list(names(X), names(X)))
diag(design) <- 0
design
## mRNA miRNA protein
## mRNA 0.0 0.1 0.1
## miRNA 0.1 0.0 0.1
## protein 0.1 0.1 0.0
More details on how to choose the design, either based on apriori knowledge / expectation, or on data-driven approach:
Here we decide to run a model with 2 components. The number of
variables to keep is listed below in keepX (optimal number
according to repeated cross-validation. More details available on our
website).
# number of variables to select per dataset and per component (here 2 comps)
list.keepX <- list( mRNA = c(8, 25), miRNA = c(14,5), protein = c(10, 5))
diablo.tcga <- block.splsda(X, Y, ncomp = 2,
keepX = list.keepX, design = design)
## Design matrix has changed to include Y; each block will be
## linked to Y.
# the message tells us that each data set will be linked to the outcome Y for maximal discrimination
# sample plots:
plot(diablo.tcga) # pairs of components across datasets and their correlation
plotIndiv(diablo.tcga, ind.names = FALSE, legend = TRUE)
# variable plots:
plotVar(diablo.tcga, legend = TRUE)
circosPlot(diablo.tcga, , cutoff = 0.7)
plotLoadings(diablo.tcga, contrib = 'max', # max = which class has maximum mean (median) expression (vs. min)
method = 'mean', # choose either mean or median
comp = 1)
# variable selection
selectVar(diablo.tcga)
Run the code above, line by line.
Sample plots: What do the sample plots plot and
plotIndiv tell you about the ability of DIABLO to extract
correlated information between data sets and to discriminate
sample groups? Are there datasets that are more noisy than others?
(remember that we decompose each data matrix with a set of components,
and loading vectors, while maximising the correlation between the
datasets, see slides).
Variable plots: What do the variable plots plotVar
and circosPlot tell you about the correlation between the
variables that were selected by DIABLO?
Loading plots: inspect the loading plots for components 1 and 2.
What are the discriminative properties of the variables selected on
component 1? and component 2? (the function selectVar
outputs the list of variables selected, and their loading
weights)
The predict function associated predicts the class of
samples from an external test set. In our specific case, one data set
(proteomics) is missing in the test set but the method can still be
applied for sample prediction. We need to ensure the names of the blocks
correspond exactly to those from the training set:
# Prepare test set data: here one block (proteins) is missing
data.test.tcga <- list(mRNA = breast.TCGA$data.test$mrna,
miRNA = breast.TCGA$data.test$mirna)
predict.diablo.tcga <- predict(diablo.tcga, newdata = data.test.tcga)
# The warning message will inform us that one block is missing
predict.diablo.tcga # Lists the different outputs
The following output is a confusion matrix that compares the real
subtypes with the predicted subtypes from our trained DIABLO model on
the second component for the prediction distance
centroids.dist and the prediction scheme
WeightedVote (each dataset casts a vote):
confusion.mat.tcga <- get.confusion_matrix(truth = breast.TCGA$data.test$subtype,
predicted = predict.diablo.tcga$WeightedVote$centroids.dist[,2]) # 2nd component
confusion.mat.tcga
## predicted.as.Basal predicted.as.Her2 predicted.as.LumA
## Basal 20 1 0
## Her2 0 13 1
## LumA 0 3 32
From this table, we see that one Basal and one Her2 sample are wrongly predicted as Her2 and Lum A respectively, and 3 LumA samples are wrongly predicted as Her2. The balanced prediction error rate can be obtained as:
get.BER(confusion.mat.tcga)
## [1] 0.06825397
It would be worthwhile at this stage to revisit the chosen design of DIABLO to assess the influence of the design on the prediction performance on this test set - even though this back and forth analysis is a biased criterion to choose the design!
We have not discussed how to choose the number of components, or the
number of variables keepX to select on each dataset. This
requires repeated cross-validation. More details on the tuning, and the
classification performance of the method can be found here: http://mixomics.org/mixdiablo/diablo-tcga-case-study/
Further details, analyses and vignette are avaiable on www.mixOmics.org
sessionInfo()
## R version 4.5.2 (2025-10-31)
## Platform: aarch64-apple-darwin20
## Running under: macOS Tahoe 26.5
##
## Matrix products: default
## BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Australia/Melbourne
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] mixOmics_6.32.0 ggplot2_4.0.2 lattice_0.22-7 MASS_7.3-65
## [5] knitr_1.50
##
## loaded via a namespace (and not attached):
## [1] sass_0.4.10 generics_0.1.4 tidyr_1.3.2
## [4] stringi_1.8.7 digest_0.6.37 magrittr_2.0.4
## [7] evaluate_1.0.4 grid_4.5.2 RColorBrewer_1.1-3
## [10] fastmap_1.2.0 plyr_1.8.9 jsonlite_2.0.0
## [13] Matrix_1.7-4 ggrepel_0.9.6 RSpectra_0.16-2
## [16] gridExtra_2.3 purrr_1.2.1 scales_1.4.0
## [19] codetools_0.2-20 jquerylib_0.1.4 cli_3.6.6
## [22] rlang_1.1.7 withr_3.0.2 cachem_1.1.0
## [25] yaml_2.3.10 ellipse_0.5.0 tools_4.5.2
## [28] parallel_4.5.2 reshape2_1.4.5 BiocParallel_1.42.2
## [31] dplyr_1.2.1 corpcor_1.6.10 vctrs_0.7.1
## [34] R6_2.6.1 matrixStats_1.5.0 lifecycle_1.0.5
## [37] stringr_1.6.0 pkgconfig_2.0.3 pillar_1.11.0
## [40] bslib_0.10.0 gtable_0.3.6 glue_1.8.0
## [43] rARPACK_0.11-0 Rcpp_1.1.0 xfun_0.53
## [46] tibble_3.3.0 tidyselect_1.2.1 rstudioapi_0.18.0
## [49] farver_2.1.2 htmltools_0.5.9 igraph_2.1.4
## [52] labeling_0.4.3 rmarkdown_2.29 compiler_4.5.2
## [55] S7_0.2.1