Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
28f8cec
add: community detection guide docs - wip
Jul 29, 2025
5632575
fix: ipython warning
BeaMarton13 Jul 31, 2025
1d700a8
fix: remove all warnings
BeaMarton13 Jul 31, 2025
be75a60
add: notebooks and made community detection guide prettier
Jul 31, 2025
3981103
delete: unused files
Jul 31, 2025
91b1b5f
delete: ipynb_checkpoints
BeaMarton13 Jul 31, 2025
0f36868
add: all modularities w plot
BeaMarton13 Aug 1, 2025
035c579
add: LPA test
BeaMarton13 Aug 1, 2025
4679657
Merge remote-tracking branch 'upstream/develop' into feat/community-d…
BeaMarton13 Aug 4, 2025
a99dcf0
removed checkpoints
BeaMarton13 Aug 4, 2025
99db9fd
add: working with clusterings
BeaMarton13 Aug 4, 2025
29e17dd
add: updated modularity
BeaMarton13 Aug 4, 2025
06b22bd
add: fixed seed for grid graph
BeaMarton13 Aug 4, 2025
c9417de
add: new resolution params
BeaMarton13 Aug 4, 2025
61bb0c2
add: updated notebooks
BeaMarton13 Aug 4, 2025
4be130d
add: updated mkdoc
BeaMarton13 Aug 5, 2025
612d756
add: download button - wip
BeaMarton13 Aug 5, 2025
6a8d874
fix: unclickable card
BeaMarton13 Aug 5, 2025
e88e9e7
add: change color on download hover
BeaMarton13 Aug 5, 2025
7708a03
add: updated modularity
BeaMarton13 Aug 6, 2025
8eef8d0
fix: PR comments
BeaMarton13 Aug 6, 2025
ef2fb40
add: updated title for working with clusterings and removed extra new…
BeaMarton13 Aug 6, 2025
f763788
add: lower case and moved sierpinski methods to functions
BeaMarton13 Aug 6, 2025
6ca7c75
add: LPA description
BeaMarton13 Aug 6, 2025
ea6d5e8
add: new sphinx version, resolution paramter update and reordered tab…
BeaMarton13 Aug 6, 2025
76dfe11
Merge remote-tracking branch 'upstream/develop' into feat/community-d…
BeaMarton13 Aug 6, 2025
e5eaa80
delete: interactive_resolution_on_sierpinski
BeaMarton13 Aug 7, 2025
e675c03
add: updated resoltuion parameter
BeaMarton13 Aug 7, 2025
3eb5565
fix: remove plot function
BeaMarton13 Aug 7, 2025
3eb8e3c
add: more space before titles
BeaMarton13 Aug 7, 2025
0623371
add: weighted clustering for GoT
BeaMarton13 Aug 7, 2025
1fb27aa
add: minor refactoring on significance and smaller note block in init…
BeaMarton13 Aug 8, 2025
c0233aa
add: minor changes
BeaMarton13 Aug 8, 2025
ea0ff51
add: smaller note box
BeaMarton13 Aug 8, 2025
ef96b4c
fix: remove duplicate plots and unused functions
BeaMarton13 Aug 8, 2025
39aa549
add more space before paragraph
BeaMarton13 Aug 11, 2025
ed837d8
add: readme for networks and removed potter-network
BeaMarton13 Aug 11, 2025
9125180
Update README.md
BeaMarton13 Aug 11, 2025
6d6111e
add: directed Leiden support and fix grid graph error
BeaMarton13 Aug 11, 2025
c8b2f15
Merge branch 'feat/community-detection-guide-docs' of github.com:BeaM…
BeaMarton13 Aug 11, 2025
bdcf209
add: download button before notebook - wip
BeaMarton13 Aug 11, 2025
f52a6d9
Merge remote-tracking branch 'upstream/develop' into feat/community-d…
BeaMarton13 Aug 11, 2025
41b5a51
add: centered titles in card
BeaMarton13 Aug 11, 2025
cc82050
Merge remote-tracking branch 'upstream/develop' into feat/community-d…
BeaMarton13 Aug 11, 2025
277c40b
add: resolution limit and modularity formula update
BeaMarton13 Aug 12, 2025
3744ab0
fix: Title level inconsistent error on docs
BeaMarton13 Aug 13, 2025
c86dd8b
add: updated generating and visualizing clusters notebook
BeaMarton13 Aug 13, 2025
4d22459
add: updated generating and visualizing clusters notebook - wip
BeaMarton13 Aug 13, 2025
d209f9c
add: simlpified circle viz and removed unused imports
BeaMarton13 Aug 14, 2025
d9d4ebd
add: intrductions and changed adjacency matrix from lower to upper
BeaMarton13 Aug 14, 2025
01819fb
add: expand factor and edge for polygon
BeaMarton13 Aug 15, 2025
1c10a8a
add: game of thrones export script, csv and remove from functions
BeaMarton13 Aug 15, 2025
09e78ac
add: updated plot order
BeaMarton13 Aug 15, 2025
f81513f
add: local seed with example in initial workflow
BeaMarton13 Aug 15, 2025
820b3fd
fix: math expression
BeaMarton13 Aug 18, 2025
0b57ee7
add: directed and density based modualrity
BeaMarton13 Aug 18, 2025
0a2ecf1
add: new resolution param example and print comms
BeaMarton13 Aug 25, 2025
45d8c14
fix: Data type cannot be displayed in build
BeaMarton13 Aug 25, 2025
83f51dd
add: from directed to undirected modularity
BeaMarton13 Aug 26, 2025
b275ab0
add: working with directed networks and minor fixes
BeaMarton13 Aug 27, 2025
04ef972
add: football network - WIP
BeaMarton13 Aug 29, 2025
16a5fb4
add: cut on dendrogram
BeaMarton13 Aug 29, 2025
da945b8
add: minor descriptions and labels for matrix
BeaMarton13 Aug 29, 2025
d82f30d
add: new membership description - wip and helper function descriptions
BeaMarton13 Aug 29, 2025
399c6fa
fix: minor fixes in some notebooks
BeaMarton13 Aug 29, 2025
6ebb7dc
add: SBM as network for membership vector
BeaMarton13 Aug 30, 2025
0e69ba4
add: hierarchical clustering
BeaMarton13 Aug 30, 2025
933df71
fix: notes - last commit for GSoC2025
BeaMarton13 Aug 31, 2025
54e23de
fix: sentence case - final commit for GSoC2025
BeaMarton13 Aug 31, 2025
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
Prev Previous commit
Next Next commit
add: lower case and moved sierpinski methods to functions
  • Loading branch information
BeaMarton13 committed Aug 6, 2025
commit f763788c96e287f7f4ddb373501b1ef1859acf73
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"id": "4aa38bb9-30e4-4859-b547-ba92036ad57a",
"metadata": {},
"source": [
"# Consensus Clustering\n",
"# Consensus clustering\n",
"\n",
"Consensus clustering is a method that combines the results of multiple clustering runs to produce a more stable and reliable clustering solution. It is particularly useful when the results of a single clustering algorithm are not robust or consistent across different runs. By aggregating the outputs of various clustering runs, consensus clustering aims to identify clusters that are consistently found across different runs and are therefore more likely to be true representations of the underlying data structur"
]
Expand Down Expand Up @@ -152,7 +152,7 @@
"id": "cd4985bf-0667-4dca-9c3a-2e40f6e63c79",
"metadata": {},
"source": [
"## Karate Cub"
"## Karate club"
]
},
{
Expand Down
272 changes: 272 additions & 0 deletions doc/source/community_detection_guide/notebooks/functions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,278 @@
" # To display the DataFrame with interactive features, including sticky headers AND frozen first column\n",
" show(df, scrollY=\"300px\", scrollCollapse=True, fixedColumns=True, pageLength=-1)"
]
},
{
"cell_type": "markdown",
"id": "8575f2b2-a73e-400f-af14-f6c833657a43",
"metadata": {},
"source": [
"## Resolution parameter on Sierpinski"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fbc4947f-38e1-4086-9c2f-522bacf63e75",
"metadata": {},
"outputs": [],
"source": [
"#import igraph as ig\n",
"#import matplotlib.pyplot as plt\n",
"#import numpy as np\n",
"#import ipywidgets as widgets\n",
"#from IPython.display import display, clear_output\n",
"#import matplotlib.cm as cm \n",
"#import matplotlib.colors as mcolors\n",
"#import colorcet as cc\n",
"#ig.config[\"plotting.backend\"] = \"matplotlib\""
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a9181bf9-eaf5-488b-a600-c5e3db066240",
"metadata": {},
"outputs": [],
"source": [
"# Global state for node mapping (needed because igraph uses integer IDs)\n",
"_coord_to_id = {}\n",
"_coords_list = [] # Stores (x, y) tuples in order of their assigned ID\n",
"_next_id = 0\n",
"\n",
"def _get_or_create_vertex(G, coord):\n",
" \"\"\"\n",
" Helper function to get the igraph vertex ID for a given coordinate.\n",
" If the coordinate doesn't exist, it creates a new vertex in G and assigns an ID.\n",
" \"\"\"\n",
" global _coord_to_id, _coords_list, _next_id\n",
" coord_tuple = tuple(coord) # Ensure the coordinate is a hashable tuple\n",
"\n",
" if coord_tuple not in _coord_to_id:\n",
" _coord_to_id[coord_tuple] = _next_id\n",
" _coords_list.append(coord_tuple)\n",
" G.add_vertex() # Add a new vertex to the igraph graph\n",
" _next_id += 1\n",
" return _coord_to_id[coord_tuple]\n",
"\n",
"def _sierpinski_igraph(G, p1, p2, p3, depth):\n",
" \"\"\"\n",
" Recursive function to build the Sierpiński triangle structure in an igraph Graph.\n",
" This function uses integer IDs for vertices after mapping from coordinates.\n",
" \"\"\"\n",
" if depth == 0:\n",
" id1 = _get_or_create_vertex(G, p1)\n",
" id2 = _get_or_create_vertex(G, p2)\n",
" id3 = _get_or_create_vertex(G, p3)\n",
" if not G.are_adjacent(id1, id2): G.add_edge(id1, id2)\n",
" if not G.are_adjacent(id2, id3): G.add_edge(id2, id3)\n",
" if not G.are_adjacent(id3, id1): G.add_edge(id3, id1)\n",
" else:\n",
" a = ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2)\n",
" b = ((p2[0]+p3[0])/2, (p2[1]+p3[1])/2)\n",
" c = ((p3[0]+p1[0])/2, (p3[1]+p1[1])/2)\n",
" _sierpinski_igraph(G, p1, a, c, depth-1)\n",
" _sierpinski_igraph(G, a, p2, b, depth-1)\n",
" _sierpinski_igraph(G, c, b, p3, depth-1)\n",
"\n",
"def draw_sierpinski_igraph_on_axes(depth, ax):\n",
" \"\"\"\n",
" Generates and draws a Sierpiński triangle using igraph onto a given Matplotlib Axes.\n",
" \"\"\"\n",
" global _coord_to_id, _coords_list, _next_id\n",
" \n",
" _coord_to_id = {}\n",
" _coords_list = []\n",
" _next_id = 0\n",
"\n",
" G = ig.Graph()\n",
" p1, p2, p3 = (0, 0), (1, 0), (0.5, np.sqrt(3)/2) \n",
" _sierpinski_igraph(G, p1, p2, p3, depth)\n",
"\n",
" ax.clear()\n",
"\n",
" if not G.vcount():\n",
" ax.set_title(f\"Sierpiński Triangle - Depth {depth} (No vertices)\")\n",
" ax.set_xticks([])\n",
" ax.set_yticks([])\n",
" ax.axis('off')\n",
" return\n",
"\n",
" layout = _coords_list\n",
" ig.plot(G, layout=layout, vertex_size=5, vertex_color=\"black\", \n",
" vertex_label=None, edge_color=\"blue\", edge_width=1,\n",
" bbox=(0, 0, 600, 600), margin=20)\n",
" \n",
" ax.set_title(f\"Sierpiński Triangle - Depth {depth}\")\n",
" ax.set_aspect('equal', adjustable='box')\n",
" ax.set_xticks([])\n",
" ax.set_yticks([])\n",
" ax.axis('off')\n",
"\n",
"# Global state for node mapping (needed because igraph uses integer IDs)\n",
"_coord_to_id = {}\n",
"_coords_list = [] # Stores (x, y) tuples in order of their assigned ID\n",
"_next_id = 0\n",
"\n",
"def _get_or_create_vertex(G, coord):\n",
" \"\"\"\n",
" Helper function to get the igraph vertex ID for a given coordinate.\n",
" If the coordinate doesn't exist, it creates a new vertex in G and assigns an ID.\n",
" \"\"\"\n",
" global _coord_to_id, _coords_list, _next_id\n",
" coord_tuple = tuple(coord) # Ensure the coordinate is a hashable tuple\n",
"\n",
" if coord_tuple not in _coord_to_id:\n",
" _coord_to_id[coord_tuple] = _next_id\n",
" _coords_list.append(coord_tuple)\n",
" G.add_vertex() # Add a new vertex to the igraph graph\n",
" _next_id += 1\n",
" return _coord_to_id[coord_tuple]\n",
"\n",
"def _sierpinski_igraph_builder(G, p1, p2, p3, depth):\n",
" \"\"\"\n",
" Recursive function to build the Sierpiński triangle structure in an igraph Graph.\n",
" This function uses integer IDs for vertices after mapping from coordinates.\n",
" \"\"\"\n",
" if depth == 0:\n",
" id1 = _get_or_create_vertex(G, p1)\n",
" id2 = _get_or_create_vertex(G, p2)\n",
" id3 = _get_or_create_vertex(G, p3)\n",
" if not G.are_adjacent(id1, id2): G.add_edge(id1, id2)\n",
" if not G.are_adjacent(id2, id3): G.add_edge(id2, id3)\n",
" if not G.are_adjacent(id3, id1): G.add_edge(id3, id1)\n",
" else:\n",
" a = ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2)\n",
" b = ((p2[0]+p3[0])/2, (p2[1]+p3[1])/2)\n",
" c = ((p3[0]+p1[0])/2, (p3[1]+p1[1])/2)\n",
" _sierpinski_igraph_builder(G, p1, a, c, depth-1)\n",
" _sierpinski_igraph_builder(G, a, p2, b, depth-1)\n",
" _sierpinski_igraph_builder(G, c, b, p3, depth-1)\n",
"\n",
"def get_sierpinski_graph_and_layout(depth):\n",
" \"\"\"\n",
" Generates a Sierpiński triangle graph and its coordinate layout.\n",
" Returns the igraph.Graph object and the list of (x,y) coordinates for its layout.\n",
" \"\"\"\n",
" import numpy as np\n",
" import igraph as ig\n",
" \n",
" global _coord_to_id, _coords_list, _next_id\n",
" \n",
" _coord_to_id = {}\n",
" _coords_list = []\n",
" _next_id = 0\n",
"\n",
" G = ig.Graph()\n",
" p1, p2, p3 = (0, 0), (1, 0), (0.5, np.sqrt(3)/2) \n",
" _sierpinski_igraph_builder(G, p1, p2, p3, depth)\n",
"\n",
" if not G.vcount():\n",
" print(f\"Warning: Sierpiński graph at depth {depth} has no vertices.\")\n",
" return G, [] # Return empty layout if no vertices\n",
"\n",
" return G, _coords_list\n",
"\n",
"# --- Leiden Clustering and Plotting Function ---\n",
"\n",
"def plot_leiden_communities_on_axes(graph, layout, resolution, ax, title_suffix=\"\"):\n",
" \"\"\"\n",
" Clusters a given graph using Leiden with a specified resolution and plots it\n",
" onto the provided Matplotlib Axes, coloring vertices by community.\n",
" \"\"\"\n",
" import colorcet as cc\n",
" \n",
" ax.clear() # Clear the axes for the new plot\n",
"\n",
" communities = graph.community_leiden(objective_function=\"modularity\", resolution=resolution)\n",
" \n",
" num_communities = len(communities)\n",
"\n",
" palette = cc.glasbey_dark\n",
" vertex_colors = [palette[membership_id % len(palette)] for membership_id in communities.membership]\n",
"\n",
" # Handle case where there are no communities or no vertices (though Leiden usually finds at least 1)\n",
" if not vertex_colors and graph.vcount() > 0:\n",
" vertex_colors = [\"lightgray\"] * graph.vcount()\n",
" elif graph.vcount() == 0:\n",
" ax.set_title(f\"Sierpiński Triangle - No vertices (Depth {SIERPINSKI_DEPTH})\")\n",
" ax.axis('off')\n",
" return\n",
"\n",
" ig.plot(\n",
" graph,\n",
">
" layout=layout,\n",
" vertex_size=32, # Adjust node size (smaller for higher depth for clarity)\n",
" vertex_color=vertex_colors, # Use community-assigned colors\n",
" vertex_label=None, # No labels for vertices\n",
" edge_color=\"black\", # Edge color (can be made less prominent)\n",
" edge_width=1.5,\n",
" bbox=(0, 0, 600, 600), # Bounding box for the internal renderer\n",
" margin=20, # Margin around the plot area\n",
" )\n",
" \n",
" # Set plot title to indicate resolution and number of communities\n",
" ax.set_title(f\"Resolution: {resolution:.3f} ({num_communities} comms) {title_suffix}\")\n",
" ax.set_aspect('equal', adjustable='box') # Ensure the aspect ratio is 1:1\n",
" ax.set_xticks([]) # Remove x-axis ticks\n",
" ax.set_yticks([]) # Remove y-axis ticks\n",
" ax.axis('off') # Turn off axis lines and labels completely\n",
"\n",
"def create_interactive_resolution_param_tabs():\n",
" import numpy as np\n",
" import ipywidgets as widgets\n",
" import matplotlib.pyplot as plt\n",
" # --- Main Tabbed Interface for Leiden Resolutions ---\n",
" \n",
" # 1. Generate the Sierpiński graph (depth 3) ONCE\n",
" # This graph will be reused for all resolution settings.\n",
" SIERPINSKI_DEPTH = 4\n",
" sierpinski_graph, sierpinski_layout = get_sierpinski_graph_and_layout(SIERPINSKI_DEPTH)\n",
" \n",
" print(f\"Generated Sierpiński Graph (Depth {SIERPINSKI_DEPTH}):\")\n",
" print(f\" Vertices: {sierpinski_graph.vcount()}\")\n",
" print(f\" Edges: {sierpinski_graph.ecount()}\")\n",
" \n",
" # Define a set of resolution parameters to display in different tabs\n",
" # 10**-1.5, 10**-0.75, 10**0, 10**0.75, 10**1.5\n",
" resolution_values = [10 ** i for i in np.arange(-1.5, 1.51, 0.75)]\n",
" \n",
" # Create an Output widget for each resolution value. Each Output widget will hold one plot.\n",
" output_widgets = [widgets.Output() for _ in resolution_values]\n",
" \n",
" # Populate the content for each tab\n",
" tab_titles = []\n",
" for i, res in enumerate(resolution_values):\n",
" with output_widgets[i]: # Direct output to the current Output widget\n",
" fig, ax = plt.subplots(figsize=(7, 7)) # Create a new figure and axes for this tab's plot\n",
" \n",
" # Call the plotting function with the pre-generated graph and layout,\n",
" # and the current resolution parameter.\n",
" plot_leiden_communities_on_axes(\n",
" sierpinski_graph,\n",
" sierpinski_layout,\n",
" resolution=res,\n",
" ax=ax,\n",
" title_suffix=f\"(Depth {SIERPINSKI_DEPTH})\" # Optional suffix for the title\n",
" )\n",
" plt.tight_layout() # Adjust layout to prevent labels/titles from overlapping\n",
" plt.show() # Display the Matplotlib figure within this Output widget\n",
" \n",
" tab_titles.append(f\"Res: {res:.3f}\") # Store the title for this tab\n",
" \n",
" # 4. Create the Tab widget\n",
" leiden_tabs = widgets.Tab()\n",
" leiden_tabs.children = output_widgets # Assign the list of Output widgets as children\n",
" \n",
" # 5. Set the titles for each tab\n",
" for i, title in enumerate(tab_titles):\n",
" leiden_tabs.set_title(i, title)\n",
" \n",
" # 6. Display the Tab widget in your Jupyter Notebook\n",
" print(\"\\nSierpiński Graph with Leiden Communities (varying resolution):\")\n",
" display(leiden_tabs)"
]
}
],
"metadata": {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"id": "352b1210-1303-43f4-881d-4c16effc7332",
"metadata": {},
"source": [
"### 1. Load the graph\n",
"### Load the graph\n",
"First we start by loading the graph:"
]
},
Expand All @@ -58,7 +58,7 @@
"id": "07166ec9-696e-46d0-87ee-40cc41f98b10",
"metadata": {},
"source": [
"### 2. Plot the loaded graph (without any community detection yet)\n",
"### Plot the network\n",
"To ensure consistency across our visualizations, we'll first define and compute a single layout for our graph. We will then define a `style` dictionary to store the plot's settings, making them reusable for all our plots:"
]
},
Expand Down Expand Up @@ -108,7 +108,7 @@
"id": "cc18a096-7533-44f3-ba1b-43840ebce47d",
"metadata": {},
"source": [
"### 3. Run community detection\n",
"### Run community detection\n",
"Let's run community detection on the network, starting with the Leiden algorithm. This method detects communities by maximizing modularity. \n",
"<div style=\"background-color: #e6ffe6; padding: 20px; border-radius: 5px;\">\n",
" \n",
Expand Down Expand Up @@ -307,7 +307,7 @@
"id": "f5ed38a2-849e-49ac-89a4-783b1b686a45",
"metadata": {},
"source": [
"### 4. Visualize detected communities\n",
"### Visualize detected communities\n",
"\n",
"Now we can visualize the obtained clusters using a simple `igraph` visualization technique. First, we will define a color map, and then we will add the `vertex_color` property to the `style` dictionary. This will allow us to color the nodes based on their communities:"
]
Expand Down Expand Up @@ -348,7 +348,7 @@
"id": "502357bf-ed1c-407d-aadc-4f4ce88250e5",
"metadata": {},
"source": [
"### 5. Exploring other community detection algorithms (optional)"
"### Exploring other community detection algorithms (optional)"
]
},
{
Expand Down Expand Up @@ -395,7 +395,7 @@
"id": "fe6c594c-0d59-4d99-95e9-98dde62720b4",
"metadata": {},
"source": [
"### 6. Test the stability of the result\n",
"### Test the stability of the result\n",
"Next, we can test the stability of the Leiden algorithm's result by:\n",
"\n",
"* Generating multiple partitions:"
Expand Down Expand Up @@ -479,7 +479,7 @@
" mean_nmi = np.mean(pairwise_nmi_values)\n",
" plt.axvline(mean_nmi, color='blue', linestyle='dashed', linewidth=2, label=f'Mean NMI: {mean_nmi:.4f}')\n",
" \n",
" plt.title('Stability of Community Detection')\n",
" plt.title('Stability of community detection')\n",
" plt.legend()\n",
" plt.tight_layout()\n",
" plt.show()\n",
Expand Down
Loading