Skip to content

Commit 9ce8135

Browse files
committed
Add gallery example: Clique Percolation Method for overlapping communities
Adds a new example to the sphinx-gallery demonstrating the Clique Percolation Method (CPM) for overlapping community detection. The example shows both k=3 and k=4, visualized with VertexCover. Closes #819 (partially — clique percolation item)
1 parent de9bc40 commit 9ce8135

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
.. _tutorials-clique-percolation:
3+
4+
==========================
5+
Clique Percolation Method
6+
==========================
7+
8+
This example shows how to detect **overlapping communities** using the
9+
Clique Percolation Method (CPM). Unlike partitioning algorithms, CPM allows
10+
a node to belong to more than one community simultaneously.
11+
"""
12+
13+
import itertools
14+
from collections import Counter
15+
16+
import igraph as ig
17+
import matplotlib.pyplot as plt
18+
19+
# %%
20+
# We construct a graph with three dense cliques that share individual nodes,
21+
# creating natural *overlapping* community boundaries:
22+
g = ig.Graph(9)
23+
g.add_edges(list(itertools.combinations([0, 1, 2, 3], 2))) # clique A
24+
g.add_edges(list(itertools.combinations([3, 4, 5, 6], 2))) # clique B, shares node 3 with A
25+
g.add_edges(list(itertools.combinations([6, 7, 8], 2))) # clique C, shares node 6 with B
26+
27+
# %%
28+
# The CPM algorithm works in three steps:
29+
#
30+
# 1. Find all k-cliques (complete subgraphs of exactly k nodes).
31+
# 2. Build a clique graph : two k-cliques are adjacent when they share k-1 nodes.
32+
# 3. Each connected component of the clique graph is a community — the union
33+
# of all its k-cliques. A node shared between cliques in *different*
34+
# components belongs to multiple communities simultaneously.
35+
k = 3
36+
cliques = [set(c) for c in g.cliques(k, k)]
37+
38+
clique_graph = ig.Graph(len(cliques))
39+
clique_graph.add_edges([
40+
(i, j)
41+
for i, j in itertools.combinations(range(len(cliques)), 2)
42+
if len(cliques[i] & cliques[j]) >= k - 1
43+
])
44+
45+
communities = []
46+
for component in clique_graph.connected_components():
47+
members = set()
48+
for idx in component:
49+
members |= cliques[idx]
50+
communities.append(sorted(members))
51+
52+
# %%
53+
# Nodes that appear in more than one community are *overlapping nodes*:
54+
overlap = [
55+
v for v, count in Counter(v for comm in communities for v in comm).items()
56+
if count > 1
57+
]
58+
print(f"Communities (k={k}): {communities}")
59+
print(f"Overlapping nodes: {overlap}")
60+
61+
# %%
62+
# We visualize the result using :class:`igraph.VertexCover`, which draws a
63+
# shaded hull around each community and handles overlapping nodes naturally:
64+
cover = ig.VertexCover(g, communities)
65+
palette = ig.RainbowPalette(n=len(communities))
66+
67+
fig, ax = plt.subplots(figsize=(6, 5))
68+
ig.plot(
69+
cover,
70+
mark_groups=True,
71+
palette=palette,
72+
vertex_size=25,
73+
vertex_label=list(range(g.vcount())),
74+
vertex_label_size=10,
75+
edge_width=1.5,
76+
target=ax,
77+
)
78+
ax.set_title(
79+
f"Clique Percolation Method (k={k})\n"
80+
f"{len(communities)} communities — overlapping nodes: {overlap}"
81+
)
82+
plt.show()
83+
84+
# %%
85+
# Advanced: effect of k
86+
# ----------------------
87+
# Raising k to 4 requires larger cliques. The 3-clique {6, 7, 8} no longer
88+
# qualifies, so community C disappears and node 6 is no longer in any community:
89+
k = 4
90+
cliques_4 = [set(c) for c in g.cliques(k, k)]
91+
92+
clique_graph_4 = ig.Graph(len(cliques_4))
93+
clique_graph_4.add_edges([
94+
(i, j)
95+
for i, j in itertools.combinations(range(len(cliques_4)), 2)
96+
if len(cliques_4[i] & cliques_4[j]) >= k - 1
97+
])
98+
99+
communities_4 = []
100+
for component in clique_graph_4.connected_components():
101+
members = set()
102+
for idx in component:
103+
members |= cliques_4[idx]
104+
communities_4.append(sorted(members))
105+
106+
print(f"Communities (k=4): {communities_4}")
107+
108+
cover_4 = ig.VertexCover(g, communities_4)
109+
palette_4 = ig.RainbowPalette(n=max(len(communities_4), 1))
110+
111+
fig, ax = plt.subplots(figsize=(6, 5))
112+
ig.plot(
113+
cover_4,
114+
mark_groups=True,
115+
palette=palette_4,
116+
vertex_size=25,
117+
vertex_label=list(range(g.vcount())),
118+
vertex_label_size=10,
119+
edge_width=1.5,
120+
target=ax,
121+
)
122+
ax.set_title(
123+
f"Clique Percolation Method (k=4)\n"
124+
f"{len(communities_4)} communities — nodes 6, 7, 8 no longer qualify"
125+
)
126+
plt.show()

0 commit comments

Comments
 (0)