Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
Makefile
README.Rmd
README_files
^data-raw$
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
.Rproj.user
.DS_Store
.RData
.Rhistory
*.RData
*.Rhistory
29 changes: 27 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,36 @@ Description: What the package does (one paragraph).
Depends:
R (>= 3.5.0)
Imports:
aplot,
circlize,
dplyr,
enrichplot,
forcats,
ggalluvial,
ggfun,
ggplot2,
ggrepel,
ggvenn,
grDevices,
igraph,
magrittr,
MCL,
methods,
paletteer,
purrr,
RColorBrewer,
rlang,
tidyr
scales,
stats,
tibble,
tidyr,
utils,
yulab.utils
Suggests:
ComplexHeatmap,
testthat
License: Artistic-2.0
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
LazyData: true
115 changes: 115 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,26 +1,141 @@
# Generated by roxygen2: do not edit by hand

export(Addclusterscore)
export(PlotNodeHeatmap)
export(TCM_sankey)
export(compute_nodeinfo)
export(getGores)
export(getMCODE_res)
export(get_node_profile)
export(ggdock)
export(ggdot_sankey)
export(gglollipop)
export(ggvenn_plot)
export(gocircle_plot)
export(ppi_subset)
export(radar_plot)
export(rank_ppi_nodes)
export(runMCODE)
export(run_MCL)
export(run_louvain)
export(search_herb)
export(search_target)
import(ggplot2)
importFrom(MCL,mcl)
importFrom(RColorBrewer,brewer.pal)
importFrom(RColorBrewer,brewer.pal.info)
importFrom(aplot,plot_list)
importFrom(circlize,circos.axis)
importFrom(circlize,circos.clear)
importFrom(circlize,circos.genomicInitialize)
importFrom(circlize,circos.genomicRect)
importFrom(circlize,circos.genomicTrack)
importFrom(circlize,circos.genomicTrackPlotRegion)
importFrom(circlize,circos.par)
importFrom(circlize,circos.text)
importFrom(circlize,circos.track)
importFrom(circlize,colorRamp2)
importFrom(circlize,get.cell.meta.data)
importFrom(dplyr,"%>%")
importFrom(dplyr,all_of)
importFrom(dplyr,arrange)
importFrom(dplyr,bind_rows)
importFrom(dplyr,case_when)
importFrom(dplyr,count)
importFrom(dplyr,desc)
importFrom(dplyr,distinct)
importFrom(dplyr,filter)
importFrom(dplyr,group_by)
importFrom(dplyr,lead)
importFrom(dplyr,left_join)
importFrom(dplyr,mutate)
importFrom(dplyr,pull)
importFrom(dplyr,select)
importFrom(dplyr,setdiff)
importFrom(dplyr,slice)
importFrom(dplyr,slice_head)
importFrom(dplyr,summarise)
importFrom(dplyr,ungroup)
importFrom(enrichplot,dotplot)
importFrom(forcats,fct_reorder)
importFrom(ggalluvial,StatStratum)
importFrom(ggalluvial,geom_flow)
importFrom(ggalluvial,geom_stratum)
importFrom(ggalluvial,to_lodes_form)
importFrom(ggfun,get_legend)
importFrom(ggplot2,coord_equal)
importFrom(ggplot2,element_text)
importFrom(ggplot2,expansion)
importFrom(ggplot2,geom_polygon)
importFrom(ggplot2,geom_segment)
importFrom(ggplot2,geom_text)
importFrom(ggplot2,ggplot)
importFrom(ggplot2,ggtitle)
importFrom(ggplot2,margin)
importFrom(ggplot2,scale_x_continuous)
importFrom(ggplot2,theme)
importFrom(ggplot2,theme_void)
importFrom(ggrepel,geom_text_repel)
importFrom(ggvenn,ggvenn)
importFrom(grDevices,colorRampPalette)
importFrom(graphics,par)
importFrom(grid,gpar)
importFrom(grid,unit)
importFrom(igraph,"V<-")
importFrom(igraph,E)
importFrom(igraph,V)
importFrom(igraph,articulation_points)
importFrom(igraph,as_adj_list)
importFrom(igraph,as_adjacency_matrix)
importFrom(igraph,as_data_frame)
importFrom(igraph,as_undirected)
importFrom(igraph,betweenness)
importFrom(igraph,bfs)
importFrom(igraph,closeness)
importFrom(igraph,cluster_louvain)
importFrom(igraph,components)
importFrom(igraph,coreness)
importFrom(igraph,degree)
importFrom(igraph,delete_vertices)
importFrom(igraph,distances)
importFrom(igraph,eccentricity)
importFrom(igraph,ecount)
importFrom(igraph,edge_attr)
importFrom(igraph,edge_attr_names)
importFrom(igraph,edge_density)
importFrom(igraph,eigen_centrality)
importFrom(igraph,induced_subgraph)
importFrom(igraph,is_igraph)
importFrom(igraph,max_cliques)
importFrom(igraph,membership)
importFrom(igraph,neighbors)
importFrom(igraph,page_rank)
importFrom(igraph,simplify)
importFrom(igraph,strength)
importFrom(igraph,subgraph.edges)
importFrom(igraph,subgraph_from_edges)
importFrom(igraph,transitivity)
importFrom(igraph,vcount)
importFrom(igraph,vertex_attr)
importFrom(igraph,vertex_attr_names)
importFrom(magrittr,"%>%")
importFrom(methods,is)
importFrom(paletteer,scale_color_paletteer_c)
importFrom(paletteer,scale_fill_paletteer_c)
importFrom(purrr,map_df)
importFrom(rlang,.data)
importFrom(rlang,sym)
importFrom(scales,pretty_breaks)
importFrom(stats,median)
importFrom(stats,runif)
importFrom(stats,setNames)
importFrom(tibble,as_tibble)
importFrom(tibble,rownames_to_column)
importFrom(tibble,tibble)
importFrom(tidyr,drop_na)
importFrom(tidyr,pivot_longer)
importFrom(tidyr,separate_rows)
importFrom(utils,head)
importFrom(utils,setTxtProgressBar)
importFrom(utils,txtProgressBar)
importFrom(yulab.utils,str_wrap)
174 changes: 174 additions & 0 deletions R/PPI_cluster.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#' Compute MCL Clustering for PPI Network
#'
#' This function performs Markov Cluster Algorithm (MCL) on an igraph object
#' and assigns the cluster labels to the nodes.
#'
#' @param g An \code{igraph} object.
#' @param inflation Numeric. The inflation parameter controls the granularity of clusters.
#' Common values for PPI: 2.0 - 3.0.
#' Larger values (e.g., 3.0) produce smaller, tighter clusters.
#' Smaller values (e.g., 1.5) produce larger, coarser clusters.
#' Default is 2.5.
#' @param addLoops Logical; Self-loops with weight 1 are added to each vertex of g when TRUE; Default is TRUE (necessary).
#' @param ... Additional parameters in function `mcl()`.
#' @importFrom igraph as_adjacency_matrix V
#' @importFrom MCL mcl
#'
#' @return The input \code{igraph} object with a new vertex attribute \code{mcl_cluster}.
#' @examples
#' data(demo_ppi)
#' library(igraph)
#' ppi <- run_MCL(demo_ppi, inflation = 2.5)
#' head(V(ppi)$mcl_cluster)
#'
#' @export
run_MCL <- function(g, inflation = 2.5, addLoops = TRUE, ...) {

stopifnot(inherits(g, "igraph"))
message(sprintf("Running MCL with inflation = %.1f ...", inflation))

adj_mat <- igraph::as_adjacency_matrix(g, sparse = TRUE)

## MCL running
mcl_res <- MCL::mcl(x = adj_mat, addLoops = addLoops, inflation = inflation, ...)

igraph::V(g)$mcl_cluster <- as.factor(mcl_res$Cluster)
message(sprintf("Done! Identified %d modules (Iterations: %d).",
mcl_res$K, mcl_res$n.iterations))

return(g)
}


#' Compute Louvain Clustering for PPI Network
#'
#' This function performs Louvain clustering (multi-level modularity optimization)
#' on an igraph object. It is suitable for detecting larger, macro-scale functional
#' modules in PPI networks.
#'
#' @param g An \code{igraph} object.
#' @param resolution Numeric. The resolution parameter controls the granularity of clusters. Default is 1.0 (standard modularity).
#' @param weights Numeric vector or NULL. Edge weights to use for clustering.
#' If NULL (default), the function attempts to use the 'weight' or 'score' edge attribute.
#' Set to NA to perform unweighted clustering.
#'
#' @return The input \code{igraph} object with a new vertex attribute \code{louvain_cluster}.
#' @importFrom igraph cluster_louvain V edge_attr_names E membership
#'
#' @examples
#' data(demo_ppi)
#' library(igraph)
#' ppi <- run_louvain(demo_ppi, resolution = 1)
#' head(V(ppi)$louvain_cluster)
#'
#' @export
run_louvain <- function(g, resolution = 1.0, weights = NULL) {

stopifnot(inherits(g, "igraph"))

# PPI networks often have 'score' or 'weight'. Using them improves accuracy.
if (is.null(weights)) {
edge_attrs <- igraph::edge_attr_names(g)
if ("weight" %in% edge_attrs) {
weights <- igraph::E(g)$weight
message("Using edge attribute 'weight' for clustering.")
} else if ("score" %in% edge_attrs) {
weights <- igraph::E(g)$score
message("Using edge attribute 'score' for clustering.")
} else {
weights <- NULL # Unweighted
message("No weights found. Running unweighted clustering.")
}
} else if (identical(weights, NA)) {
weights <- NULL # Explicitly unweighted
}

message(sprintf("Running Louvain (Resolution: %.1f)...", resolution))

# Run Louvain Algorithm
louvain_res <- igraph::cluster_louvain(g, weights = weights, resolution = resolution)

# Assign Cluster Labels
igraph::V(g)$louvain_cluster <- as.factor(igraph::membership(louvain_res))
num_clusters <- length(unique(igraph::membership(louvain_res)))
modularity_score <- max(louvain_res$modularity, na.rm = TRUE)

message(sprintf("Done! Identified %d modules (Modularity: %.3f).",
num_clusters, modularity_score))

return(g)
}


#' Score and Rank Network Clusters
#'
#' This function evaluates the clusters/modules identified in the graph.
#' It calculates a score for each cluster based on density and size (MCODE style),
#' and returns a ranked data frame.
#'
#' @param g An \code{igraph} object. The graph must have a vertex attribute containing cluster labels.
#' @param cluster_attr Character. The name of the vertex attribute that stores cluster labels. Default is louvain_cluster.
#' @param min_size Integer. Clusters smaller than this size will be ignored. Default is 3.
#'
#' @return A data frame containing cluster statistics, ranked by Score.
#' @importFrom igraph vertex_attr vertex_attr_names induced_subgraph vcount edge_density V ecount
#' @importFrom utils head
#' @examples
#' data(demo_ppi)
#' ppi <- run_louvain(demo_ppi, resolution = 1)
#' louvain_score <- Addclusterscore(ppi, cluster_attr = "louvain_cluster", min_size = 3)
#' head(louvain_score)
#'
#' @export
Addclusterscore <- function(g, cluster_attr = "louvain_cluster", min_size = 3) {

stopifnot(inherits(g, "igraph"))

if (!cluster_attr %in% igraph::vertex_attr_names(g)) {
stop(paste0("Attribute '", cluster_attr, "' not found in graph. Please run clustering first."))
}

labels <- igraph::vertex_attr(g, cluster_attr)
unique_clusters <- unique(labels)
unique_clusters <- unique_clusters[!is.na(unique_clusters)]
message(paste("Evaluating", length(unique_clusters), "clusters based on attribute:", cluster_attr))

stats_list <- lapply(unique_clusters, function(cid) {
nodes_in_mod <- igraph::V(g)$name[labels == cid]
n_nodes <- length(nodes_in_mod)

if (n_nodes < min_size) return(NULL)
subg <- igraph::induced_subgraph(g, nodes_in_mod)

# Density = E / (N * (N-1) / 2)
dens <- igraph::edge_density(subg)

# Score = Density * N
score <- dens * n_nodes

data.frame(
Cluster_ID = as.character(cid),
Score = round(score, 3),
Nodes = n_nodes,
Edges = igraph::ecount(subg),
Density = round(dens, 3),
Gene_List = paste(utils::head(nodes_in_mod, 5), collapse = ", "),
Full_Genes = paste(nodes_in_mod, collapse = ","),
stringsAsFactors = FALSE
)
})

stats_list <- stats_list[!sapply(stats_list, is.null)]

if (length(stats_list) == 0) {
warning("No clusters passed the size filter.")
return(data.frame())
}

df_res <- do.call(rbind, stats_list)
df_res <- df_res[order(df_res$Score, decreasing = TRUE), ]
rownames(df_res) <- NULL

return(df_res)
}

Loading