diff --git a/.travis.yml b/.travis.yml index 40d280d8fd9..ca9cf7d2d75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ bioc_packages: r_github_packages: - hadley/lazyeval - igraph/igraphdata - - gaborcsardi/roxygen - gaborcsardi/pkgconfig r_packages: diff --git a/R/community.R b/R/community.R index e2efee23107..7fa2fadba85 100644 --- a/R/community.R +++ b/R/community.R @@ -1,7 +1,7 @@ # IGraph R package # Copyright (C) 2005-2012 Gabor Csardi # 334 Harvard street, Cambridge, MA 02139 USA -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or @@ -11,7 +11,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA @@ -30,26 +30,26 @@ #' igraph community detection functions return their results as an object from #' the \code{communities} class. This manual page describes the operations of #' this class. -#' +#' #' Community structure detection algorithms try to find dense subgraphs in #' directed or undirected graphs, by optimizing some criteria, and usually #' using heuristics. -#' +#' #' igraph implements a number of community detection methods (see them below), #' all of which return an object of the class \code{communities}. Because the #' community structure detection algorithms are different, \code{communities} #' objects do not always have the same structure. Nevertheless, they have some #' common operations, these are documented here. -#' +#' #' The \code{print} generic function is defined for \code{communities}, it #' prints a short summary. -#' +#' #' The \code{length} generic function call be called on \code{communities} and #' returns the number of communities. -#' +#' #' The \code{sizes} function returns the community sizes, in the order of their #' ids. -#' +#' #' \code{membership} gives the division of the vertices, into communities. It #' returns a numeric vector, one value for each vertex, the id of its #' community. Community ids start from one. Note that some algorithms calculate @@ -57,59 +57,59 @@ #' not just a single partitioning. For these algorithms typically the #' membership for the highest modularity value is returned, but see also the #' manual pages of the individual algorithms. -#' +#' #' \code{communities} is also the name of a function, that returns a list of #' communities, each identified by their vertices. The vertices will have #' symbolic names if the \code{add.vertex.names} igraph option is set, and the #' graph itself was named. Otherwise numeric vertex ids are used. -#' +#' #' \code{modularity} gives the modularity score of the partitioning. (See #' \code{\link{modularity.igraph}} for details. For algorithms that do not #' result a single partitioning, the highest modularity value is returned. -#' +#' #' \code{algorithm} gives the name of the algorithm that was used to calculate #' the community structure. -#' +#' #' \code{crossing} returns a logical vector, with one value for each edge, #' ordered according to the edge ids. The value is \code{TRUE} iff the edge #' connects two different communities, according to the (best) membership #' vector, as returned by \code{membership()}. -#' +#' #' \code{is_hierarchical} checks whether a hierarchical algorithm was used to #' find the community structure. Some functions only make sense for #' hierarchical methods (e.g. \code{merges}, \code{cut_at} and #' \code{as.dendrogram}). -#' +#' #' \code{merges} returns the merge matrix for hierarchical methods. An error #' message is given, if a non-hierarchical method was used to find the #' community structure. You can check this by calling \code{is_hierarchical} on #' the \code{communities} object. -#' +#' #' \code{cut_at} cuts the merge tree of a hierarchical community finding method, #' at the desired place and returns a membership vector. The desired place can #' be expressed as the desired number of communities or as the number of merge #' steps to make. The function gives an error message, if called with a #' non-hierarchical method. -#' +#' #' \code{as.dendrogram} converts a hierarchical community structure to a #' \code{dendrogram} object. It only works for hierarchical methods, and gives #' an error message to others. See \code{\link[stats]{dendrogram}} for details. -#' +#' #' \code{as.hclust} is similar to \code{as.dendrogram}, but converts a #' hierarchical community structure to a \code{hclust} object. -#' +#' #' \code{as_phylo} converts a hierarchical community structure to a \code{phylo} #' object, you will need the \code{ape} package for this. -#' +#' #' \code{show_trace} works (currently) only for communities found by the leading #' eigenvector method (\code{\link{cluster_leading_eigen}}), and #' returns a character vector that gives the steps performed by the algorithm #' while finding the communities. -#' +#' #' \code{code_len} is defined for the InfoMAP method #' (\code{\link{cluster_infomap}} and returns the code length of the #' partition. -#' +#' #' It is possibly to call the \code{plot} function on \code{communities} #' objects. This will plot the graph (and uses \code{\link{plot.igraph}} #' internally), with the communities shown. By default it colores the vertices @@ -117,7 +117,7 @@ #' corresponding to the communities. It passes additional arguments to #' \code{\link{plot.igraph}}, please see that and also #' \code{\link{igraph.plotting}} on how to change the plot. -#' +#' #' @rdname communities #' @aliases communities membership algorithm crossing cutat merges sizes cut_at #' is.hierarchical print.communities plot.communities length.communities @@ -164,60 +164,61 @@ #' (best) split is not available. #' @return \code{print} returns the \code{communities} object itself, #' invisibly. -#' +#' #' \code{length} returns an integer scalar. -#' +#' #' \code{sizes} returns a numeric vector. -#' +#' #' \code{membership} returns a numeric vector, one number for each vertex in #' the graph that was the input of the community detection. -#' +#' #' \code{modularity} returns a numeric scalar. -#' +#' #' \code{algorithm} returns a character scalar. -#' +#' #' \code{crossing} returns a logical vector. -#' +#' #' \code{is_hierarchical} returns a logical scalar. -#' +#' #' \code{merges} returns a two-column numeric matrix. -#' +#' #' \code{cut_at} returns a numeric vector, the membership vector of the #' vertices. -#' +#' #' \code{as.dendrogram} returns a \code{\link[stats]{dendrogram}} object. -#' +#' #' \code{show_trace} returns a character vector. -#' +#' #' \code{code_len} returns a numeric scalar for communities found with the #' InfoMAP method and \code{NULL} for other methods. -#' +#' #' \code{plot} for \code{communities} objects returns \code{NULL}, invisibly. -#' +#' #' #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @seealso See \code{\link{plot_dendrogram}} for plotting community structure #' dendrograms. -#' +#' #' See \code{\link{compare}} for comparing two community structures #' on the same graph. -#' +#' #' The different methods for finding communities, they all return a #' \code{communities} object: \code{\link{cluster_edge_betweenness}}, #' \code{\link{cluster_fast_greedy}}, #' \code{\link{cluster_label_prop}}, #' \code{\link{cluster_leading_eigen}}, -#' \code{\link{cluster_louvain}}, \code{\link{cluster_optimal}}, -#' \code{\link{cluster_spinglass}}, \code{\link{cluster_walktrap}}. +#' \code{\link{cluster_louvain}}, \code{\link{cluster_leiden}}, +#' \code{\link{cluster_optimal}}, \code{\link{cluster_spinglass}}, +#' \code{\link{cluster_walktrap}}. #' @keywords graphs #' @export #' @examples -#' +#' #' karate <- make_graph("Zachary") #' wc <- cluster_walktrap(karate) #' modularity(wc) #' membership(wc) #' plot(wc, karate) -#' +#' membership <- function(communities) { if (!is.null(communities$membership)) { @@ -357,13 +358,13 @@ modularity <- function(x, ...) UseMethod("modularity") #' Modularity of a community structure of a graph -#' +#' #' This function calculates how modular is a given division of a graph into #' subgraphs. -#' +#' #' \code{modularity} calculates the modularity of a graph with respect to the #' given \code{membership} vector. -#' +#' #' The modularity of a graph with respect to some division (or vertex types) #' measures how good the division is, or how separated are the different vertex #' types from each other. It defined as \deqn{Q=\frac{1}{2m} \sum_{i,j} @@ -375,11 +376,11 @@ modularity <- function(x, ...) #' \eqn{c_j}{cj} that of \eqn{j}, the sum goes over all \eqn{i} and \eqn{j} #' pairs of vertices, and \eqn{\delta(x,y)}{delta(x,y)} is 1 if \eqn{x=y} and 0 #' otherwise. -#' +#' #' If edge weights are given, then these are considered as the element of the #' \eqn{A} adjacency matrix, and \eqn{k_i}{ki} is the sum of weights of #' adjacent edges for vertex \eqn{i}. -#' +#' #' \code{modularity_matrix} calculates the modularity matrix. This is a dense matrix, #' and it is defined as the difference of the adjacency matrix and the #' configuration model null model matrix. In other words element @@ -397,13 +398,14 @@ modularity <- function(x, ...) #' @param \dots Additional arguments, none currently. #' @return For \code{modularity} a numeric scalar, the modularity score of the #' given configuration. -#' +#' #' For \code{modularity_matrix} a numeic square matrix, its order is the number of #' vertices in the graph. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @seealso \code{\link{cluster_walktrap}}, #' \code{\link{cluster_edge_betweenness}}, -#' \code{\link{cluster_fast_greedy}}, \code{\link{cluster_spinglass}} for +#' \code{\link{cluster_fast_greedy}}, \code{\link{cluster_spinglass}}, +#' \code{\link{cluster_louvain}} and \code{\link{cluster_leiden}} for #' various community detection methods. #' @references Clauset, A.; Newman, M. E. J. & Moore, C. Finding community #' structure in very large networks, \emph{Phyisical Review E} 2004, 70, 066111 @@ -411,13 +413,13 @@ modularity <- function(x, ...) #' @export #' @keywords graphs #' @examples -#' +#' #' g <- make_full_graph(5) %du% make_full_graph(5) %du% make_full_graph(5) #' g <- add_edges(g, c(1,6, 1,11, 6, 11)) #' wtc <- cluster_walktrap(g) #' modularity(wtc) #' modularity(g, membership(wtc)) -#' +#' modularity.igraph <- function(x, membership, weights=NULL, ...) { # Argument checks @@ -451,13 +453,13 @@ modularity_matrix <- function(graph, weights=NULL) { # Argument checks if (!is_igraph(graph)) { stop("Not a graph object") } - if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { - weights <- E(graph)$weight - } - if (!is.null(weights) && any(!is.na(weights))) { - weights <- as.numeric(weights) - } else { - weights <- NULL + if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight + } + if (!is.null(weights) && any(!is.na(weights))) { + weights <- as.numeric(weights) + } else { + weights <- NULL } on.exit( .Call(C_R_igraph_finalizer) ) @@ -554,7 +556,7 @@ complete.dend <- function(comm, use.modularity) { #' @importFrom stats as.dendrogram #' @method as.dendrogram communities #' @export - + as.dendrogram.communities <- function(object, hang=-1, use.modularity=FALSE, ...) { if (!is_hierarchical(object)) { @@ -572,9 +574,9 @@ as.dendrogram.communities <- function(object, hang=-1, use.modularity=FALSE, ## If multiple components, then we merge them in arbitrary order merges <- complete.dend(object, use.modularity) - + storage.mode(merges) <- "integer" - + if (is.null(object$names)) { object$names <- 1:(nrow(merges)+1) } @@ -644,7 +646,7 @@ as.dendrogram.communities <- function(object, hang=-1, use.modularity=FALSE, #' @importFrom stats as.hclust #' @method as.hclust communities #' @export - + as.hclust.communities <- function(x, hang=-1, use.modularity=FALSE, ...) { as.hclust(as.dendrogram(x, hang=hang, use.modularity=use.modularity)) @@ -703,7 +705,7 @@ as_phylo.communities <- function(x, use.modularity=FALSE, ...) { edge.length[k] <- height[i] } } - j <- j + 2L + j <- j + 2L } obj <- list(edge=edge, edge.length=edge.length/2, tip.label=labels, @@ -744,7 +746,7 @@ cut_at <- function(communities, no, steps) { no=noc } steps <- communities$vcount-no - community.to.membership2(mm, communities$vcount, steps) + community.to.membership2(mm, communities$vcount, steps) } } @@ -801,24 +803,24 @@ community.to.membership2 <- function(merges, vcount, steps) { #' Finding communities in graphs based on statistical meachanics -#' +#' #' This function tries to find communities in graphs via a spin-glass model and #' simulated annealing. -#' +#' #' This function tries to find communities in a graph. A community is a set of #' nodes with many edges inside the community and few edges between outside it #' (i.e. between the community itself and the rest of the graph.) -#' +#' #' This idea is reversed for edges having a negative weight, ie. few negative #' edges inside a community and many negative edges between communities. Note #' that only the \sQuote{neg} implementation supports negative edge weights. -#' +#' #' The \code{spinglass.cummunity} function can solve two problems related to #' community detection. If the \code{vertex} argument is not given (or it is #' \code{NULL}), then the regular community detection problem is solved #' (approximately), i.e. partitioning the vertices into communities, by #' optimizing the an energy function. -#' +#' #' If the \code{vertex} argument is given and it is not \code{NULL}, then it #' must be a vertex id, and the same energy function is used to find the #' community of the the given vertex. See also the examples below. @@ -880,7 +882,7 @@ community.to.membership2 <- function(merges, vcount, steps) { #' @return If the \code{vertex} argument is not given, ie. the first form is #' used then a \code{\link{cluster_spinglass}} returns a #' \code{\link{communities}} object. -#' +#' #' If the \code{vertex} argument is present, ie. the second form is used then a #' named list is returned with the following components: #' \item{community}{Numeric vector giving the ids of the vertices in the same @@ -891,29 +893,29 @@ community.to.membership2 <- function(merges, vcount, steps) { #' community of \code{vertex} and the rest of the graph. } #' @author Jorg Reichardt for the original code and Gabor Csardi #' \email{csardi.gabor@@gmail.com} for the igraph glue code. -#' +#' #' Changes to the original function for including the possibility of negative #' ties were implemented by Vincent Traag (\url{http://www.traag.net/}). #' @seealso \code{\link{communities}}, \code{\link{components}} #' @references J. Reichardt and S. Bornholdt: Statistical Mechanics of #' Community Detection, \emph{Phys. Rev. E}, 74, 016110 (2006), #' \url{http://arxiv.org/abs/cond-mat/0603718} -#' +#' #' M. E. J. Newman and M. Girvan: Finding and evaluating community structure in #' networks, \emph{Phys. Rev. E} 69, 026113 (2004) -#' +#' #' V.A. Traag and Jeroen Bruggeman: Community detection in networks with #' positive and negative links, \url{http://arxiv.org/abs/0811.2329} (2008). #' @export #' @keywords graphs #' @examples -#' +#' #' g <- sample_gnp(10, 5/10) %du% sample_gnp(9, 5/9) #' g <- add_edges(g, c(1, 12)) #' g <- induced_subgraph(g, subcomponent(g, 1)) #' cluster_spinglass(g, spins=2) #' cluster_spinglass(g, vertex=1) -#' +#' cluster_spinglass <- function(graph, weights=NULL, vertex=NULL, spins=25, parupdate=FALSE, start.temp=1, stop.temp=0.01, cool.fact=0.99, @@ -940,7 +942,7 @@ cluster_spinglass <- function(graph, weights=NULL, vertex=NULL, spins=25, "orig"=0, "neg"=1) on.exit( .Call(C_R_igraph_finalizer) ) - if (is.null(vertex)) { + if (is.null(vertex)) { res <- .Call(C_R_igraph_spinglass_community, graph, weights, as.numeric(spins), as.logical(parupdate), as.numeric(start.temp), @@ -956,21 +958,142 @@ cluster_spinglass <- function(graph, weights=NULL, vertex=NULL, spins=25, class(res) <- "communities" } else { res <- .Call(C_R_igraph_spinglass_my_community, graph, weights, - as.igraph.vs(graph, vertex)-1, as.numeric(spins), + as.igraph.vs(graph, vertex)-1, as.numeric(spins), as.numeric(update.rule), as.numeric(gamma)) res$community <- res$community + 1 } res } +#' Finding community structure of a graph using the Leiden algorithm of Traag, +#' van Eck & Waltman. +#' +#' @param objective_function Whether to use the Constant Potts Model (CPM) or +#' modularity. Must be either \code{"CPM"} or \code{"modularity"}. +#' @param weights Optional edge weights to be used. Can be a vector or an edge +#' attribute name. If the graph has a \code{weight} edge attribute, then this +#' is used by default. Supply \code{NA} here if the graph has a \code{weight} +#' edge attribute, but you want to ignore it. +#' @param resolution_parameter The resolution parameter to use. Higher +#' resolutions lead to more smaller communities, while lower resolutions lead +#' to fewer larger communities. +#' @param beta Parameter affecting the randomness in the Leiden algorithm. +#' This affects only the refinement step of the algorithm. +#' @param initial_membership If provided, the Leiden algorithm +#' will try to improve this provided membership. If no argument is +#' provided, the aglorithm simply starts from the singleton partition. +#' @param n_iterations the number of iterations to iterate the Leiden +#' algorithm. Each iteration may improve the partition further. +#' @param vertex_weights the vertex weights used in the Leiden algorithm. +#' If this is not provided, it will be automatically determined on the +#' basis of whether you want to use CPM or modularity. If you do provide +#' this, please make sure that you understand what you are doing. +#' @return \code{cluster_leiden} returns a \code{\link{communities}} +#' object, please see the \code{\link{communities}} manual page for details. +#' @author Vincent Traag +#' @seealso See \code{\link{communities}} for extracting the membership, +#' modularity scores, etc. from the results. +#' +#' Other community detection algorithms: \code{\link{cluster_walktrap}}, +#' \code{\link{cluster_spinglass}}, +#' \code{\link{cluster_leading_eigen}}, +#' \code{\link{cluster_edge_betweenness}}, +#' \code{\link{cluster_fast_greedy}}, +#' \code{\link{cluster_label_prop}} +#' \code{\link{cluster_louvain}} +#' @references Traag, V. A., Waltman, L., & van Eck, N. J. (2019). From Louvain +#' to Leiden: guaranteeing well-connected communities. Scientific +#' reports, 9(1), 5233. doi: 10.1038/s41598-019-41695-z +#' @export +#' @keywords graphs +#' @examples +#' g <- graph.famous("Zachary") +#' # By default CPM is used +#' g <- cluster_leiden(g, resolution_parameter=0.06) +#' +cluster_leiden <- function(graph, objective_function=c("CPM", "modularity"), + weights=NULL, resolution_parameter=1, beta=0.01, + initial_membership=NULL, n_iterations=2, vertex_weights=NULL) +{ + if (!is_igraph(graph)) { + stop("Not a graph object") + } + + # Parse objective function argument + objective_function <- igraph.match.arg(objective_function) + objective_function <- switch(objective_function, "cpm"=0, "modularity"=1) + + # Parse edge weights argument + if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight + } + if (!is.null(weights) && !any(is.na(weights))) { + weights <- as.numeric(weights) + } else { + weights <- NULL + } + # Parse initial_membership argument + if (!is.null(initial_membership) && !any(is.na(initial_membership))) { + initial_membership <- as.numeric(initial_membership) + } else { + initial_membership <- NULL + } + + # Parse node weights argument + if (!is.null(vertex_weights) && !any(is.na(vertex_weights))) { + vertex_weights <- as.numeric(vertex_weights) + if (objective_function == 1) { # Using modularity + warning("Providing node weights contradicts using modularity") + } + } else { + if (objective_function == 1) { # Using modularity + # Set correct node weights + vertex_weights <- strength(graph, weights=weights) + # Also correct resolution parameter + resolution_parameter <- resolution_parameter/sum(vertex_weights) + } + } + + on.exit( .Call(C_R_igraph_finalizer) ) + membership <- initial_membership + if (n_iterations > 0) { + for (i in 1:n_iterations) { + res <- .Call(C_R_igraph_community_leiden, graph, weights, + vertex_weights, as.numeric(resolution_parameter), + as.numeric(beta), !is.null(membership), + membership) + membership <- res$membership + } + } else { + prev_quality <- -Inf + quality <- 0.0 + while (prev_quality < quality) { + prev_quality <- quality; + res <- .Call(C_R_igraph_community_leiden, graph, weights, + vertex_weights, as.numeric(resolution_parameter), + as.numeric(beta), !is.null(membership), + membership) + membership <- res$membership + quality <- res$quality + } + } + res$algorithm <- "leiden" + res$vcount <- vcount(graph) + res$membership <- res$membership + 1 + if (igraph_opt("add.vertex.names") && is_named(graph)) { + res$names <- vertex_attr(graph, "name") + } + class(res) <- "communities" + res +} #' Community strucure via short random walks -#' +#' #' This function tries to find densely connected subgraphs, also called #' communities in a graph via random walks. The idea is that short random walks #' tend to stay in the same community. -#' +#' #' This function is the implementation of the Walktrap community finding #' algorithm, see Pascal Pons, Matthieu Latapy: Computing communities in large #' networks using random walks, http://arxiv.org/abs/physics/0512106 @@ -995,22 +1118,23 @@ cluster_spinglass <- function(graph, weights=NULL, vertex=NULL, spins=25, #' \email{csardi.gabor@@gmail.com} for the R and igraph interface #' @seealso See \code{\link{communities}} on getting the actual membership #' vector, merge matrix, modularity score, etc. -#' +#' #' \code{\link{modularity}} and \code{\link{cluster_fast_greedy}}, #' \code{\link{cluster_spinglass}}, #' \code{\link{cluster_leading_eigen}}, -#' \code{\link{cluster_edge_betweenness}} for other community detection +#' \code{\link{cluster_edge_betweenness}}, \code{\link{cluster_louvain}}, +#' and \code{\link{cluster_leiden}} for other community detection #' methods. #' @references Pascal Pons, Matthieu Latapy: Computing communities in large #' networks using random walks, http://arxiv.org/abs/physics/0512106 #' @export #' @keywords graphs #' @examples -#' +#' #' g <- make_full_graph(5) %du% make_full_graph(5) %du% make_full_graph(5) #' g <- add_edges(g, c(1,6, 1,11, 6, 11)) #' cluster_walktrap(g) -#' +#' cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, merges=TRUE, modularity=TRUE, membership=TRUE) { @@ -1021,7 +1145,7 @@ cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, if (membership && !modularity) { modularity <- TRUE } - + if (!is.null(weights)) { weights <- as.numeric(weights) } @@ -1044,10 +1168,10 @@ cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, #' Community structure detection based on edge betweenness -#' +#' #' Many networks consist of modules which are densely connected themselves but #' sparsely connected to other modules. -#' +#' #' The edge betweenness score of an edge measures the number of shortest paths #' through it, see \code{\link{edge_betweenness}} for details. The idea of the #' edge betweenness based community structure detection is that it is likely @@ -1057,15 +1181,15 @@ cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, #' get a hierarchical map, a rooted tree, called a dendrogram of the graph. The #' leafs of the tree are the individual vertices and the root of the tree #' represents the whole graph. -#' +#' #' \code{cluster_edge_betweenness} performs this algorithm by calculating the #' edge betweenness of the graph, removing the edge with the highest edge #' betweenness score, then recalculating edge betweenness of the edges and #' again removing the one with the highest score, etc. -#' +#' #' \code{edge.betweeness.community} returns various information collected #' throught the run of the algorithm. See the return value down here. -#' +#' #' @aliases edge.betweenness.community cluster_edge_betweenness #' @param graph The graph to analyze. #' @param weights The edge weights. Supply \code{NULL} to omit edge weights. By @@ -1104,7 +1228,7 @@ cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, #' \code{\link{cluster_fast_greedy}}, #' \code{\link{cluster_leading_eigen}} for other community detection #' methods. -#' +#' #' See \code{\link{communities}} for extracting the results of the community #' detection. #' @references M Newman and M Girvan: Finding and evaluating community @@ -1112,15 +1236,15 @@ cluster_walktrap <- function(graph, weights=E(graph)$weight, steps=4, #' @export #' @keywords graphs #' @examples -#' +#' #' g <- sample_pa(100, m = 2, directed = FALSE) #' eb <- cluster_edge_betweenness(g) -#' +#' #' g <- make_full_graph(10) %du% make_full_graph(10) #' g <- add_edges(g, c(1,11)) #' eb <- cluster_edge_betweenness(g) #' eb -#' +#' cluster_edge_betweenness <- function(graph, weights=E(graph)$weight, directed=TRUE, edge.betweenness=TRUE, @@ -1155,10 +1279,10 @@ cluster_edge_betweenness <- function(graph, weights=E(graph)$weight, } #' Community structure via greedy optimization of modularity -#' +#' #' This function tries to find dense subgraph, also called communities in #' graphs via directly optimizing a modularity score. -#' +#' #' This function implements the fast greedy modularity optimization algorithm #' for finding community structure, see A Clauset, MEJ Newman, C Moore: Finding #' community structure in very large networks, @@ -1182,23 +1306,24 @@ cluster_edge_betweenness <- function(graph, weights=E(graph)$weight, #' @author Tamas Nepusz \email{ntamas@@gmail.com} and Gabor Csardi #' \email{csardi.gabor@@gmail.com} for the R interface. #' @seealso \code{\link{communities}} for extracting the results. -#' +#' #' See also \code{\link{cluster_walktrap}}, #' \code{\link{cluster_spinglass}}, #' \code{\link{cluster_leading_eigen}} and -#' \code{\link{cluster_edge_betweenness}} for other methods. +#' \code{\link{cluster_edge_betweenness}}, \code{\link{cluster_louvain}} +#' \code{\link{cluster_leiden}} for other methods. #' @references A Clauset, MEJ Newman, C Moore: Finding community structure in #' very large networks, http://www.arxiv.org/abs/cond-mat/0408187 #' @export #' @keywords graphs #' @examples -#' +#' #' g <- make_full_graph(5) %du% make_full_graph(5) %du% make_full_graph(5) #' g <- add_edges(g, c(1,6, 1,11, 6, 11)) #' fc <- cluster_fast_greedy(g) #' membership(fc) #' sizes(fc) -#' +#' cluster_fast_greedy <- function(graph, merges=TRUE, modularity=TRUE, membership=TRUE, weights=E(graph)$weight) { @@ -1236,14 +1361,14 @@ igraph.i.levc.arp <- function(externalP, externalE) { #' Community structure detecting based on the leading eigenvector of the #' community matrix -#' +#' #' This function tries to find densely connected subgraphs in a graph by #' calculating the leading non-negative eigenvector of the modularity matrix of #' the graph. -#' +#' #' The function documented in these section implements the \sQuote{leading #' eigenvector} method developed by Mark Newman, see the reference below. -#' +#' #' The heart of the method is the definition of the modularity matrix, #' \code{B}, which is \code{B=A-P}, \code{A} being the adjacency matrix of the #' (undirected) network, and \code{P} contains the probability that certain @@ -1251,7 +1376,7 @@ igraph.i.levc.arp <- function(externalP, externalE) { #' words, a \code{P[i,j]} element of \code{P} is the probability that there is #' an edge between vertices \code{i} and \code{j} in a random network in which #' the degrees of all vertices are the same as in the input graph. -#' +#' #' The leading eigenvector method works by calculating the eigenvector of the #' modularity matrix for the largest positive eigenvalue and then separating #' vertices into two community based on the sign of the corresponding element @@ -1259,7 +1384,7 @@ igraph.i.levc.arp <- function(externalP, externalE) { #' that means that the network has no underlying comuunity structure. Check #' Newman's paper to understand why this is a good method for detecting #' community structure. -#' +#' #' @aliases leading.eigenvector.community #' @param graph The input graph. Should be undirected as the method needs a #' symmetric matrix. @@ -1314,14 +1439,14 @@ igraph.i.levc.arp <- function(externalP, externalE) { #' @export #' @keywords graphs #' @examples -#' +#' #' g <- make_full_graph(5) %du% make_full_graph(5) %du% make_full_graph(5) #' g <- add_edges(g, c(1,6, 1,11, 6, 11)) #' lec <- cluster_leading_eigen(g) #' lec -#' +#' #' cluster_leading_eigen(g, start=membership(lec)) -#' +#' cluster_leading_eigen <- function(graph, steps=-1, weights=NULL, start=NULL, options=arpack_defaults, @@ -1331,17 +1456,17 @@ cluster_leading_eigen <- function(graph, steps=-1, weights=NULL, # Argument checks if (!is_igraph(graph)) { stop("Not a graph object") } steps <- as.integer(steps) - if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { - weights <- E(graph)$weight - } - if (!is.null(weights) && any(!is.na(weights))) { - weights <- as.numeric(weights) - } else { - weights <- NULL + if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight + } + if (!is.null(weights) && any(!is.na(weights))) { + weights <- as.numeric(weights) + } else { + weights <- NULL } if (!is.null(start)) { start <- as.numeric(start)-1 } options.tmp <- arpack_defaults; options.tmp[ names(options) ] <- options ; options <- options.tmp - + on.exit( .Call(C_R_igraph_finalizer) ) # Function call res <- .Call(C_R_igraph_community_leading_eigenvector, graph, steps, @@ -1360,18 +1485,18 @@ cluster_leading_eigen <- function(graph, steps=-1, weights=NULL, } #' Finding communities based on propagating labels -#' +#' #' This is a fast, nearly linear time algorithm for detecting community #' structure in networks. In works by labeling the vertices with unique labels #' and then updating the labels by majority voting in the neighborhood of the #' vertex. -#' +#' #' This function implements the community detection method described in: #' Raghavan, U.N. and Albert, R. and Kumara, S.: Near linear time algorithm to #' detect community structures in large-scale networks. Phys Rev E 76, 036106. #' (2007). This version extends the original method by the ability to take edge #' weights into consideration and also by allowing some labels to be fixed. -#' +#' #' From the abstract of the paper: \dQuote{In our algorithm every node is #' initialized with a unique label and at every step each node adopts the label #' that most of its neighbors currently have. In this iterative process densely @@ -1398,31 +1523,32 @@ cluster_leading_eigen <- function(graph, steps=-1, weights=NULL, #' @author Tamas Nepusz \email{ntamas@@gmail.com} for the C implementation, #' Gabor Csardi \email{csardi.gabor@@gmail.com} for this manual page. #' @seealso \code{\link{communities}} for extracting the actual results. -#' -#' \code{\link{cluster_fast_greedy}}, \code{\link{cluster_walktrap}} and -#' \code{\link{cluster_spinglass}} for other community detection methods. +#' +#' \code{\link{cluster_fast_greedy}}, \code{\link{cluster_walktrap}}, +#' \code{\link{cluster_spinglass}}, \code{\link{cluster_louvain}} and +#' \code{\link{cluster_leiden}} for other community detection methods. #' @references Raghavan, U.N. and Albert, R. and Kumara, S.: Near linear time #' algorithm to detect community structures in large-scale networks. \emph{Phys #' Rev E} 76, 036106. (2007) #' @export #' @keywords graphs #' @examples -#' +#' #' g <- sample_gnp(10, 5/10) %du% sample_gnp(9, 5/9) #' g <- add_edges(g, c(1, 12)) #' cluster_label_prop(g) -#' +#' cluster_label_prop <- function(graph, weights=NULL, initial=NULL, fixed=NULL) { # Argument checks if (!is_igraph(graph)) { stop("Not a graph object") } - if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { - weights <- E(graph)$weight - } - if (!is.null(weights) && any(!is.na(weights))) { - weights <- as.numeric(weights) - } else { - weights <- NULL + if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight + } + if (!is.null(weights) && any(!is.na(weights))) { + weights <- as.numeric(weights) + } else { + weights <- NULL } if (!is.null(initial)) initial <- as.numeric(initial) if (!is.null(fixed)) fixed <- as.logical(fixed) @@ -1443,16 +1569,16 @@ cluster_label_prop <- function(graph, weights=NULL, initial=NULL, #' Finding community structure by multi-level optimization of modularity -#' +#' #' This function implements the multi-level modularity optimization algorithm #' for finding community structure, see references below. It is based on the #' modularity measure and a hierarchial approach. -#' +#' #' This function implements the multi-level modularity optimization algorithm #' for finding community structure, see VD Blondel, J-L Guillaume, R Lambiotte #' and E Lefebvre: Fast unfolding of community hierarchies in large networks, #' \url{http://arxiv.org/abs/arXiv:0803.0476} for the details. -#' +#' #' It is based on the modularity measure and a hierarchial approach. #' Initially, each vertex is assigned to a community on its own. In every step, #' vertices are re-assigned to communities in a local, greedy way: each vertex @@ -1461,7 +1587,7 @@ cluster_label_prop <- function(graph, weights=NULL, initial=NULL, #' a vertex on its own, and the process starts again with the merged #' communities. The process stops when there is only a single vertex left or #' when the modularity cannot be increased any more in a step. -#' +#' #' This function was contributed by Tom Gregorovic. #' #' @aliases multilevel.community @@ -1475,35 +1601,36 @@ cluster_label_prop <- function(graph, weights=NULL, initial=NULL, #' @author Tom Gregorovic, Tamas Nepusz \email{ntamas@@gmail.com} #' @seealso See \code{\link{communities}} for extracting the membership, #' modularity scores, etc. from the results. -#' +#' #' Other community detection algorithms: \code{\link{cluster_walktrap}}, #' \code{\link{cluster_spinglass}}, #' \code{\link{cluster_leading_eigen}}, #' \code{\link{cluster_edge_betweenness}}, #' \code{\link{cluster_fast_greedy}}, #' \code{\link{cluster_label_prop}} +#' \code{\link{cluster_leiden}} #' @references Vincent D. Blondel, Jean-Loup Guillaume, Renaud Lambiotte, #' Etienne Lefebvre: Fast unfolding of communities in large networks. J. Stat. #' Mech. (2008) P10008 #' @export #' @keywords graphs #' @examples -#' +#' #' # This is so simple that we will have only one level #' g <- make_full_graph(5) %du% make_full_graph(5) %du% make_full_graph(5) #' g <- add_edges(g, c(1,6, 1,11, 6, 11)) #' cluster_louvain(g) -#' +#' cluster_louvain <- function(graph, weights=NULL) { # Argument checks if (!is_igraph(graph)) { stop("Not a graph object") } - if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { - weights <- E(graph)$weight - } - if (!is.null(weights) && any(!is.na(weights))) { - weights <- as.numeric(weights) - } else { - weights <- NULL + if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { + weights <- E(graph)$weight + } + if (!is.null(weights) && any(!is.na(weights))) { + weights <- as.numeric(weights) + } else { + weights <- NULL } on.exit( .Call(C_R_igraph_finalizer) ) @@ -1523,17 +1650,17 @@ cluster_louvain <- function(graph, weights=NULL) { #' Optimal community structure -#' +#' #' This function calculates the optimal community structure of a graph, by #' maximizing the modularity measure over all possible partitions. -#' +#' #' This function calculates the optimal community structure for a graph, in #' terms of maximal modularity score. -#' +#' #' The calculation is done by transforming the modularity maximization into an #' integer programming problem, and then calling the GLPK library to solve #' that. Please the reference below for details. -#' +#' #' Note that modularity optimization is an NP-complete problem, and all known #' algorithms for it have exponential time complexity. This means that you #' probably don't want to run this function on larger graphs. Graphs with up to @@ -1542,20 +1669,20 @@ cluster_louvain <- function(graph, weights=NULL) { #' #' @section Examples: #' \preformatted{ -#' +#' #' ## Zachary's karate club #' g <- make_graph("Zachary") -#' -#' ## We put everything into a big 'try' block, in case +#' +#' ## We put everything into a big 'try' block, in case #' ## igraph was compiled without GLPK support -#' +#' #' ## The calculation only takes a couple of seconds #' oc <- cluster_optimal(g) -#' +#' #' ## Double check the result #' print(modularity(oc)) #' print(modularity(g, membership(oc))) -#' +#' #' ## Compare to the greedy optimizer #' fc <- cluster_fast_greedy(g) #' print(modularity(fc)) @@ -1609,10 +1736,10 @@ cluster_optimal <- function(graph, weights=NULL) { #' Infomap community finding -#' +#' #' Find community structure that minimizes the expected description length of a #' random walker trajectory -#' +#' #' Please see the details of this method in the references given below. #' #' @aliases infomap.community @@ -1642,7 +1769,7 @@ cluster_optimal <- function(graph, weights=NULL) { #' information flow reveal community structure in complex networks, \emph{PNAS} #' 105, 1118 (2008) \url{http://dx.doi.org/10.1073/pnas.0706851105}, #' \url{http://arxiv.org/abs/0707.0609} -#' +#' #' A more detailed paper: M. Rosvall, D. Axelsson, and C. T. Bergstrom, The map #' equation, \emph{Eur. Phys. J. Special Topics} 178, 13 (2009). #' \url{http://dx.doi.org/10.1140/epjst/e2010-01179-1}, @@ -1650,37 +1777,37 @@ cluster_optimal <- function(graph, weights=NULL) { #' @export #' @keywords graphs #' @examples -#' +#' #' ## Zachary's karate club #' g <- make_graph("Zachary") -#' +#' #' imc <- cluster_infomap(g) #' membership(imc) #' communities(imc) -#' +#' cluster_infomap <- function(graph, e.weights=NULL, v.weights=NULL, nb.trials=10, modularity=TRUE) { - + # Argument checks if (!is_igraph(graph)) { stop("Not a graph object") } - if (is.null(e.weights) && "weight" %in% edge_attr_names(graph)) { - e.weights <- E(graph)$weight - } - if (!is.null(e.weights) && any(!is.na(e.weights))) { - e.weights <- as.numeric(e.weights) - } else { - e.weights <- NULL - } - if (is.null(v.weights) && "weight" %in% vertex_attr_names(graph)) { - v.weights <- V(graph)$weight - } - if (!is.null(v.weights) && any(!is.na(v.weights))) { - v.weights <- as.numeric(v.weights) - } else { - v.weights <- NULL + if (is.null(e.weights) && "weight" %in% edge_attr_names(graph)) { + e.weights <- E(graph)$weight + } + if (!is.null(e.weights) && any(!is.na(e.weights))) { + e.weights <- as.numeric(e.weights) + } else { + e.weights <- NULL + } + if (is.null(v.weights) && "weight" %in% vertex_attr_names(graph)) { + v.weights <- V(graph)$weight + } + if (!is.null(v.weights) && any(!is.na(v.weights))) { + v.weights <- as.numeric(v.weights) + } else { + v.weights <- NULL } nb.trials <- as.integer(nb.trials) - + on.exit( .Call(C_R_igraph_finalizer) ) # Function call res <- .Call(C_R_igraph_community_infomap, graph, e.weights, @@ -1712,7 +1839,7 @@ plot.communities <- function(x, y, plot(y, vertex.color=col, mark.groups=mark.groups, edge.color=edge.color, - ...) + ...) } @@ -1727,9 +1854,9 @@ plot_dendrogram <- function(x, mode=igraph_opt("dend.plot.type"), ...) #' Community structure dendrogram plots -#' +#' #' Plot a hierarchical community structure as a dendrogram. -#' +#' #' \code{plot_dendrogram} supports three different plotting functions, selected via #' the \code{mode} argument. By default the plotting function is taken from the #' \code{dend.plot.type} igraph option, and it has for possible values: @@ -1740,7 +1867,7 @@ plot_dendrogram <- function(x, mode=igraph_opt("dend.plot.type"), ...) #' package. \item \code{hclust} Use \code{plot.hclust} from the \code{stats} #' package. \item \code{dendrogram} Use \code{plot.dendrogram} from the #' \code{stats} package. } -#' +#' #' The different plotting functions take different sets of arguments. When #' using \code{plot.phylo} (\code{mode="phylo"}), we have the following syntax: #' \preformatted{ @@ -1753,7 +1880,7 @@ plot_dendrogram <- function(x, mode=igraph_opt("dend.plot.type"), ...) #' \item \code{use.edge.length} Passed to \code{plot.phylo}. #' \item \code{dots} Attitional arguments to pass to \code{plot.phylo}. #' } -#' +#' #' The syntax for \code{plot.hclust} (\code{mode="hclust"}): \preformatted{ #' plot_dendrogram(x, mode="hclust", rect = 0, colbar = palette(), #' hang = 0.01, ann = FALSE, main = "", sub = "", xlab = "", @@ -1779,12 +1906,12 @@ plot_dendrogram <- function(x, mode=igraph_opt("dend.plot.type"), ...) #' \code{plot.hclust}. #' \item \code{dots} Attitional arguments to pass to \code{plot.hclust}. #' } -#' +#' #' The syntax for \code{plot.dendrogram} (\code{mode="dendrogram"}): #' \preformatted{ #' plot_dendrogram(x, \dots) #' } The extra arguments are simply passed to \code{as.dendrogram}. -#' +#' #' @param x An object containing the community structure of a graph. See #' \code{\link{communities}} for details. #' @param mode Which dendrogram plotting function to use. See details below. @@ -1800,12 +1927,12 @@ plot_dendrogram <- function(x, mode=igraph_opt("dend.plot.type"), ...) #' @export #' @keywords graphs #' @examples -#' +#' #' karate <- make_graph("Zachary") #' fc <- cluster_fast_greedy(karate) #' plot_dendrogram(fc) -#' -plot_dendrogram.communities <- function(x, +#' +plot_dendrogram.communities <- function(x, mode=igraph_opt("dend.plot.type"), ..., use.modularity=FALSE, palette=categorical_pal(8)) { @@ -1818,7 +1945,7 @@ plot_dendrogram.communities <- function(x, have_ape <- requireNamespace("ape", quietly = TRUE) mode <- if (have_ape) "phylo" else "hclust" } - + if (mode=="hclust") { dendPlotHclust(x, use.modularity=use.modularity, ...) } else if (mode=="dendrogram") { @@ -1859,14 +1986,14 @@ dendPlotDendrogram <- function(communities, hang=-1, ..., dendPlotPhylo <- function(communities, colbar=palette(), col=colbar[membership(communities)], mark.groups=communities(communities), - use.modularity=FALSE, + use.modularity=FALSE, edge.color="#AAAAAAFF", edge.lty=c(1,2), ...) { - + phy <- as_phylo(communities, use.modularity=use.modularity) getedges <- function(tip) { - repeat { + repeat { ee <- which(! phy$edge[,1] %in% tip & phy$edge[,2] %in% tip) if (length(ee)<=1) { break } tip <- c(tip, unique(phy$edge[ee,1])) @@ -1886,18 +2013,18 @@ dendPlotPhylo <- function(communities, colbar=palette(), } else { ecol <- edge.color } - + elty <- rep(edge.lty[2], nrow(phy$edge)) elty[ unlist(gredges) ] <- edge.lty[1] - + plot(phy, edge.color=ecol, edge.lty=elty, tip.color=col, ...) } #' Compares community structures using various metrics -#' +#' #' This function assesses the distance between two community structures. -#' -#' +#' +#' #' @aliases compare.communities compare.membership compare #' @param comm1 A \code{\link{communities}} object containing a community #' structure; or a numeric vector, the membership vector of the first community @@ -1915,38 +2042,43 @@ dendPlotPhylo <- function(communities, colbar=palette(), #' (1985). #' @return A real number. #' @author Tamas Nepusz \email{ntamas@@gmail.com} -#' @seealso \code{\link{cluster_walktrap}}, +#' @seealso See \code{\link{cluster_walktrap}}, +#' \code{\link{cluster_spinglass}}, +#' \code{\link{cluster_leading_eigen}}, #' \code{\link{cluster_edge_betweenness}}, -#' \code{\link{cluster_fast_greedy}}, \code{\link{cluster_spinglass}} for -#' various community detection methods. +#' \code{\link{cluster_fast_greedy}}, +#' \code{\link{cluster_label_prop}} +#' \code{\link{cluster_louvain}} +#' \code{\link{cluster_leiden}} +#' for various community detection methods. #' @references Meila M: Comparing clusterings by the variation of information. #' In: Scholkopf B, Warmuth MK (eds.). \emph{Learning Theory and Kernel #' Machines: 16th Annual Conference on Computational Learning Theory and 7th #' Kernel Workshop}, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in #' Computer Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. -#' +#' #' Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure #' identification. \emph{J Stat Mech} P09008, 2005. -#' +#' #' van Dongen S: Performance criteria for graph clustering and Markov cluster #' experiments. Technical Report INS-R0012, National Research Institute for #' Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. -#' +#' #' Rand WM: Objective criteria for the evaluation of clustering methods. #' \emph{J Am Stat Assoc} 66(336):846-850, 1971. -#' +#' #' Hubert L and Arabie P: Comparing partitions. \emph{Journal of #' Classification} 2:193-218, 1985. #' @export #' @keywords graphs #' @examples -#' +#' #' g <- make_graph("Zachary") #' sg <- cluster_spinglass(g) #' le <- cluster_leading_eigen(g) #' compare(sg, le, method="rand") #' compare(membership(sg), membership(le)) -#' +#' compare <- function(comm1, comm2, method=c("vi", "nmi", "split.join", "rand", "adjusted.rand")) @@ -1990,11 +2122,11 @@ i_compare <- function (comm1, comm2, method=c("vi", "nmi", "split.join", } else { as.numeric(comm2) } - method <- switch(igraph.match.arg(method), vi = 0, nmi = 1, + method <- switch(igraph.match.arg(method), vi = 0, nmi = 1, split.join = 2, rand = 3, adjusted.rand = 4) on.exit(.Call(C_R_igraph_finalizer) ) res <- .Call(C_R_igraph_compare_communities, comm1, comm2, method) - res + res } #' Split-join distance of two community structures @@ -2047,16 +2179,16 @@ split_join_distance <- function(comm1, comm2) { } #' Groups of a vertex partitioning -#' +#' #' Create a list of vertex groups from some graph clustering or community #' structure. -#' +#' #' Currently two methods are defined for this function. The default method #' works on the output of \code{\link{components}}. (In fact it works on any #' object that is a list with an entry called \code{membership}.) -#' +#' #' The second method works on \code{\link{communities}} objects. -#' +#' #' @aliases groups groups.default groups.communities #' @param x Some object that represents a grouping of the vertices. See details #' below. @@ -2069,7 +2201,7 @@ split_join_distance <- function(comm1, comm2) { #' g <- make_graph("Zachary") #' fgc <- cluster_fast_greedy(g) #' groups(fgc) -#' +#' #' g2 <- make_ring(10) + make_full_graph(5) #' groups(components(g2)) #' @export @@ -2114,14 +2246,14 @@ communities <- groups.communities #' Contract several vertices into a single one -#' +#' #' This function creates a new graph, by merging several vertices into one. The #' vertices in the new graph correspond to sets of vertices in the input graph. -#' +#' #' The attributes of the graph are kept. Graph and edge attributes are #' unchanged, vertex attributes are combined, according to the #' \code{vertex.attr.comb} parameter. -#' +#' #' @aliases contract.vertices contract #' @param graph The input graph, it can be directed or undirected. #' @param mapping A numeric vector that specifies the mapping. Its elements @@ -2133,19 +2265,19 @@ communities <- groups.communities #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @keywords graphs #' @examples -#' +#' #' g <- make_ring(10) #' g$name <- "Ring" #' V(g)$name <- letters[1:vcount(g)] #' E(g)$weight <- runif(ecount(g)) -#' +#' #' g2 <- contract(g, rep(1:5, each=2), #' vertex.attr.comb=toString) -#' +#' #' ## graph and edge attributes are kept, vertex attributes are #' ## combined using the 'toString' function. #' print(g2, g=TRUE, v=TRUE, e=TRUE) -#' +#' #' @export contract <- contract diff --git a/cigraph b/cigraph index 5886f216f71..0bd3384cca5 160000 --- a/cigraph +++ b/cigraph @@ -1 +1 @@ -Subproject commit 5886f216f715e83cce891fbaa600c4a39f5c26f0 +Subproject commit 0bd3384cca577a94fa4c67d27bd7cdde68e6fde6 diff --git a/tests/testthat/test_leiden.R b/tests/testthat/test_leiden.R new file mode 100644 index 00000000000..6b59d159891 --- /dev/null +++ b/tests/testthat/test_leiden.R @@ -0,0 +1,36 @@ + +context("cluster_leiden") + +test_that("cluster_leiden works", { + + library(igraph) + set.seed(42) + + g <- make_graph("Zachary") + mc <- cluster_leiden(g, resolution_parameter=0.06) + + expect_that(as.vector(membership(mc)), + equals(c(1,1,1,1,1,1,1,1,2,1,1,1,1,1,2,2,1, + 1,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2))) + expect_that(length(mc), equals(2)) + expect_that(sizes(mc), + equals(structure(c(17L, 17L), .Dim = 2L, + .Dimnames = structure(list(`Community sizes` = c("1", "2")), + .Names = "Community sizes"), + class = "table") + )) + + set.seed(42) + mc <- cluster_leiden(g, "modularity") + + expect_that(as.vector(membership(mc)), + equals(c(1,1,1,1,2,2,2,1,3,3,2,1,1,1,3,3,2, + 1,3,1,3,1,3,4,4,4,3,4,4,3,3,4,3,3))) + expect_that(length(mc), equals(4)) + expect_that(sizes(mc), + equals(structure(c(11L, 5L, 12L, 6L), .Dim = 4L, + .Dimnames = structure(list(`Community sizes` = c("1", "2", "3", "4")), + .Names = "Community sizes"), + class = "table") + )) +}) diff --git a/tools/stimulus/init.c b/tools/stimulus/init.c index 2eb3d6203b0..c4e5a9b7d1b 100644 --- a/tools/stimulus/init.c +++ b/tools/stimulus/init.c @@ -11,7 +11,7 @@ Most likely possible values need to be added below. */ -/* FIXME: +/* FIXME: Check these declarations against the C/Fortran source code. */ @@ -88,6 +88,7 @@ extern SEXP R_igraph_community_label_propagation(SEXP, SEXP, SEXP, SEXP); extern SEXP R_igraph_community_leading_eigenvector(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP R_igraph_community_multilevel(SEXP, SEXP); extern SEXP R_igraph_community_optimal_modularity(SEXP, SEXP); +extern SEXP R_igraph_community_leiden(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP R_igraph_community_to_membership2(SEXP, SEXP, SEXP); extern SEXP R_igraph_compare_communities(SEXP, SEXP, SEXP); extern SEXP R_igraph_complementer(SEXP, SEXP); @@ -450,6 +451,7 @@ static const R_CallMethodDef CallEntries[] = { {"R_igraph_community_leading_eigenvector", (DL_FUNC) &R_igraph_community_leading_eigenvector, 9}, {"R_igraph_community_multilevel", (DL_FUNC) &R_igraph_community_multilevel, 2}, {"R_igraph_community_optimal_modularity", (DL_FUNC) &R_igraph_community_optimal_modularity, 2}, + {"R_igraph_community_leiden", (DL_FUNC) &R_igraph_community_leiden, 7}, {"R_igraph_community_to_membership2", (DL_FUNC) &R_igraph_community_to_membership2, 3}, {"R_igraph_compare_communities", (DL_FUNC) &R_igraph_compare_communities, 3}, {"R_igraph_complementer", (DL_FUNC) &R_igraph_complementer, 2},