diff --git a/doc/examples_sphinx-gallery/stochastic_variability.py b/doc/examples_sphinx-gallery/stochastic_variability.py new file mode 100644 index 000000000..944f82118 --- /dev/null +++ b/doc/examples_sphinx-gallery/stochastic_variability.py @@ -0,0 +1,134 @@ +""" +.. _tutorials-stochastic-variability: + +========================================================= +Stochastic Variability in Community Detection Algorithms +========================================================= + +This example demonstrates the variability of stochastic community detection methods by analyzing the consistency of multiple partitions using similarity measures normalized mutual information (NMI), variation of information (VI), rand index (RI) on both random and structured graphs. + +""" +# %% +# Import libraries +import igraph as ig +import matplotlib.pyplot as plt +import itertools + +# %% +# First, we generate a graph. +# Load the karate club network +karate = ig.Graph.Famous("Zachary") + +# %% +#For the random graph, we use an Erdős-Rényi :math:`G(n, m)` model, where 'n' is the number of nodes +#and 'm' is the number of edges. We set 'm' to match the edge count of the empirical (Karate Club) +#network to ensure structural similarity in terms of connectivity, making comparisons meaningful. +n_nodes = karate.vcount() +n_edges = karate.ecount() +#Generate an Erdős-Rényi graph with the same number of nodes and edges +random_graph = ig.Graph.Erdos_Renyi(n=n_nodes, m=n_edges) + +# %% +# Now, lets plot the graph to visually understand them. + +# Create subplots +fig, axes = plt.subplots(1, 2, figsize=(12, 6)) + +# Karate Club Graph +layout_karate = karate.layout("fr") +ig.plot( + karate, layout=layout_karate, target=axes[0], vertex_size=30, vertex_color="lightblue", edge_width=1, + vertex_label=[str(v.index) for v in karate.vs], vertex_label_size=10 +) +axes[0].set_title("Karate Club Network") + +# Erdős-Rényi Graph +layout_random = random_graph.layout("fr") +ig.plot( + random_graph, layout=layout_random, target=axes[1], vertex_size=30, vertex_color="lightcoral", edge_width=1, + vertex_label=[str(v.index) for v in random_graph.vs], vertex_label_size=10 +) +axes[1].set_title("Erdős-Rényi Random Graph") +# %% +# Function to compute similarity between partitions +def compute_pairwise_similarity(partitions, method): + similarities = [] + + for p1, p2 in itertools.combinations(partitions, 2): + similarity = ig.compare_communities(p1, p2, method=method) + similarities.append(similarity) + + return similarities + +# %% +# We have used, stochastic community detection using the Louvain method, iteratively generating partitions and computing similarity metrics to assess stability. +# The Louvain method is a modularity maximization approach for community detection. +# Since exact modularity maximization is NP-hard, the algorithm employs a greedy heuristic that processes vertices in a random order. +# This randomness leads to variations in the detected communities across different runs, which is why results may differ each time the method is applied. +def run_experiment(graph, iterations=50): + partitions = [graph.community_multilevel().membership for _ in range(iterations)] + nmi_scores = compute_pairwise_similarity(partitions, method="nmi") + vi_scores = compute_pairwise_similarity(partitions, method="vi") + ri_scores = compute_pairwise_similarity(partitions, method="rand") + return nmi_scores, vi_scores, ri_scores + +# %% +# Run experiments +nmi_karate, vi_karate, ri_karate = run_experiment(karate) +nmi_random, vi_random, ri_random = run_experiment(random_graph) + +# %% +# Lastly, lets plot probability density histograms to understand the result. +fig, axes = plt.subplots(3, 2, figsize=(12, 10)) +measures = [ + (nmi_karate, nmi_random, "NMI", 0, 1), # Normalized Mutual Information (0-1, higher = more similar) + (vi_karate, vi_random, "VI", 0, None), # Variation of Information (0+, lower = more similar) + (ri_karate, ri_random, "RI", 0, 1), # Rand Index (0-1, higher = more similar) +] +colors = ["red", "blue", "green"] + +for i, (karate_scores, random_scores, measure, lower, upper) in enumerate(measures): + # Karate Club histogram + axes[i][0].hist( + karate_scores, bins=20, alpha=0.7, color=colors[i], edgecolor="black", + density=True # Probability density + ) + axes[i][0].set_title(f"Probability Density of {measure} - Karate Club Network") + axes[i][0].set_xlabel(f"{measure} Score") + axes[i][0].set_ylabel("Density") + axes[i][0].set_xlim(lower, upper) # Set axis limits explicitly + + # Erdős-Rényi Graph histogram + axes[i][1].hist( + random_scores, bins=20, alpha=0.7, color=colors[i], edgecolor="black", + density=True + ) + axes[i][1].set_title(f"Probability Density of {measure} - Erdős-Rényi Graph") + axes[i][1].set_xlabel(f"{measure} Score") + axes[i][1].set_xlim(lower, upper) # Set axis limits explicitly + +plt.tight_layout() +plt.show() + +# %% +# We have compared the probability density of NMI, VI, and RI for the Karate Club network (structured) and an Erdős-Rényi random graph. +# +# **NMI (Normalized Mutual Information):** +# +# - Karate Club Network: The distribution is concentrated near 1, indicating high similarity across multiple runs, suggesting stable community detection. +# - Erdős-Rényi Graph: The values are more spread out, with lower NMI scores, showing inconsistent partitions due to the lack of clear community structures. +# +# **VI (Variation of Information):** +# +# - Karate Club Network: The values are low and clustered, indicating stable partitioning with minor variations across runs. +# - Erdős-Rényi Graph: The distribution is broader and shifted toward higher VI values, meaning higher partition variability and less consistency. +# +# **RI (Rand Index):** +# +# - Karate Club Network: The RI values are high and concentrated near 1, suggesting consistent clustering results across multiple iterations. +# - Erdős-Rényi Graph: The distribution is more spread out, but with lower RI values, confirming unstable community detection. +# +# **Conclusion** +# +# The Karate Club Network exhibits strong, well-defined community structures, leading to consistent results across runs. +# The Erdős-Rényi Graph, being random, lacks clear communities, causing high variability in detected partitions. \ No newline at end of file diff --git a/doc/source/sg_execution_times.rst b/doc/source/sg_execution_times.rst deleted file mode 100644 index a63741754..000000000 --- a/doc/source/sg_execution_times.rst +++ /dev/null @@ -1,109 +0,0 @@ - -:orphan: - -.. _sphx_glr_sg_execution_times: - - -Computation times -================= -**00:10.013** total execution time for 25 files **from all galleries**: - -.. container:: - - .. raw:: html - - - - - - - - .. list-table:: - :header-rows: 1 - :class: table table-striped sg-datatable - - * - Example - - Time - - Mem (MB) - * - :ref:`sphx_glr_tutorials_visualize_cliques.py` (``../examples_sphinx-gallery/visualize_cliques.py``) - - 00:02.970 - - 0.0 - * - :ref:`sphx_glr_tutorials_ring_animation.py` (``../examples_sphinx-gallery/ring_animation.py``) - - 00:01.287 - - 0.0 - * - :ref:`sphx_glr_tutorials_cluster_contraction.py` (``../examples_sphinx-gallery/cluster_contraction.py``) - - 00:00.759 - - 0.0 - * - :ref:`sphx_glr_tutorials_betweenness.py` (``../examples_sphinx-gallery/betweenness.py``) - - 00:00.735 - - 0.0 - * - :ref:`sphx_glr_tutorials_visual_style.py` (``../examples_sphinx-gallery/visual_style.py``) - - 00:00.711 - - 0.0 - * - :ref:`sphx_glr_tutorials_delaunay-triangulation.py` (``../examples_sphinx-gallery/delaunay-triangulation.py``) - - 00:00.504 - - 0.0 - * - :ref:`sphx_glr_tutorials_configuration.py` (``../examples_sphinx-gallery/configuration.py``) - - 00:00.416 - - 0.0 - * - :ref:`sphx_glr_tutorials_online_user_actions.py` (``../examples_sphinx-gallery/online_user_actions.py``) - - 00:00.332 - - 0.0 - * - :ref:`sphx_glr_tutorials_erdos_renyi.py` (``../examples_sphinx-gallery/erdos_renyi.py``) - - 00:00.313 - - 0.0 - * - :ref:`sphx_glr_tutorials_connected_components.py` (``../examples_sphinx-gallery/connected_components.py``) - - 00:00.216 - - 0.0 - * - :ref:`sphx_glr_tutorials_complement.py` (``../examples_sphinx-gallery/complement.py``) - - 00:00.201 - - 0.0 - * - :ref:`sphx_glr_tutorials_generate_dag.py` (``../examples_sphinx-gallery/generate_dag.py``) - - 00:00.194 - - 0.0 - * - :ref:`sphx_glr_tutorials_visualize_communities.py` (``../examples_sphinx-gallery/visualize_communities.py``) - - 00:00.176 - - 0.0 - * - :ref:`sphx_glr_tutorials_bridges.py` (``../examples_sphinx-gallery/bridges.py``) - - 00:00.169 - - 0.0 - * - :ref:`sphx_glr_tutorials_spanning_trees.py` (``../examples_sphinx-gallery/spanning_trees.py``) - - 00:00.161 - - 0.0 - * - :ref:`sphx_glr_tutorials_isomorphism.py` (``../examples_sphinx-gallery/isomorphism.py``) - - 00:00.153 - - 0.0 - * - :ref:`sphx_glr_tutorials_quickstart.py` (``../examples_sphinx-gallery/quickstart.py``) - - 00:00.142 - - 0.0 - * - :ref:`sphx_glr_tutorials_minimum_spanning_trees.py` (``../examples_sphinx-gallery/minimum_spanning_trees.py``) - - 00:00.137 - - 0.0 - * - :ref:`sphx_glr_tutorials_simplify.py` (``../examples_sphinx-gallery/simplify.py``) - - 00:00.079 - - 0.0 - * - :ref:`sphx_glr_tutorials_bipartite_matching_maxflow.py` (``../examples_sphinx-gallery/bipartite_matching_maxflow.py``) - - 00:00.073 - - 0.0 - * - :ref:`sphx_glr_tutorials_articulation_points.py` (``../examples_sphinx-gallery/articulation_points.py``) - - 00:00.067 - - 0.0 - * - :ref:`sphx_glr_tutorials_topological_sort.py` (``../examples_sphinx-gallery/topological_sort.py``) - - 00:00.058 - - 0.0 - * - :ref:`sphx_glr_tutorials_bipartite_matching.py` (``../examples_sphinx-gallery/bipartite_matching.py``) - - 00:00.058 - - 0.0 - * - :ref:`sphx_glr_tutorials_shortest_path_visualisation.py` (``../examples_sphinx-gallery/shortest_path_visualisation.py``) - - 00:00.052 - - 0.0 - * - :ref:`sphx_glr_tutorials_maxflow.py` (``../examples_sphinx-gallery/maxflow.py``) - - 00:00.052 - - 0.0