R/networks.R

Defines functions networks.diffc networks.hclust networks.tnodes networks.cdistr network.kstest network.pca.pltvar network.pca.contri network.pca.varexp network.pca.hclust is.networks is.network

Documented in is.network is.networks network.kstest network.pca.contri network.pca.hclust network.pca.pltvar network.pca.varexp networks.cdistr networks.diffc networks.hclust networks.tnodes

#' Checks if object is a interactive omics network object
#'
#' @param network An object
#' 
#' @return A bool
#' 
#' @export
is.network <- function(network) {
    if (!is.ionet(network)) {
        stop("Detected a non-network object ")
    }
    return(TRUE)
}

#' Checks if object is a list of interactive omics network objects
#'
#' @param networks A list of objects
#' 
#' @return A bool
#' 
#' @export
is.networks <- function(networks) {
    if (!is(networks, "list")) {
        stop("Expected a list of network objects")
    }
    if (length(networks) == 0) {
        stop("Expected at least one network objects")
    }
    sh <- lapply(networks, is.network)
    return(TRUE)
}

#' PCA - Hierarchical Clustering
#' 
#' @param network An interactive omics network object
#' @return A ggplot object
#' 
#' @importFrom factoextra get_dist hcut fviz_dend
#' @importFrom ggplot2 theme element_text
#' 
#' @export
network.pca.hclust <- function(network) {
    nodes <- network$nodes
    available.measures <- base::intersect(names(ig.centrality()), colnames(nodes))
    x <- nodes[,available.measures]
    x <- na.omit(x)
    x.scaled <- scale(x, center=TRUE, scale=TRUE)
    x.dist <- factoextra::get_dist(t(x.scaled), method="euclidean")
    k.colors <- c("#00AFBB","#2E9FDF", "#E7B800", "#FC4E07")
    n.measures <- length(available.measures)
    if (n.measures >= 6) {
        k <- 4
    } else if (n.measures >= 4) {
        k <- 3
    } else {
        k <- 2
    }
    x.clust <- factoextra::hcut(x.dist, k=k, stand=TRUE)
    p <- factoextra::fviz_dend(x.clust, horiz=TRUE, rect=TRUE, cex=0.7, k_colors=k.colors[1:k]) +
         ggplot2::theme(plot.title=ggplot2::element_text(hjust=0.5)) +
         theme_simplex()

    return(p)
}

#' PCA - Variance Explained
#' 
#' @param network An interactive omics network object
#' 
#' @return A ggplot object
#' 
#' @importFrom factoextra fviz_screeplot
#' @importFrom ggplot2 labs theme_bw theme element_text
#' 
#' @export
network.pca.varexp <- function(network) {
    pca <- network$pca
    p <- factoextra::fviz_screeplot(pca,
                                    barfil="#E7B800", 
                                    barcol="#E7B800") +
         ggplot2::theme_bw() +
         ggplot2::labs(title="Variance Explained", x="PC", y="Percent") +
         ggplot2::theme(plot.title=ggplot2::element_text(hjust=0.5)) +
         theme_simplex()
    
    return(p)
}

#' PCA - Contibutors
#' 
#' @param network An interactive omics network object
#' 
#' @return A ggplot object
#' 
#' @importFrom factoextra fviz_contrib
#' @importFrom ggplot2 labs theme_bw theme element_text
#' 
#' @export
network.pca.contri <- function(network) {
    pca <- network$pca
    p <- factoextra::fviz_contrib(pca, 
                                  choice="var", 
                                  axes=c(1,2), 
                                  color="#00AFBB", 
                                  fill="#00AFBB") +
    ggplot2::theme_bw() +
    ggplot2::labs(title="Contribution to Principal Components", x="", y="Contribution") + 
    ggplot2::theme(plot.title=ggplot2::element_text(hjust=0.5),
          axis.text.x=ggplot2::element_text(angle=45, vjust=0.5)) +
    theme_simplex()
    
    p$layers[[2]] <- NULL # Remove line
    return(p)
}

#' PCA - Show Contributors
#' 
#' @param network An interactive omics network object
#' 
#' @return A ggplot object
#' 
#' @importFrom factoextra fviz_pca_var
#' @importFrom ggplot2 labs theme_bw theme element_text element_blank
#' 
#' @export
network.pca.pltvar <- function(network) {
    pca <- network$pca
    p <- factoextra::fviz_pca_var(pca, 
                                  axes=c(1,2),
                                  col.var="contrib", 
                                  repel=TRUE,
                                  gradient.cols=c("#00AFBB", "#E7B800", "#FC4E07")) +
         ggplot2::theme_bw() +
         ggplot2::labs(title="Variable Contributions") +
         theme(plot.title=ggplot2::element_text(hjust=0.5),
               legend.title=ggplot2::element_blank()) +
         theme_simplex()
    
    return(p)
}

#' Centrality-based enrichment
#' 
#' @param network An interactive omics network object
#' @param metric Centrality measure
#' @param geneset A list of genes or other omics features
#' 
#' @return A ggplot object
#' 
#' @importFrom dplyr arrange select sym
#' @importFrom tibble deframe
#' @importFrom magrittr %>%
#' 
#' @export
network.kstest <- function(network, metric, geneset) {
    signature <- network$nodes %>%
                 dplyr::arrange(desc(!!sym(metric))) %>%
                 dplyr::select(symbol) %>%
                 tibble::deframe()
    
    ranks <- match(geneset, signature)
    ranks <- ranks[!is.na(ranks)]
    results <- kstest(n.x=length(signature), y=ranks, plotting=TRUE)
    return(results$plot)
}

#' Centrality distributions
#' 
#' @param networks A named list of interactive omics network objects
#' @param metric Centrality measure
#' @param plot Plot type
#' @param rescale Rescale values between 0-1
#' 
#' @return A ggplot object
#' 
#' @importFrom dplyr select bind_rows
#' @importFrom stringr str_to_title
#' @importFrom ggpubr ggboxplot ggdensity ggecdf stat_compare_means
#' @importFrom magrittr %>%
#' @importFrom ggplot2 theme element_blank
#' 
#' @export
networks.cdistr <- function(networks, 
                            metric, 
                            plot=c("boxplot", "density", "ecdf"),
                            rescale=FALSE) {
    
    # Check for network object(s)
    is.networks(networks)

    # Default args
    plot <- match.arg(plot)

    df <- mapply(function(x, y) {
        df <- dplyr::select(x$nodes, (!!metric)) %>%
              magrittr::set_colnames("value")
        if (rescale) {
            df$value <- df$value/sum(df$value)
        }
        colnames(df) <- metric
        df$network <- y
        return(df)
    }, networks, names(networks), SIMPLIFY=FALSE) %>%
    dplyr::bind_rows()

    if (plot == "boxplot") {
        p <- ggpubr::ggboxplot(df, x="network", y=metric, fill="network", add="jitter") + 
             ggpubr::stat_compare_means() +
             labs(x="", y=stringr::str_to_title(metric))
    }
    if (plot == "density") {
        p <- ggpubr::ggdensity(df, x=metric, color="network", fill="network", add="mean", rug=TRUE) +
             labs(y="Density", x=stringr::str_to_title(metric))
    }
    if (plot == "ecdf") {
        p <- ggpubr::ggecdf(df, x=metric, color="network") + 
             labs(x=stringr::str_to_title(metric))
    }

    p +
    ggplot2::theme(legend.title=ggplot2::element_blank()) +
    theme_simplex()
}

#' Important Nodes
#' 
#' @param networks A named list of interactive omics network objects
#' @param metric Centrality measure
#' @param top Number of nodes to label
#' @param size Size of labels
#' @param search Include specific nodes for labeling
#' @param log Log-transform y-axis
#' @param reverse Flip y-axis
#' @param symbols Show symbols instead of node identifiers
#' @param shared Highly shared nodes only
#' 
#' @return A ggplot object
#' 
#' @importFrom dplyr select filter bind_cols mutate if_else
#' @importFrom plyr rbind.fill
#' @importFrom reshape2 melt
#' @importFrom ggpubr ggboxplot ggdensity ggecdf stat_compare_means
#' @importFrom magrittr %>% set_colnames
#' @importFrom ggrepel geom_label_repel
#' @importFrom ggplot2 aes geom_jitter xlab ylab theme theme_bw element_text element_blank scale_y_continuous scale_y_reverse
#' @importFrom stringr str_to_title
#' 
#' @export
networks.tnodes <- function(networks, 
                            metric, 
                            top=15, 
                            size=3,
                            search=NULL,
                            log=FALSE, 
                            reverse=FALSE,
                            symbols=FALSE, 
                            shared=FALSE) {

    # Check for network object(s)
    is.networks(networks)
    
    # Data
    df <- mapply(function(x, y) {
        dplyr::select(x$nodes, (!!metric)) %>%
        magrittr::set_colnames(y)
    }, networks, names(networks), SIMPLIFY=FALSE) %>%
    dplyr::bind_cols()

    p <- df %>%
         reshape2::melt(id.vars=NULL) %>%
         ggplot2::ggplot(aes(x=variable, y=value)) + 
         ggplot2::geom_jitter() +
         ggplot2::xlab("") +
         ggplot2::ylab(stringr::str_to_title(metric)) +
         ggplot2::theme_bw() +
         ggplot2::theme(text=ggplot2::element_text(size=18),
                        legend.title=ggplot2::element_blank()) + 
         theme_simplex()
    
    # Labeling
    if (is.null(search)) {
        labeling <- mapply(function(x, label) {
            nodes <- x$nodes
            if (reverse) {
                nodes.sorted <- nodes[order(nodes[,metric]),]
            } else {
                nodes.sorted <- nodes[order(-nodes[,metric]),]
            }
            nodes.top <- nodes.sorted[seq(top),]
            data.frame(id=nodes.top$id, variable=label, value=nodes.top[,metric])
        }, networks, names(networks), SIMPLIFY=FALSE)
        intersecting <- Reduce(intersect, lapply(labeling, function(x) x$id))
    } else {
        labeling <- mapply(function(x, label) {
            nodes <- x$nodes
            nodes.top <- nodes[nodes$id %in% search,]
            data.frame(id=nodes.top$id, variable=label, value=nodes.top[,metric])
        }, networks, names(networks), SIMPLIFY=FALSE)
        intersecting <- c()
    }
    
    df <- labeling %>%
          plyr::rbind.fill() %>%
          dplyr::mutate(group=dplyr::if_else(id %in% intersecting, "Shared", variable)) %>%
          dplyr::mutate(group=as.factor(group))
    
    if (shared) {
        df %<>% dplyr::filter(group == "Shared")
    }

    if (symbols) {
        df$label <- networks[[1]]$get.symbols(df$id)
    } else {
        df$label <- df$id
    }

    if (log & reverse) {
        p <- p + ggplot2::scale_y_continuous(trans=pseudo.log.trans.rev())
    } else if (reverse) {
        p <- p + ggplot2::scale_y_reverse()
    } else if (log) {
        p <- p + ggplot2::scale_y_continuous(trans=pseudo.log.trans())
    }

    p + 
    ggrepel::geom_label_repel(data=df, 
                              aes(x=variable, y=value, label=label, color=group),
                              label.size=0.1,
                              size=size,
                              label.padding=0.15,
                              box.padding=0.1,
                              min.segment.length=0,
                              segment.alpha=0.25,
                              direction="both")
}

#' Hierarchical Clustering of Networks by Centrality
#' 
#' @param networks A named list of interactive omics network objects
#' @param metric Centrality measure
#' @param method Correlation method
#' 
#' @return A ggplot object
#' 
#' @importFrom dplyr select bind_cols
#' @importFrom tidyr drop_na
#' @importFrom factoextra fviz_dend
#' @importFrom magrittr %>% set_colnames
#' @importFrom ggplot2 labs
#' 
#' @export
networks.hclust <- function(networks, 
                            metric, 
                            method=c("pearson", "kendall")) {
    
    # Check for network object(s)
    is.networks(networks)

    # Default args
    method <- match.arg(method)

    df <- mapply(function(x, y) {
        dplyr::select(x$nodes, (!!metric)) %>%
        magrittr::set_colnames(y)
    }, networks, names(networks), SIMPLIFY=FALSE) %>%
    dplyr::bind_cols() %>%
    tidyr::drop_na()

    dd <- factoextra::get_dist(t(df), method=method)
    hc <- hclust(dd, method="ward.D2")
    factoextra::fviz_dend(hc, horiz=TRUE) +
    ggplot2::labs(title="") + 
    theme_simplex()
}

#' Differential Centrality Rankings Across Networks
#' 
#' @param networks A named list of interactive omics network objects
#' @param metric Centrality measure
#' @param p Scaling exponent
#' @param top Number of nodes to include
#' @param show Show rank or metric value
#' 
#' @return A ggplot object
#' 
#' @importFrom dplyr select mutate arrange pull desc
#' @importFrom magrittr %>% set_colnames
#' @importFrom ggplot2 ggplot aes geom_tile geom_text theme scale_fill_gradient labs theme_classic theme element_blank element_text element_rect geom_bar coord_flip
#' @importFrom ggpubr ggarrange
#' @importFrom reshape2 melt
#' @importFrom glue glue
#' 
#' @export
networks.diffc <- function(networks, metric, p=1, top=25, show=c("r", "v")) {
    show <- match.arg(show)
    data <-  mapply(function(x) {
        df <- dplyr::select(x$nodes, id, {{metric}}) %>%
            magrittr::set_colnames(c("n", "v")) %>%
            dplyr::mutate(r = rank(-v)) %>%
            dplyr::mutate(rsw = (nrow(.)-r+1)^p) %>%       
            dplyr::mutate(rswn = rsw/sum(rsw)) %>%
            dplyr::select(n, v, r, rswn) 
        
        rownames(df) <- df$n
        return(df)
    }, networks, SIMPLIFY=FALSE)
    
    # Compute difference between normalized ranks
    dfs.rswn <- lapply(data, function(x) dplyr::select(x, rswn))
    df.rswn <- do.call(cbind, dfs.rswn)
    colnames(df.rswn) <- names(dfs.rswn)
    
    df.s <- data.frame(avg=apply(df.rswn, 1, mean),
                       max=apply(df.rswn, 1, max),
                       min=apply(df.rswn, 1, min))
    
    df.s$mxabv <- df.s$max-df.s$avg
    df.s$mxblw <- df.s$avg-df.s$min
    df.s <- dplyr::select(df.s, mxabv, mxblw)
    df.delta <- data.frame(delta=apply(df.s, 1, max), node=rownames(df.s), row.names=rownames(df.s))
    
    # Grab nodes with largest differences
    nodes.ranked <- dplyr::arrange(df.delta, (desc(delta))) %>% dplyr::pull(node)
    
    # What values to what want to show
    dfs <- lapply(data, function(x) dplyr::select(x, {{show}}))
    df <- do.call(cbind, dfs)
    colnames(df) <- names(dfs)
    
    # Data for plotting
    df <- head(df[nodes.ranked,,drop=F], top)
    df.rswn <- head(df.rswn[nodes.ranked,,drop=F], top)
    df.delta <- head(df.delta[nodes.ranked,,drop=F], top)
    
    # Use normalized ranks for coloring
    df.rswn.s <- t(apply(df.rswn, 1, scale))
    colnames(df.rswn.s) <- colnames(df.rswn)
    
    # Paste in colors
    df$node <- rownames(df)
    df.m <- reshape2::melt(df)
    df.rswn.s.m <- reshape2::melt(df.rswn.s)
    colnames(df.rswn.s.m) <- c("v1","v2","rwsn_scaled")
    df.m.cbind <- cbind(df.m, df.rswn.s.m$rwsn_scaled)
    colnames(df.m.cbind) <- c("node", "variable", "value", "rwsn_scaled")
    df.m.cbind$node <- factor(df.m.cbind$node, levels=rev(nodes.ranked))
    
    p1 <- ggplot2::ggplot(df.m.cbind, mapping=aes(x=variable, y=node, fill=rwsn_scaled)) +
        ggplot2::geom_tile(color="#030303") +
        ggplot2::geom_text(aes(label=round(value, 0))) +
        ggplot2::theme(axis.text.x=ggplot2::element_blank()) +
        ggplot2::scale_fill_gradient(low="#555AE6", high="#FFFFFF") +
        ggplot2::labs(title="Differential Centrality", subtitle=glue("Ranked by {metric}"), x="") +
        ggplot2::theme_classic() +
        ggplot2::theme(axis.title.y=ggplot2::element_blank(),
                      axis.line.x=ggplot2::element_blank(),
                      axis.ticks.x=ggplot2::element_blank(),
                      axis.text.x=ggplot2::element_text(angle=45, hjust=1),
                      legend.position="none",
                      strip.background=ggplot2::element_rect(fill="#EEEEEE", color="#FFFFFF"))
    
    # Side bar for ranking
    df.delta.m <- reshape2::melt(df.delta)
    df.delta.m$node <- factor(df.delta.m$node, levels=rev(nodes.ranked))
    
    p2 <- df.delta.m %>%
        ggplot2::ggplot(aes(x=node, y=value)) +
        ggplot2::geom_bar(stat="identity", width=0.5, fill="#555AE6", color="#030303") +
        ggplot2::coord_flip() +
        ggplot2::theme_classic() +
        ggplot2::labs(title="", subtitle="DCS", y="") +
        ggplot2::theme(axis.text.x=ggplot2::element_blank(),
                       axis.line.x=ggplot2::element_blank(),
                       axis.ticks.x=ggplot2::element_blank(),
                       axis.title.y=ggplot2::element_blank(),
                       axis.text.y=ggplot2::element_blank(),
                       strip.background=ggplot2::element_rect(fill="#EEEEEE", color="#FFFFFF"))    
    
    ggpubr::ggarrange(p1, p2, align="h", widths=c(0.8, 0.2))
}
montilab/bieulergy documentation built on March 25, 2022, 3:40 p.m.