Libraries used to create and generate this report:
R version 4.3.3 (2024-02-29)2.211.421.0.40.341.3.4Libraries used to analyse data:
2.3.11.0.121.4.2Libraries used to load data:
1.16.11.14.86.24.0Cytoscape is used for visualization. Figures were generated using the Cytoscape v3.9.1 and several Cytoscape apps:
1.1.31.1.6In this tutorial, we decide to remove samples with at least one missing data. To remove samples with missing data, we propose the following NARemoving() function. Input parameters are:
data: the data typemargin: a vector giving the subscripts which the function will be applied over (e.g. 1 indicates rows and 2 indicates columns)threshold: threshold above which samples/features are deletedNARemoving <- function(data, margin, threshold){
#' NA removing
#'
#' Calculate percentage of na
#' Remove na from rows (margin = 1) or column (margin = 2)
#'
#' @param data data.frame.
#' @param margin int. 1 = row and 2 = column
#' @param threshold int. Number of missing data accepted
#'
#' @return Return data.frame with a specific number of na by row/column
data_na <- apply(data, MARGIN = margin, FUN = function(v){sum(is.na(v)) / length(v) * 100})
# print(table(data_na))
toRemove <- split(names(data_na[data_na > threshold]), " ")[[1]]
if(margin == 1){
data_withoutNa <- data[!(row.names(data) %in% toRemove),]
print(paste0("Remove ", as.character(length(toRemove)), " samples."))
}
if(margin == 2){
data_withoutNa <- data[,!(colnames(data) %in% toRemove)]
print(paste0("Remove ", as.character(length(toRemove)), " features"))
}
return(data_withoutNa)
}For help, we created two functions to calculate these ACC values and choose the best threshold based on the topology:
CCCalculation(): this function calculates the Clustering Coefficient (CC) for each nodeACCCalculation(); this function averages the CC for a network, in order to obtain the ACC## CC calculation function
CCCalculation <- function(node, graph){
#' Clustering Coefficient (CC) calculation
#'
#' Calculate the Clustering Coefficient (CC) for each node in a network
#'
#' @param node str.
#' @param graph igraph. Network object (e.g. the fused network object)
#'
#' @return Return the corresponding CC value
degNode <- degree(graph = graph, v = node, loops = FALSE)
if(degNode > 1){
neighborNames <- neighbors(graph = graph, v = node)
graph_s <- subgraph(graph = graph, vids = neighborNames)
neighborNb <- sum(degree(graph_s, loops = FALSE))
CC <- neighborNb / (degNode * (degNode-1))
}else{CC <- 0}
return(CC)
}
## ACC calculation function
ACCCalculation <- function(graph){
#' Average Clustering Coefficient (ACC) calculation
#'
#' It average the Clustering Coefficient (CC) of a network
#'
#' @param graph igraph. Network object (e.g. the fused network object)
#'
#' @return Return the corresponding ACC value
nodes <- V(graph)
ACC <- do.call(sum, lapply(nodes, CCCalculation, graph)) / length(nodes)
return(ACC)
}Choose the dataset on which you want to apply SNF!!
Different datasets are available. Note that each dataset has its specificity and some analysis steps should be adapted.
Figure 3.1: Four datasets are available: Metagenomic dataset from Tara Ocean (image from Sunagawa et al., 2015), Breast cancer dataset from TCGA (image from TCGA website), CLL dataset (Dietrich et al., 2018) and tomato plant dataset (figure from google image).
To retrieve data: files are available in /shared/projects/tp_etbii_2024_165650/Networks/TaraOcean_mibiomics directory path in the IFB server.
dataset:
TARAoceans_proNOGS.cvsTARAoceans_proPhylo.csvmetadata:
TARAoceans_metadata.csvSamples come from eight oceans around the world (SPO: South Pacific Ocean, NAO: North Atlantic Ocean, IO: Indian Ocean, RS: Red Sea, MS: Mediterranean Sea, NPO: North Pacific Ocean, SO: Southern Ocean, SAO: South Atlantic Ocean).
Samples can come from different layers with different temperatures:
In a previous analysis (Sunagawa et al., 2015), they identified a stratification mostly driven by the temperature rather than geography or other environmental factors.
We have two types of data:
Does an integrative analysis of these two data types retrieve the stratification driver by the layers? Does it also find a geographical clustering?
Data are coming from: MiBiOmics gitlab.
To retrieve data:
dataset: using data("breast.TCGA") from the mixOmics R package
breast.TCGA$data.train$mirnabreast.TCGA$data.train$mrnabreast.TCGA$data.train$proteinmetadata: using data("breast.TCGA") from the mixOmics R package
breast.TCGA$data.train$subtypeHuman breast cancer is a heterogeneous disease. Breast tumors can be classified into several subtypes (PAM50 classification), according to the mRNA expression level (Sorlie et al., 2001). In this dataset, we have three subtypes:
We have three types of data:
Does an integrative analysis of these three data types retrieve the classification of the breast cancer? Or find another classification?
Data are coming from the mixOmics R package. The full data can be downloaded here.
To retrieve data:
dataset: using data("CLL_data") from the MOFAdata R package
CLL_data_t$DrugsCLL_data_t$MethylationCLL_data_t$mRNACLL_data_t$Mutationsmetadata: file is available in /shared/projects/tp_etbii_2024_165650/Networks/CLL directory path in the IFB server.
sample_metadata.txtThe Chronic Lymphocytic Leukaemia (CLL) is type of blood and bone marrow cancer. The full data are explained in Dietrich et al., 2018 and available here.
We have four types of data:
To retrieve data: files are available in /shared/projects/tp_etbii_2024_165650/Networks/Tomato directory path in the IFB server.
dataset:
mrna.tsvprots.tsvmetadata:
samples_metadata.tsvIn order to study the protein turnover in developing tomato fruit (Solanum lycopersicum) in Belouah et al., two omics data types were collected:
Each data type was collected in nine different developmental stages: GR1, GR2, GR3, GR4, GR5, GR6, GR7, GR8 and GR9. For each developmental stages, we have three replicates.
Does an integrative analysis of these data types retrieve the different developmental stages?
Data are coming from Belouah et al., 2019.
The metagenomic dataset from the Tara Ocean project contains 2 data types:
Data files are available in /shared/projects/tp_etbii_2024_165650/Networks/TaraOcean_mibiomics directory path in the IFB server.
First, we load the data type from TARAoceans_proNOGS.cvs and TARAoceans_proPhylo.csv files.
The data file contains header (head = TRUE) and the first column contains row names (row.names = 1). Below, the first rows and columns are displayed.
tara_nog <- read.table(file = "../00_Data/TaraOcean_mibiomics/TARAoceans_proNOGS.csv", sep = ",", head = TRUE, row.names = 1)
tara_nog[c(1:5), c(1:5)]## NOG317682 NOG135470 NOG85325 NOG285859 NOG147792
## TARA_109_SRF 0 2.390962e-05 0 4.663604e-08 1.800215e-07
## TARA_149_MES 0 4.339824e-06 0 5.182915e-07 4.190123e-06
## TARA_110_MES 0 1.348252e-05 0 6.000043e-07 2.218342e-07
## TARA_102_MES 0 6.380711e-06 0 3.816016e-07 0.000000e+00
## TARA_142_SRF 0 9.484144e-06 0 6.437103e-08 1.132431e-06
Dimensions of the data.
## [1] 139 638
The nog data contain 139 samples (rows) and 638 features (columns). Data are in the right shape: samples in rows and features in columns.
The data file contains header (head = TRUE) and the first column contains row names (row.names = 1). Below, the first rows and columns are displayed.
tara_phy <- read.table(file = "../00_Data/TaraOcean_mibiomics/TARAoceans_proPhylo.csv", sep = ",", head = TRUE, row.names = 1)
tara_phy[c(1:5), c(1:3)]## EU638706.1.1353 JN537192.1.1500 CAFJ01000195.23.1515
## TARA_109_SRF 0 0 0
## TARA_149_MES 0 0 0
## TARA_110_MES 0 4 0
## TARA_102_MES 0 1 0
## TARA_142_SRF 0 3 0
Number of rows in the data.
## [1] 139
Number of columns in the data.
## [1] 356
The phy data contain 139 samples (rows) and 356 features (columns). Data are in the right shape: samples in rows and features in columns.
The TARAoceans_metadata.csv file contains metadata. It contains header (head = TRUE) and row names (row.names = 1).
tara_metadata <- read.table(file = "../00_Data/TaraOcean_mibiomics/TARAoceans_metadata.csv", sep = ",", head = TRUE, row.names = 1)
head(tara_metadata)## ocean depth
## TARA_109_SRF SPO SRF
## TARA_149_MES NAO MES
## TARA_110_MES SPO MES
## TARA_102_MES SPO MES
## TARA_142_SRF NAO SRF
## TARA_109_DCM SPO DCM
The metadata contains two types of information: ocean and depth.
## [1] "ocean" "depth"
Samples come from 8 oceans.
## [1] "SPO" "NAO" "IO" "SO" "SAO" "NPO" "RS" "MS"
Samples were collected in 4 different depths.
## [1] "SRF" "MES" "DCM" "MIX"
The data don’t contain missing values. We can go to the following steps.
##
## FALSE
## 88682
##
## FALSE
## 49484
We assume that data have been already prepared and normalized.
Nog data are scaled: each column will scaled to have the mean equals to zero and the standard deviation equals to one.
The following figures are the distribution of the data, before (left) and after (right) scaling. We expected a normal distribution of the data after scaling.
hist(as.matrix(tara_nog), nclass = 100, main = "Orthologous genes - Prepared data", xlab = "values")
hist(tara_nog_scaled, nclass = 100, main = "Orthologous genes - Scaled data", xlab = "values")Before scaling, data values are almost all closed to zero. After scaling, data values seem to follow something close to a normal distribution. Data values are centered to zero.
Phy data are scaled: each column will scaled to have the mean equals to zero and the standard deviation equals to one.
The following figures are the distribution of the data, before (left) and after (right) scaling. We expected a normal distribution of the data after scaling.
hist(as.matrix(tara_phy), nclass = 100, main = "Phylogenetic profile - Prepared data", xlab = "values")
hist(tara_phy_scaled, nclass = 100, main = "Phylogenetic profile - Scaled data", xlab = "values")This kind of data are sparse: there are a lot of zero values. Majority of the values are in the first range on the histogram. After scaling, data values seem to follow something close to a normal distribution. Data values are centered to zero.
In this part, we create the similarity network for each data type.
We calculate the Euclidean distance between each pair of samples for each type of data.
tara_nog_dist <- dist2(tara_nog_scaled, tara_nog_scaled)
tara_phy_dist <- dist2(tara_phy_scaled, tara_phy_scaled)The distance matrix dimensions are 139 rows and 139 columns. Indeed, we calculated pairwise distance, so the matrix contains samples in rows and in columns.
## [1] 139 139
The diagonal is composed of zero values (or values very closed). Indeed, there is no distance between the same sample.
## TARA_109_SRF TARA_149_MES TARA_110_MES TARA_102_MES TARA_142_SRF
## TARA_109_SRF 1.705303e-13 536.8095 329.8441 5.991295e+02 646.4804
## TARA_149_MES 5.368095e+02 0.0000 261.2395 3.325547e+02 738.4168
## TARA_110_MES 3.298441e+02 261.2395 0.0000 2.078134e+02 638.5818
## TARA_102_MES 5.991295e+02 332.5547 207.8134 2.273737e-13 877.6855
## TARA_142_SRF 6.464804e+02 738.4168 638.5818 8.776855e+02 0.0000
High distance values mean that samples are not similar. And small distance values mean that samples are similar.
The distance matrix is then transformed into similarity matrix for each data type. We set two parameters:
K = 20: number of nearest neighborssigma = 0.5: hyperparameterThe affinityMatrix() function transforms the distance into similarity according the distance with the nearest neighbors.
tara_nog_W <- affinityMatrix(tara_nog_dist, K, signma)
tara_phy_W <- affinityMatrix(tara_phy_dist, K, signma)The following figures are the heatmap of the similarity matrix (W) of each data type. The left heatmap are the nog data and the right heatmap are the phy data. Samples are clustered using hierarchical clustering. For a better visualization, we log-transform similarities.
pheatmap(log(tara_nog_W, 10), show_rownames = FALSE, show_colnames = FALSE, annotation = tara_metadata, main = "Orthologous genes - log10-transformed similarity values")
pheatmap(log(tara_phy_W, 10), show_rownames = FALSE, show_colnames = FALSE, annotation = tara_metadata, main = "Phylogenetic profil - log10-transformed similarity values")Red color means a high similarity value between two samples whereas blue color means a small similarity value between two samples.
The two heatmaps are different. Data seem to probably carry different type of information about the samples:
We created a similarity matrix for each data type. We saw that each network carries common information and its own information. Now, we will integrate all this information into only one fused similarity matrix.
We create the fused similarity matrix using these three parameters:
K = 20: number of the nearest neighborsT = 10: number of iterations## TARA_109_SRF TARA_149_MES TARA_110_MES TARA_102_MES TARA_142_SRF
## TARA_109_SRF 5.000000e-01 9.778842e-05 0.0002535809 0.0004410605 0.0019200171
## TARA_149_MES 9.778842e-05 5.000000e-01 0.0129882372 0.0126485110 0.0004977051
## TARA_110_MES 2.535809e-04 1.298824e-02 0.5000000000 0.0266185584 0.0029147232
## TARA_102_MES 4.410605e-04 1.264851e-02 0.0266185584 0.5000000000 0.0009179439
## TARA_142_SRF 1.920017e-03 4.977051e-04 0.0029147232 0.0009179439 0.5000000000
The dimension of the fused similarity matrix are 139 rows and 139 columns, such as the previous similarity matrices. The fused similarity matrix contains similarities between samples, we can also called them weights.
The fused similarity matrix contains 19321 weights.
## [1] 19321
The fused similarity matrix doesn’t contain zero:
## [1] 0
The following figure is the heatmap of the fused similarity matrix. Samples are automatically clustered with a hierarchical clustering. Weights are log-transformed for a better visualization.
pheatmap(log(tara_W, 10), show_rownames = FALSE, show_colnames = FALSE, annotation = tara_metadata, main = "Tara Ocean data - log10-transformed fused similarity matrix")Groups are more clearly defined in this fused similarity matrix. One corresponds to the depth MESO and other to a mix of DCM and SRF. The fused similarity matrix seems to be a mix of each similarity data type matrix.
Now, we create a fused similarity network from the fused similarity matrix. Self loops are remove (diag = FALSE) and only the upper values of the matrix are taken (mode = "upper", avoid duplicate information).
Then, the fused similarity network is saved into a the TaraOcean_W_edgeList.txt file:
write.table(as_data_frame(tara_W_net), "../02_Results/01_TaraOcean/TaraOcean_W_edgeList.txt", quote = FALSE, col.names = TRUE, row.names = FALSE, sep = "\t")Figure 4.1: The fused similarity network of the Tara Ocean dataset.
According to Cytoscape, the network contains 139 samples (nodes) and 9591 connections (edges). The number of edges is smaller than in the similarity matrix because in the similarity matrix the weights are duplicates. The similarity matrix contains also the weights for each sample compare to itself (self loops).
For now, the network is fully connected: each sample is connected to every sample. Connections between samples are weights: some connections are strong (samples are similar) some other are weak (samples are not similar).
So in this section, we will choose a threshold to keep the strongest connections.
We extract the weight to display the corresponding distribution to try to find a threshold. The distribution in the left is created using the raw weights. The distribution in the right is created using the log-transformed weights.
tara_weights <- edge.attributes(tara_W_net)$weight
hist(tara_weights, nclass = 100, main = "Fused similarity network weight distribution", xlab = "weights")
hist(log(tara_weights, 10), nclass = 100, main = "Fused similarity network weight distribution", xlab = "weights")
abline(v = log(0.001, 10), col = "cyan", lwd = 3)The log-transformed weight distribution shows a binomial distribution. We would probably like to cut between the two peaks and choose the corresponding weight: 0.001 With this threshold, we select 6440 connections.
We calculate the median of the weights.
## [1] 0.002143026
With the mean (0.002143) as threshold, we select 4796 connections.
## [1] 4796
Calculate the third quantile of the weights:
## 75%
## 0.004119391
With the third quantile (0.0041194) as threshold, we select 2398 connections.
## [1] 2398
In the following figure, we display the log-transformed weight distribution with the two previous calculated metrics as markers.
hist(log(tara_weights, 10), nclass = 100, main = "Fused similarity network weight distribution", xlab = "log10(weights)")
abline(v = log(tara_W_median, 10), col = "blue", lwd = 3)
text(log(tara_W_median, 10), 400, pos = 2, "Median", col = "blue", cex = 1)
abline(v = log(tara_W_q75, 10), col = "purple", lwd = 3)
text(log(tara_W_q75, 10), 400, pos = 4, "quantile 75%", col = "purple", cex = 1)To determine a range of thresholds to try, we check the weights.
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000570 0.0004456 0.0021613 0.0071942 0.0042193 0.5000000
We set the range between 0 and 0.0005.
## [1] 201
Then, we calculate the Average Clustering Coefficient for each threshold.
tara_W_ACC <- do.call(rbind, lapply(thresholds, function(t, net){
net_sub <- subgraph.edges(net, E(net)[weight >= t])
df <- data.frame("ACC" = ACCCalculation(net_sub), "thresholds" = t, "EN" = length(E(net_sub)))
return(df)
}, tara_W_net))Calculated values are displayed in the following figures.
plot(x = tara_W_ACC$thresholds, y = tara_W_ACC$ACC, xlab = "thresholds", ylab = "ACC", main = "ACC calculation of the Fused network W", type = "o")
points(x = tara_W_ACC$thresholds[1], y = tara_W_ACC$ACC[1], col = "red", pch = 16, cex = 1.2)
points(x = tara_W_ACC$thresholds[21], y = tara_W_ACC$ACC[21], col = "pink", pch = 16, cex = 1.2)
points(x = tara_W_ACC$thresholds[29], y = tara_W_ACC$ACC[29], col = "purple", pch = 16, cex = 1.2)
abline(v = tara_W_ACC$thresholds[29], col = "purple")
text(tara_W_ACC$thresholds[29], 0.7, pos = 4, paste0("Threshold = ", tara_W_ACC$thresholds[29]), col = "purple")
text(tara_W_ACC$thresholds[29], 0.6, pos = 4, paste0("ACCmax = ", round(tara_W_ACC$ACC[29], 2)), col = "purple")
plot(x = tara_W_ACC$thresholds, y = tara_W_ACC$EN, xlab = "thresholds", ylab = "number of edges", main = "EN of the Fused network W", type = "o")
abline(v = tara_W_ACC$thresholds[29], col = "purple")
text(tara_W_ACC$thresholds[29], 2800, pos = 4, paste0("Threshold = ", tara_W_ACC$thresholds[29]), col = "purple")
text(tara_W_ACC$thresholds[29], 2000, pos = 4, paste0("ACCmax = ", round(tara_W_ACC$ACC[29], 2)), col = "purple")
text(tara_W_ACC$thresholds[29], 1200, pos = 4, paste0("EN = ", tara_W_ACC[29, "EN"]), col = "purple")Figure 4.2: Left: Average Clustering Coeeficient (ACC) values for each threshold - Right: Number of edges on network for each threshold
If we selected the purple local maxima, we will have 404 edges. It could be not enough edges. Let’s see during the visualization.
## [1] 0.014
The network visualization on the left was created with the third quantile (0.0041194). The network visualization on the right was created with the ACC method (0.014).