%\textwidth=6.2in \textheight=8.5in %\parskip=.3cm \oddsidemargin=.1in \evensidemargin=.1in %\headheight=-.3in %\VignetteIndexEntry{Case study: predicting protein function} %\VignettePackage{BiocStyle} %\VignetteEngine{utils::Sweave} \documentclass[12pt]{article} <>= BiocStyle::latex() @ \usepackage{dsfont} \usepackage{booktabs} \usepackage{fixltx2e} %subscripts %\usepackage{hyperref} \newcommand{\textq}[1]{``#1''} % \newcommand{\Rcode}[1]{{\texttt{#1}}} % \newcommand{\Robject}[1]{{\texttt{#1}}} % \newcommand{\Rfunction}[1]{{\texttt{#1}}} % \newcommand{\Rpackage}[1]{{\textit{#1}}} % \newcommand{\Rclass}[1]{{\textit{#1}}} \newcommand{\Rmethod}[1]{{\textit{#1}}} \newcommand{\Rfunarg}[1]{{\textit{#1}}} \begin{document} \SweaveOpts{concordance=TRUE, include = TRUE, echo=TRUE} %\SweaveOpts{fig=FALSE, prefix.string=yeast_latexfiles/} %\setkeys{Gin}{width=1\textwidth} \bioctitle[The diffuStats R package]{diffuStats: an R package to compute diffusion-based scores on biological networks} \author[1,2]{Sergio Picart-Armada\thanks{\email{sergi.picart@upc.edu}}} \author[3,4]{Wesley K. Thompson} \author[3]{Alfonso Buil} \author[1,2]{Alexandre Perera-Lluna} \affil[1]{B2SLab, Departament d'Enginyeria de Sistemes, Autom\`atica i Inform\`atica Industrial, Universitat Polit\`ecnica de Catalunya, CIBER-BBN, Barcelona, 08028, Spain} \affil[2]{Institut de Recerca Pedi\`atrica Hospital Sant Joan de D\'eu, Esplugues de Llobregat, Barcelona, 08950, Spain} \affil[3]{Mental Health Center Sct. Hans, 4000 Roskilde, Denmark} \affil[4]{Department of Family Medicine and Public Health, University of California, San Diego, La Jolla, California, USA} %\small{\author{Sergio Picart-Armada, %Wesley K. Thompson, \\ %Alfonso Buil and Alexandre Perera-Lluna}} \maketitle \section{Abstract} Label propagation approaches are a standard and ubiquitous procedure in computational biology for giving context to molecular entities. Node labels, which can derive from gene expression, genome-wide association studies, protein domains or metabolomics profiling, are propagated to their neighbours, effectively smoothing the scores through prior annotated knowledge and prioritising novel candidates. However, there are several settings to tune when defining the diffusion process, including the diffusion kernel, the numeric codification of the labels and a choice of statistical normalisation of the scores. These settings can have a large impact on results, and there is currently no software implementing many of them in one place to screen their performance in the application of interest. This vignette presents \Rpackage{diffuStats}, an R package with a collection of diffusion kernels and scores, as well as a parallel permutation analysis for the normalised scores, that eases the analysis of several sets of molecular entities at once. \section{Introduction} The application of label propagation algorithms \cite{labelpropagation} is based on the guilt by association principle \cite{gba}, which can be rephrased in the protein-protein interaction context as \textq{proteins that interact are more likely to share biological functions}. However, this principle is extremely general and has experienced success in numerous applications in bioinformatics. HotNet \cite{hotnet} uses a diffusion process with mutated genes as seed nodes to find modules with a statistically high number of mutated genes in cancer. Another attempt to find relevant modules from gene expression and mutation data can be found in \cite{mosca}, where the authors propose a diffusion process followed by a statistical normalisation and an automatic process to extract a subnetwork. TieDIE \cite{tiedie} runs two diffusion processes to link perturbation in the genome with changes in the transcriptome, effectively linking two sets of genes. GeneMANIA \cite{genemania} is a web server that predicts gene function using label propagation with a bias on the unlabelled nodes. Network-based learning sharing a background with diffusion has been also applied to protein classification using multiple networks \cite{fastprotein}. Label propagation using graph kernels has been proven successful in gene-disease association \cite{valentini, diffusion_gwas}. Equivalent formulations can be found under different terminology, like the electrical model applied to prioritise candidate genes in eQTL in \cite{electricalmodel}. The heterogeneity of applications hinders comparisons among approaches, therefore tools gathering the state of the art are highly needed. An existing solution is \Rpackage{RANKS}, an R package that contains a variety of diffusion kernels and kernelised scores for label propagation using a binary input vector. \Rpackage{RANKS} eases kernelised scores benchmarking and models it as a \textq{one-class} classification semi-supervised learning problem, in which only some members of the class (positives) are known. Another possibility is to divide nodes into labelled positive, negative and unlabelled, like in \cite{genemania}, which poses questions like the effect of unlabelled nodes and possible numeric codifications of the labels, or the option to include quantitative data in the labels. In addition, statistical normalisations such as in \cite{mosca} remove the effect of network structures like hubs and should be taken into account when choosing a diffusion scoring method. This motivates the introduction of our \Rpackage{diffuStats} R package, which collects widely adopted input codifications and explicitly accounts for unlabelled nodes. It also includes three statistically normalised scores, which can be obtained through Monte Carlo trials or a parametric alternative. The \Rpackage{diffuStats} package uses existent classes and provides high-level functions to screen the performance of several diffusion scores, in order to facilitate their integration in any computational biology study. \section{Methodology} One of the main purposes of \Rpackage{diffuStats} is to offer a battery of approaches to compute and compare diffusion scores. The diffusion scores $f$ using an input vector $y$ and a diffusion kernel $K$ are generally computed as $$ f = K\cdot y$$ possibly followed by further adjustments or a statistical normalisation. The decisions taken in the definition of $K$, $y$ and the posterior normalisation generally give rise to different priorisations due to a different treatment of the balance between positive and negative examples, the unlabelled data and the network structure. The following sections cover the implemented choices for the kernel $K$ and the initial labels $y$. \subsection{Diffusion kernels and regularisation} The representation of any kind of data in a network model allows the definition of notions like distance or similarity based on the links in the network. This section will follow the notation in \cite{smola} and summarise the kernels proposed by the authors. In general, an undirected graph $G = (V, E)$ consists of a set of $n$ nodes $V$ and a set of edges $E$ of unordered pairs of nodes. This can be extended to weighted, undirected graphs, where each edge $i \sim j$ has a weight attribute $W_{ij} \in [0, \infty)$. The degree matrix of $G$ is defined as the $n \times n$ diagonal matrix so that $D_{ii} = \sum_{j=1}^n W_{ij}$. The (unnormalised) Laplacian of $G$ is defined as the $n \times n$ matrix $L = D - W$, whereas its normalised version is $\tilde{L} = D^{-\frac{1}{2}}\cdot L \cdot D^{-\frac{1}{2}}$. The graph Laplacian is diagonalisable and can be written in terms of its eigenvalues $v_j$ and eigenvectors $\lambda_j$, as $L = \sum_{j=1}^n \lambda_j v_j v_j^T$. The proposed kernels stem from a family of regularisation functions $r(\lambda)$ on the spectrum of the graph Laplacian: $$K = \sum_{j=1}^n r^{-1}(\lambda_j) v_j v_j^T$$ Well known graph kernels belong to this family because they can be written as transformations on the Laplacian spectrum. Table \ref{tab:kernels} summarises them, assuming the usage of the normalised Laplacian - the unnormalised Laplacian can also be used as long as the resulting kernel is still positive semidefinite. Further details about this family of kernels, all available in our package \Rpackage{diffuStats}, can be found in the original manuscript \cite{smola}. \begin{table}[!t] \begin{center} \begin{tabular}{@{}ll@{}} \toprule Kernel & Function \\\midrule Regularised Laplacian & $r(\lambda) = 1 + \sigma^2\lambda$ \\ Diffusion process & $r(\lambda) = \exp(\frac{\sigma^2}{2}\lambda)$ \\ $p$-Step random walk & $r(\lambda) = (a - \lambda)^{-p}$ with $a \geq 2$, $p \geq 1$ \\ Inverse cosine & $r(\lambda) = (cos(\lambda \frac{\pi}{4}))^{-1}$ \\\bottomrule \end{tabular} \caption{Implemented diffusion kernels from \cite{smola}} \label{tab:kernels} \end{center} \end{table} Additionally, the \Rpackage{diffuStats} package includes the commute time kernel, introduced in \cite{ctkernel}. This kernel, also writable in terms of a regularisation function, is simply the pseudoinverse of the graph Laplacian, $K = L^+$. The default option in the \Rpackage{diffuStats} package is the regularised Laplacian kernel, as it is widely adopted and describes many physical models, for instance in \cite{hotnet, tiedie, electricalmodel}. \subsection{Diffusion scores} Besides choosing a graph kernel, the codification of the input and the presence of a statistical normalisation can lead to important differences in the results. Table \ref{tab:scores} gives an overview of the implemented scores, which will be detailed in the following sections. The argument \Rfunarg{method} in the function \Rfunction{diffuse} can be set to the desired scores in table \ref{tab:scores}, which are described in the following sections. The numeric values of the positive, negative and unlabelled examples are respectively $y^+$, $y^-$ and $y^u$. Column \textq{normalised} refers to the application of a statistical model involving permutations, whereas \textq{stochastic} enumerates the normalised scores that need actual Monte Carlo permutations. The scores that also accept quantitative inputs instead of binary labels are listed in the \textq{quantitative} column. \begin{table}[!t] \begin{center} \resizebox{\textwidth}{!}{ \begin{tabular}{@{}llllllll@{}} \toprule Score & $y^+$ & $y^-$ & $y^u$ & Normalised & Stochastic & Quantitative & Reference \\\midrule raw & 1 & 0 & 0 & No & No & Yes & \cite{hotnet} \\ ml & 1 & -1 & 0 & No & No & No & \cite{fastprotein, labelpropagation} \\ gm & 1 & -1 & $k$ & No & No & No & \cite{genemania} \\ ber\textsubscript{s} & 1 & 0 & 0 & No & No & Yes & \cite{mosca} \\ ber\textsubscript{p} & 1 & 0 & 0* & Yes & Yes & Yes & \cite{mosca} \\ mc & 1 & 0 & 0* & Yes & Yes & Yes & \cite{mosca} \\ z & 1 & 0 & 0* & Yes & No & Yes & \cite{kerneltesting} \\\bottomrule \end{tabular} } \caption{Implemented diffusion scores} \label{tab:scores} \end{center} \end{table} \subsubsection{Scores without statistical normalisation} The base diffusion score \Rcode{raw}, which has been used in algorithms like HotNet \cite{hotnet} and TieDIE \cite{tiedie}, solves a diffusion problem in terms of the regularised Laplacian kernel \cite{smola}. $$ f_{raw} = K\cdot y_{raw} $$ $K$ is the kernel matrix and $y_{raw}$ the vector of codified inputs. In general, the $i$-th component of $y$ equals $y^+$ if node $i$ is a positive, $y^-$ if $i$ is a negative and $y^u$ if $i$ is unlabelled. In the particular case of $y_{raw}$, the positively labelled nodes introduce one flow unit in the network ($y_{raw}^{+}=1$), whereas the negative and unlabelled nodes are treated equivalently and do not introduce anything ($y_{raw}^{-}=y_{raw}^{u}=0$). In the physical model using the regularised Laplacian kernel, the flow can be evacuated from the graph due to the presence of first-order leaking in every node, see \cite{hotnet} for further details on this. This formulation is proportional up to a scaling factor to the average score in \Rpackage{RANKS}. On the other hand, the classical label propagation \cite{labelpropagation} treats positives as $y_{ml}^{+}=1$ and negatives as $y_{ml}^{-}=-1$, while unlabelled nodes remain as $y_{ml}^{u}=0$, thus making a distinction between the last two. A biological example can be found in protein classification \cite{fastprotein}. This option is available as \Rcode{ml}, and intuitively scores a node by counting if the majority of its neighbours vote positive or negative: $$ f_{ml} = K\cdot y_{ml} $$ The authors of GeneMANIA \cite{genemania} propose a modification on the \Rcode{ml} input - they adhere to $y_{gm}^{+}=1$ and $y_{gm}^{-}=-1$, but introduce a bias term in the unlabelled nodes $$y_{gm}^{u} = \frac{n^{+}-n^{-}}{n^{+}+n^{-}+n^{u}}$$ being $n^{+}$, $n^{-}$ and $n^{u}$ the number of positives, negatives and unlabelled entities. The \Rcode{gm} score is then computed through $$ f_{gm} = K\cdot y_{gm} $$ The last option in this part, named \Rcode{ber\_s} \cite{mosca}, is a quantification of the relative change in the node score before and after the network smoothing. The score for a particular node $i$ can be written as $$ f_{ber_s, i} = \frac{f_{raw, i}}{y_{raw, i} + \epsilon}$$ where $\epsilon > 0$ is a parameter that regulates the importance of the relative change. \subsubsection{Scores with statistical normalisation} Recently, the combination of a permutation analysis with diffusion processes has been suggested \cite{mosca}. This is a way to quantify how the diffusion score of a certain node compares to its score if the input was randomised - nodes that might have systematically high or low scores regardless of the input are normalised accordingly. The cornerstone of normalised scores is the empirical p-value \cite{empiricalpvalue} that indicates, for a node $i$, the proportion of input permutations that led to a diffusion score as high or higher than the original diffusion score. Specifically, $f_{raw}$ is compared to scores from random trials $j$, $f_{raw}^{null, j} = K\cdot \pi_j(y_{raw})$, where $\pi_j(y_{raw})$ is a permutation of $y_{raw}$ on the labelled entities. The empirical p-value for node $i$ is therefore defined as in \cite{empiricalpvalue}: $$p_i = \frac{r_i + 1}{n + 1}$$ being $r_i$ the number of trials $j$ in which $f_{raw, i}^{null, j} \geq f_{raw, i}$, and $n$ the total number of trials. To be consistent with the increasing direction of the scores, the \Rcode{mc} scores are defined as $$f_{mc, i} = 1 - p_i$$ Importantly, the permutation has been applied only to the observed nodes. Therefore, any node outside the labelled background cannot receive a different input score in a permuted input. This implies that even if both negatives and unlabelled nodes are assigned the same input value ($y^{-}=y^{u}=0$), the negatives are actually permuted and eventually exchanged for a $1$ in some permutations provided that the number of runs is large enough. For this reason, negatives and unlabelled nodes are not equivalent in these scores. A parametric alternative to $f_{mc}$, are \Rcode{z} scores, where each node $i$ is scored as: $$f_{z,i} = \frac{f_{raw, i} - E(f_{raw, i}^{null, j})} {\sqrt{V(f_{raw, i}^{null, j})}}$$ The expectation and variance are computed in a closed form and these scores do not require running actual permutations, therefore saving on computational time and avoiding stochastic models. The analytical expression proven below also works for the general case when the input has quantitative labels. First of all, note that all the unlabelled nodes will cancel out when substracting the expected value, so they can be excluded without loss of generality. Thus, let the row vector $\tilde{k}_{i}$ be the $i$-th row of the submatrix from $K$ including the columns indexed by the labelled nodes only, and let $n_{lab}$ be the number of labelled nodes. Analogously, let $\tilde{y}$ be the (quantitative or binary) inputs for the observed nodes, with the same indexing as $\tilde{k}_{i}$. Consider the following scalar quantities: $$ Sk_i^I = \sum_{j = 1}^{n_{lab}} (\tilde{k}_{i})_{j} $$ $$ Sk_i^{II} = \sum_{j = 1}^{n_{lab}} (\tilde{k}_{i})_{j}^2 $$ $$ Sy^I = \sum_{j = 1}^{n_{lab}} \tilde{y}_j $$ $$ Sy^{II} = \sum_{j = 1}^{n_{lab}} \tilde{y}_j^2 $$ Using these definitions and notation, the raw score for node $i$ is $$f_i = \tilde{k}_{i} \cdot \tilde{y}$$ Its null score using a permuted input score is the random variable $$ f_i^{null} = \tilde{k}_{i} \cdot X $$ where $X$ is the random variable that arises from permuting $\tilde{y}$. In order to compute the z-scores, the expected value and variance of $f_i^{null}$ are needed. Starting with its expected value, $$ E_X(f_i^{null}) = E_X(\tilde{k}_{i} \cdot X) = \tilde{k}_{i}\cdot E_X(X) = \tilde{k}_i \cdot\frac{\sum_{j = 1}^{n_{lab}} \tilde{y}_j}{n_{lab}}\cdot\mathds{1}= \frac{Sk_i^I \cdot Sy^I}{n_{lab}} $$ where $\mathds{1}$ is the $n_{lab}$-th dimensional column vector full of ones. Regarding its variance, $$ V_X(f_i^{null}) = V_X(\tilde{k}_{i} \cdot X) = \tilde{k}_{i}\cdot V_X(X)\cdot \tilde{k}_{i}^T $$ The covariance of $X$ can be written as $$ V_X(X) = \left[ \frac{\sum_{j = 1}^{n_{lab}} \tilde{y}_j^2}{n_{lab}} - \left(\frac{\sum_{j = 1}^{n_{lab}} \tilde{y}_j}{n_{lab}}\right)^2 \right] \left[ \frac{n_{lab}}{n_{lab} - 1}Id - \frac{1}{n_{lab} - 1}\mathds{1}\mathds{1}^T \right]$$ being $Id$ the $n_{lab} \times n_{lab}$ identity matrix. Operating, the variance of $f_i^{null}$ can be finally computed as $$ V_X(f_i^{null}) = \frac{1}{(n_{lab} - 1)n_{lab}^2} \left[n_{lab}Sy^{II} - (Sy^I)^2 \right] \left[n_{lab}Sk_i^{II} - (Sk_i^I)^2 \right]$$ The z-score for node $i$ is, in terms of the new notation: $$f_{z,i} = \frac{f_{i} - E_X(f_{i}^{null})} {\sqrt{V_X(f_{i}^{null})}}$$ This closes the \Rcode{z} scoring option, which clearly does not require any actual permutations but normalises through the theoretical first and second statistical moments of the null distribution of the diffusion scores. Finally, the authors in \cite{mosca} also suggest a combination of a classical score with an statistically normalised one. Available as \Rcode{ber\_p}, the score of node $i$ is defined as $$f_{ber_p, i} = -\log_{10}(p_i)\cdot f_{raw, i}$$ This approach corrects the original diffusion scores by the effect of the network, in order to mitigate the effect of structures like hubs. \subsubsection{Quantitative inputs} In its current release, \Rpackage{diffuStats} also accepts quantitative labels as input in the following scores: \Rcode{raw}, \Rcode{ber\_s}, \Rcode{z}, \Rcode{ber\_p} and \Rcode{mc}. The scores \Rcode{ml} and \Rcode{gm} are naturally excluded from non-binary inputs due to their definitions. Currently \Rcode{mc} scores (and therefore \Rcode{ber\_p} scores as well) accept quantitative inputs that are treated as sparse. Dense continuous inputs might take more time to compute, but this will be extended in future versions. Beware that quantitative inputs should be meaningful and one-tailed (monotonic) - the nature of diffusion processes involves averaging and strong effects with opposing signs cancel out instead of adding up. \subsection{Implementation, functions and classes} The package \Rpackage{diffuStats} is mainly implemented in R \cite{r}, but takes advantage of existing classes in \Rpackage{igraph} \cite{igraph} and basic data types, thus not introducing any new data structure - this minimises the learning effort by the final user. Inputs and outputs are conceived to require minimal formatting effort in the whole analysis. The computationally intense stochastic part of the permutation analysis is implemented in C++ through the packages \Rpackage{Rcpp} \cite{rcpp}, \Rpackage{RcppArmadillo} \cite{rcpparmadillo} and parallelised through \Rpackage{RcppParallel} \cite{rcppparallel}. Package \Rpackage{diffuStats} also contains documented functions and unit testing with small cases to spot potential bugs. Two vignettes facilitate the user experience: an introductory vignette showing the basic usage of the functions on a synthetic example and this vignette, which further describes the contents of the package and shows an application to real data. \begin{figure}[!tpb]%figure1 {\includegraphics{diffustats_fig_overview.eps}} \caption{Overview of the main package functions.} \label{fig:overview} \end{figure} A diagram containing the main functions in the R package \Rpackage{diffuStats} can be found in (Fig. \ref{fig:overview}). The main funcion is \Rfunction{diffuse}, a wrapper for computing diffusion scores from several categories at once, stemming from possibly different observed backgrounds. Function \Rfunction{diffuse} makes use of the deterministic \Rfunction{diffuse\_raw} or the stochastic \Rfunction{diffuse\_mc} implementations and combines them to give the desired scores for all the nodes in each of the observed backgrounds. The second wrapper \Rfunction{perf} compares the result of the diffusion scores to target validation scores. Validation scores might include only part of the nodes of the network and these nodes can be background-specific. Regarding memory and processing power requirements, the analysis of networks with more than 10,000 nodes might need additional RAM memory and processing power. The main reason is the manipulation of dense graph kernel matrices that scale quadratically with the network order. To give a reference, a dense matrix of double-precision real numbers with 10,000 rows and columns uses roughly 800MB of memory. Computing a graph kernel on a large network can require -depending on the kernel- matrix diagonalisation, matrix products and matrix inversion operations that are likely to use a considerable amount of memory and time. \subsection{Limitations} The kernel framework is known to scale poorly with the number of nodes of the network when the kernel is explicitly computed and dense. Therefore, \Rpackage{diffuStats} is best suited for manipulating biological networks of a medium size - few tenths of thousands of nodes. Protein-protein interaction networks can have around 20,000 nodes, which is also the limit of the capabilities \Rpackage{diffuStats}, as it is now, in a standard workstation with 16GB of physical memory. In particular, the explicit kernel computation is a demanding step, although it only has to be performed once with a given network. Figure \ref{fig:profiling} contains a profiling on the kernel computation using a Compaq q8100 workstation (intel core i5 650 @3.20GHz, 16GB physical memory). This should give an approximation of what to expect in terms of time and memory consumption. The same figure contains the memory usage of the kernel matrix per se. \begin{figure}[!tpb]%figure1 {\includegraphics{diffustats_fig_profiling.eps}} \caption{Profiling of the computation of the regularised Laplacian kernel on a synthetic n-th order network. Undirected networks are generated through \Rfunction{barabasi.game} in \CRANpkg{igraph} using default parameters and \Rfunarg{m = 6} (each node adds 6 edges).} \label{fig:profiling} \end{figure} On the other hand, the parallel implementation of the stochastic permutation analysis is another demanding task. It has been optimised assuming that the number of positives is usually low. Lists of scores with a very high amount of positives might slow down the permutation analysis, but not the parametric \Rcode{z}. \section{Getting started} This vignette contains a classical example of label propagation on a biological network. The core tools for this analysis are the \Rpackage{igraph} R package \cite{igraph} and the \Rpackage{diffuStats} package, whereas \Rpackage{ggplot2} \cite{ggplot2} is a convenient tool to plot the results. The data for this example is the \Rcode{yeast} interactome with functional annotations, as found in the data package \Rcode{igraphdata} \cite{igraphdata}. <<01_imports>>= # Core library(igraph) library(diffuStats) # Plotting library(ggplot2) library(ggsci) # Data library(igraphdata) data(yeast) set.seed(1) @ \subsection{Data description} A summary of the network object can be obtained by just showing the object: <<02_data1>>= summary(yeast) @ For this analysis, only the largest connected component of this graph will be used, although the algorithms can handle graphs with several connected components. <<02_data2>>= yeast <- diffuStats::largest_cc(yeast) @ This yields to a graph with \Sexpr{format(vcount(yeast))} nodes and \Sexpr{format(ecount(yeast))} edges. There are several attributes that can be of interest. First of all, the \Rcode{name} of the protein nodes: <<02_data3>>= head(V(yeast)$name) @ Furthermore, the corresponding aliases and complete names can be found in \Rcode{Description} <<02_data4>>= head(V(yeast)$Description) @ The labels to perform network propagation are MIPS categories \cite{mips}, which provide means to classify proteins regarding their function. These functions are coded as characters in the \Rcode{yeast} object, in the node attribute \Rcode{Class} <<02_data5>>= table_classes <- table(V(yeast)$Class, useNA = "always") table_classes @ The graph attribute \Rcode{Classes} maps these abbreviations to the actual category: <<02_data6>>= head(yeast$Classes) @ Finally, the graph edges have a \Rcode{Confidence} attribute that assesses the amount of evidence supporting the interaction. All the edges will be kept in this analysis, but different confidences can be weighted to favour diffusion in high confidence edges. <<02_data7>>= table(E(yeast)$Confidence) @ More on the yeast object can be found through \Rcode{?yeast}. \subsection{First analysis: protein ranking} In this first case, the diffusion scores will be applied to the prediction of a single protein function. Let's assume that 50\% of the labelled proteins in the graph as \Rcode{transport and sensing} (category \Rcode{A}) are actually unlabelled. Now, using the labels of the known positive and negative examples for \Rcode{transport and sensing}, can we correctly label the remaining 50\%? First of all, the list of known and unknown positives is generated. The function \Rfunction{diffuse} uses (row)names in the input scores so that unlabelled nodes are accounted as so. <<03_datasplit>>= perc <- .5 # Transport and sensing is class A nodes_A <- V(yeast)[Class %in% "A"]$name nodes_unlabelled <- V(yeast)[Class %in% c(NA, "U")]$name nodes_notA <- setdiff(V(yeast)$name, c(nodes_A, nodes_unlabelled)) # Known labels known_A <- sample(nodes_A, perc*length(nodes_A)) known_notA <- sample(nodes_notA, perc*length(nodes_notA)) known <- c(known_A, known_notA) # Unknown target nodes target_A <- setdiff(nodes_A, known_A) target_notA <- setdiff(nodes_notA, known_notA) target <- c(target_A, target_notA) target_id <- V(yeast)$name %in% target # True scores scores_true <- V(yeast)$Class %in% "A" @ Now that the input is ready, the diffusion algorithm can be applied to rank all the proteins. As a first approach, the vanilla diffusion scores will be computed through the \Rfunarg{raw} method and the default regularised Laplacian kernel, which is calculated on the fly. <<03_diffuse>>= # Vector of scores scores_A <- setNames((known %in% known_A)*1, known) # Diffusion diff <- diffuStats::diffuse( yeast, scores = scores_A, method = "raw" ) @ Diffusion scores are ready and in the same format they were introduced: <<03_diff1>>= head(diff) @ Now, the scores obtained by the proteins actually belonging to \Rcode{transport and sensing} can be compared to proteins with other labels. <<03_diff2, fig=TRUE>>= # Compare scores df_plot <- data.frame( Protein = V(yeast)$name, Class = ifelse(scores_true, "Transport and sensing", "Other"), DiffusionScore = diff, Target = target_id, Method = "raw", stringsAsFactors = FALSE ) ggplot(subset(df_plot, Target), aes(x = Class, y = DiffusionScore)) + geom_boxplot(aes(fill = Method)) + theme_bw() + scale_y_log10() + xlab("Protein class") + ylab("Diffusion score") + ggtitle("Target proteins in 'transport and sensing'") @ The last plot justifies the usefulness of label propagation, as proteins in \Rcode{transport and sensing} obtain higher diffusion scores than the rest. The network analysis can be deepened by examining, for instance, the subnetwork containing the proteins with the top 30 diffusion scores, highlighting with squares the ones that were positive labels in the input. Notice the small clusters of proteins: <<03_diff3, fig=TRUE>>= # Top scores subnetwork vertex_ids <- head(order(df_plot$DiffusionScore, decreasing = TRUE), 30) yeast_top <- igraph::induced.subgraph(yeast, vertex_ids) # Overlay desired properties # use tkplot for interactive plotting igraph::plot.igraph( yeast_top, vertex.color = diffuStats::scores2colours( df_plot$DiffusionScore[vertex_ids]), vertex.shape = diffuStats::scores2shapes( df_plot$Protein[vertex_ids] %in% known_A), vertex.label.color = "gray10", main = "Top 30 proteins from diffusion scores" ) @ \subsection{Second example: comparing scores with single protein ranking} The proposed diffusion scores can be easily applied and compared. The regularised Laplacian kernel will be used to compute all the implemented scores for the target nodes in \Rcode{transport and sensing}. <<04_kernel>>= K_rl <- diffuStats::regularisedLaplacianKernel(yeast) @ Functions \Rfunction{diffuse} and \Rfunction{perf} do accept, however, an \Rcode{igraph} object as well, and compute the kernel automatically. For medium networks (10,000 nodes or more) the kernel computation can be computationally expensive in memory and time, so precomputing it avoids unnecessary recalculations. The diffusion scores can be computed over a list of methods or sets of parameters. This can be achieved with instructions like \Rfunction{lapply}, but \Rpackage{diffuStats} contains a wrapper to facilitate this task. The function \Rfunction{diffuse\_grid} takes the specified combinations of parameters -which can include the scoring method as well- and computes the diffusion scores for each one. The results are concatenated in a data frame that can be easily plotted: <<04_diff>>= list_methods <- c("raw", "ml", "gm", "ber_s", "ber_p", "mc", "z") df_diff <- diffuse_grid( K = K_rl, scores = scores_A, grid_param = expand.grid(method = list_methods), n.perm = 1000 ) df_diff$transport <- ifelse( df_diff$node_id %in% nodes_A, "Transport and sensing", "Other" ) @ The results can be directly plotted: <<04_plot, fig=TRUE>>= df_plot <- subset(df_diff, node_id %in% target) ggplot(df_plot, aes(x = transport, y = node_score)) + geom_boxplot(aes(fill = method)) + scale_fill_npg() + theme_bw() + theme(axis.text.x = element_text( angle = 45, vjust = 1, hjust = 1)) + facet_wrap( ~ method, nrow = 1, scales = "free") + xlab("Protein class") + ylab("Diffusion score") + ggtitle("Target proteins scores in 'transport and sensing'") @ As expected, all the diffusion scores qualitatively show differences between positive and negative labels, but the quality of class separation will generally depend on the dataset and scoring method. \subsection{Third example: benchmarking scores with multiple protein functions} The package \Rpackage{diffuStats} is able to perform several screenings at once. To show its usefulness, we will generalise the procedure in the last section but screening all the categories in the yeast graph. First of all, the input data must meet an adequate format - a straightforward approach is to populate a matrix with the input labels (one category per column). <<05_scores>>= # All classes except NA and unlabelled names_classes <- setdiff(names(table_classes), c("U", NA)) # matrix format mat_classes <- sapply( names_classes, function(class) { V(yeast)$Class %in% class } )*1 rownames(mat_classes) <- V(yeast)$name colnames(mat_classes) <- names_classes @ The former 50\% known / 50\% unknown approach will be kept with the same split, although not all the \Sexpr{ncol(mat_classes)} categories will be totally balanced in the splits now. All the methods will be compared using the area under the ROC curve (AUROC) as a performance index. Please note that \Rpackage{diffuStats} is equipped with basic performance measures for rankers: the AUROC, the area under the Precision-Recall curve, or AUPRC, and their partial versions. These are available through the helper function \Rfunction{metric\_fun} and can be passed in list format to \Rfunction{perf}. These measures are based on the \CRANpkg{precrec} R package - further detail can be found in the original manuscript \cite{precrec}. <<05_perf>>= list_methods <- c("raw", "ml", "gm", "ber_s", "ber_p", "mc", "z") df_methods <- perf( K = K_rl, scores = mat_classes[known, ], validation = mat_classes[target, ], grid_param = expand.grid( method = list_methods, stringsAsFactors = FALSE), n.perm = 1000 ) @ This allows plotting of the AUCs over the categories for each method in one step: <<05_plot, fig=TRUE>>= ggplot(df_methods, aes(x = method, y = auc)) + geom_boxplot(aes(fill = method)) + scale_fill_npg() + theme_bw() + xlab("Method") + ylab("Area under the curve") + ggtitle("Methods performance in all categories") @ <<05_plotsave, echo=FALSE, eval=FALSE, include=FALSE>>= # Save plot for manuscript # # library(extrafont) setEPS() postscript("data-raw/BoxplotAUC.eps", width = 3.38, height = 3.38) ggplot(df_methods, aes(x = method, y = auc)) + # geom_boxplot(aes(fill = method), outlier.size = 1) + geom_boxplot(outlier.size = 1) + scale_fill_npg(name = "Method") + theme_bw() + xlab("Method") + ylab("Area under the curve") + # coord_fixed() + ggtitle("Benchmark of protein categories") + theme( text = element_text(size = 10), axis.text.x = element_text( size = 8, angle = 45, vjust = 1, hjust = 1, color = "black"), axis.text.y = element_text(size = 8, color = "black"), plot.title = element_text(size = 12, hjust = .5)) dev.off() # Build table for manuscript df_perf <- reshape2::acast(df_methods, Column~method, value.var = "auc") df_test <- perf_wilcox( df_perf, digits_p = 1, adjust = function(p) p.adjust(p, method = "fdr"), scientific = FALSE) xtable::xtable(df_test) @ Scaling up the analysis can be useful for assessing how adequate a diffusion score is in the dataset of interest. These results suggest that, for the current \Rcode{yeast} interactome and protein functions, the best priorisations are those obtained through a statistical normalisation, which might motivate its usage in other biological networks. The user can also statistically compare the performance metrics through the function \Rfunction{perf\_wilcox}. This generates a table with (i) the estimates on the differences on performance between the methods in rows and columns, with their confidence intervals and (ii) their associated p-value (Wilcoxon test). Positive and negative estimates respectively favour the method in the row and the column. <<05_test, results=tex>>= # Format the data df_perf <- reshape2::acast(df_methods, Column~method, value.var = "auc") # Compute the comparison matrix df_test <- perf_wilcox( df_perf, digits_p = 1, adjust = function(p) p.adjust(p, method = "fdr"), scientific = FALSE) knitr::kable(df_test, format = "latex") @ \section{Conclusions} The \Rpackage{diffuStats} package is a new computational tool to compute and compare single-network diffusion scores that are object of active research in several bioinformatics areas. It is an effort to gather a collection of settings in the diffusion process like the graph kernel, the label codification and the choice of a statistical normalisation. The \Rpackage{diffuStats} package will help the end user in choosing and computing the best performing diffusion scores in the application of interest. \section{Funding} This work was supported by the Spanish Ministry of Economy and Competitiveness (MINECO) [TEC2014-60337-R to A.P.] and the National Institutes of Health (NIH) [R01GM104400 to W.T.]. AP. and S.P. thank for funding the Spanish Biomedical Research Centre in Diabetes and Associated Metabolic Disorders (CIBERDEM) and the Networking Biomedical Research Centre in the subject area of Bioengineering, Biomaterials and Nanomedicine (CIBER-BBN), both initiatives of Instituto de Investigaci\'on Carlos III (ISCIII). SP. thanks the AGAUR FI-scholarship programme. \newpage %\bibliographystyle{plain} % \bibliographystyle{apalike} \bibliography{bibliography} \newpage \appendix \section{Session info} Here is the output of \Rfunction{sessionInfo()} on the system that compiled this vignette: <>= toLatex(sessionInfo()) @ \end{document}