162 lines
6.8 KiB
Python
162 lines
6.8 KiB
Python
"""Functions for detecting communities based on Leiden Community Detection
|
||
algorithm.
|
||
|
||
These functions do not have NetworkX implementations.
|
||
They may only be run with an installable :doc:`backend </backends>`
|
||
that supports them.
|
||
"""
|
||
|
||
import itertools
|
||
from collections import deque
|
||
|
||
import networkx as nx
|
||
from networkx.utils import not_implemented_for, py_random_state
|
||
|
||
__all__ = ["leiden_communities", "leiden_partitions"]
|
||
|
||
|
||
@not_implemented_for("directed")
|
||
@py_random_state("seed")
|
||
@nx._dispatchable(edge_attrs="weight", implemented_by_nx=False)
|
||
def leiden_communities(G, weight="weight", resolution=1, max_level=None, seed=None):
|
||
r"""Find a best partition of `G` using Leiden Community Detection (backend required)
|
||
|
||
Leiden Community Detection is an algorithm to extract the community structure
|
||
of a network based on modularity optimization. It is an improvement upon the
|
||
Louvain Community Detection algorithm. See :any:`louvain_communities`.
|
||
|
||
Unlike the Louvain algorithm, it guarantees that communities are well connected in addition
|
||
to being faster and uncovering better partitions. [1]_
|
||
|
||
The algorithm works in 3 phases. On the first phase, it adds the nodes to a queue randomly
|
||
and assigns every node to be in its own community. For each node it tries to find the
|
||
maximum positive modularity gain by moving each node to all of its neighbor communities.
|
||
If a node is moved from its community, it adds to the rear of the queue all neighbors of
|
||
the node that do not belong to the node’s new community and that are not in the queue.
|
||
|
||
The first phase continues until the queue is empty.
|
||
|
||
The second phase consists in refining the partition $P$ obtained from the first phase. It starts
|
||
with a singleton partition $P_{refined}$. Then it merges nodes locally in $P_{refined}$ within
|
||
each community of the partition $P$. Nodes are merged with a community in $P_{refined}$ only if
|
||
both are sufficiently well connected to their community in $P$. This means that after the
|
||
refinement phase is concluded, communities in $P$ sometimes will have been split into multiple
|
||
communities.
|
||
|
||
The third phase consists of aggregating the network by building a new network whose nodes are
|
||
now the communities found in the second phase. However, the non-refined partition is used to create
|
||
an initial partition for the aggregate network.
|
||
|
||
Once this phase is complete it is possible to reapply the first and second phases creating bigger
|
||
communities with increased modularity.
|
||
|
||
The above three phases are executed until no modularity gain is achieved or `max_level` number
|
||
of iterations have been performed.
|
||
|
||
Parameters
|
||
----------
|
||
G : NetworkX graph
|
||
weight : string or None, optional (default="weight")
|
||
The name of an edge attribute that holds the numerical value
|
||
used as a weight. If None then each edge has weight 1.
|
||
resolution : float, optional (default=1)
|
||
If resolution is less than 1, the algorithm favors larger communities.
|
||
Greater than 1 favors smaller communities.
|
||
max_level : int or None, optional (default=None)
|
||
The maximum number of levels (steps of the algorithm) to compute.
|
||
Must be a positive integer or None. If None, then there is no max
|
||
level and the algorithm will run until converged.
|
||
seed : integer, random_state, or None (default)
|
||
Indicator of random number generation state.
|
||
See :ref:`Randomness<randomness>`.
|
||
|
||
Returns
|
||
-------
|
||
list
|
||
A list of disjoint sets (partition of `G`). Each set represents one community.
|
||
All communities together contain all the nodes in `G`.
|
||
|
||
Examples
|
||
--------
|
||
>>> import networkx as nx
|
||
>>> G = nx.petersen_graph()
|
||
>>> nx.community.leiden_communities(G, backend="example_backend") # doctest: +SKIP
|
||
[{2, 3, 5, 7, 8}, {0, 1, 4, 6, 9}]
|
||
|
||
Notes
|
||
-----
|
||
The order in which the nodes are considered can affect the final output. In the algorithm
|
||
the ordering happens using a random shuffle.
|
||
|
||
References
|
||
----------
|
||
.. [1] Traag, V.A., Waltman, L. & van Eck, N.J. From Leiden to Leiden: guaranteeing
|
||
well-connected communities. Sci Rep 9, 5233 (2019). https://doi.org/10.1038/s41598-019-41695-z
|
||
|
||
See Also
|
||
--------
|
||
leiden_partitions
|
||
:any:`louvain_communities`
|
||
"""
|
||
partitions = leiden_partitions(G, weight, resolution, seed)
|
||
if max_level is not None:
|
||
if max_level <= 0:
|
||
raise ValueError("max_level argument must be a positive integer or None")
|
||
partitions = itertools.islice(partitions, max_level)
|
||
final_partition = deque(partitions, maxlen=1)
|
||
return final_partition.pop()
|
||
|
||
|
||
@not_implemented_for("directed")
|
||
@py_random_state("seed")
|
||
@nx._dispatchable(edge_attrs="weight", implemented_by_nx=False)
|
||
def leiden_partitions(G, weight="weight", resolution=1, seed=None):
|
||
"""Yield partitions for each level of Leiden Community Detection (backend required)
|
||
|
||
Leiden Community Detection is an algorithm to extract the community
|
||
structure of a network based on modularity optimization.
|
||
|
||
The partitions across levels (steps of the algorithm) form a dendrogram
|
||
of communities. A dendrogram is a diagram representing a tree and each
|
||
level represents a partition of the G graph. The top level contains the
|
||
smallest communities and as you traverse to the bottom of the tree the
|
||
communities get bigger and the overall modularity increases making
|
||
the partition better.
|
||
|
||
Each level is generated by executing the three phases of the Leiden Community
|
||
Detection algorithm. See :any:`leiden_communities`.
|
||
|
||
Parameters
|
||
----------
|
||
G : NetworkX graph
|
||
weight : string or None, optional (default="weight")
|
||
The name of an edge attribute that holds the numerical value
|
||
used as a weight. If None then each edge has weight 1.
|
||
resolution : float, optional (default=1)
|
||
If resolution is less than 1, the algorithm favors larger communities.
|
||
Greater than 1 favors smaller communities.
|
||
seed : integer, random_state, or None (default)
|
||
Indicator of random number generation state.
|
||
See :ref:`Randomness<randomness>`.
|
||
|
||
Yields
|
||
------
|
||
list
|
||
A list of disjoint sets (partition of `G`). Each set represents one community.
|
||
All communities together contain all the nodes in `G`. The yielded partitions
|
||
increase modularity with each iteration.
|
||
|
||
References
|
||
----------
|
||
.. [1] Traag, V.A., Waltman, L. & van Eck, N.J. From Leiden to Leiden: guaranteeing
|
||
well-connected communities. Sci Rep 9, 5233 (2019). https://doi.org/10.1038/s41598-019-41695-z
|
||
|
||
See Also
|
||
--------
|
||
leiden_communities
|
||
:any:`louvain_partitions`
|
||
"""
|
||
raise NotImplementedError(
|
||
"'leiden_partitions' is not implemented by networkx. "
|
||
"Please try a different backend."
|
||
)
|