Compare Infomap, Louvain, and Leiden with igraph¶
This tutorial shows how to use Infomap in a python-igraph workflow. It keeps the result in igraph’s native VertexClustering shape so it fits next to community_multilevel() for Louvain and community_leiden() for Leiden.
Infomap optimizes the map equation, which describes flow on the network. Louvain and Leiden optimize modularity-style objectives. The labels produced by all three methods are local community IDs, so compare partitions by membership, sizes, and downstream behavior rather than by the numeric label values themselves.
import infomap
import igraph as ig
import networkx as nx
import pandas as pd
print("infomap:", infomap.__version__)
print("igraph:", ig.__version__)
print("networkx:", nx.__version__)
print("pandas:", pd.__version__)
infomap: 2.12.0
igraph: 1.0.0
networkx: 3.6.1
pandas: 2.3.3
Build a small igraph graph¶
NetworkX provides the karate club graph as a convenient built-in dataset. The graph is converted to igraph for this tutorial so all community methods use igraph-native graph data.
nx_graph = nx.karate_club_graph()
graph = ig.Graph(edges=list(nx_graph.edges()), directed=False)
graph.vs["name"] = [str(vertex.index) for vertex in graph.vs]
graph.vs["club"] = [nx_graph.nodes[vertex.index]["club"] for vertex in graph.vs]
graph.es["weight"] = [1.0] * graph.ecount()
print(graph.summary())
IGRAPH UNW- 34 78 --
+ attr: club (v), name (v), weight (e)
Run the community methods¶
infomap.find_igraph_communities() returns an igraph.VertexClustering and adds a codelength attribute. Louvain is available as community_multilevel(). Leiden is used through community_leiden() when the installed igraph build supports it.
infomap_clusters = infomap.find_igraph_communities(
graph,
edge_weights="weight",
trials=20,
seed=123,
)
louvain_clusters = graph.community_multilevel(weights="weight")
leiden_clusters = None
leiden_note = None
if not hasattr(graph, "community_leiden"):
leiden_note = "igraph Leiden is not available in this igraph installation."
else:
try:
leiden_clusters = graph.community_leiden(
weights="weight",
objective_function="modularity",
)
except Exception as exc:
leiden_note = f"igraph Leiden skipped: {exc}"
print("Infomap communities:", len(infomap_clusters), "codelength:", infomap_clusters.codelength)
print("Louvain communities:", len(louvain_clusters), "modularity:", louvain_clusters.modularity)
if leiden_clusters is None:
print(leiden_note)
else:
print("Leiden communities:", len(leiden_clusters), "modularity:", leiden_clusters.modularity)
Infomap communities: 3 codelength: 4.3117926458018285
Louvain communities: 4 modularity: 0.41880341880341876
Leiden communities: 4 modularity: 0.41978961209730437
Compare assignments¶
The DataFrame uses vertex names as user-facing node labels. Community IDs are generated independently by each algorithm.
df = pd.DataFrame(
{
"node": graph.vs["name"],
"club": graph.vs["club"],
"infomap": infomap_clusters.membership,
"louvain": louvain_clusters.membership,
}
)
if leiden_clusters is not None:
df["leiden"] = leiden_clusters.membership
df.head(10)
| node | club | infomap | louvain | leiden | |
|---|---|---|---|---|---|
| 0 | 0 | Mr. Hi | 0 | 0 | 0 |
| 1 | 1 | Mr. Hi | 0 | 0 | 0 |
| 2 | 2 | Mr. Hi | 0 | 0 | 0 |
| 3 | 3 | Mr. Hi | 0 | 0 | 0 |
| 4 | 4 | Mr. Hi | 1 | 1 | 1 |
| 5 | 5 | Mr. Hi | 1 | 1 | 1 |
| 6 | 6 | Mr. Hi | 1 | 1 | 1 |
| 7 | 7 | Mr. Hi | 0 | 0 | 0 |
| 8 | 8 | Mr. Hi | 2 | 2 | 2 |
| 9 | 9 | Officer | 0 | 0 | 2 |
df.drop(columns="node").nunique().rename("communities").to_frame()
| communities | |
|---|---|
| club | 2 |
| infomap | 3 |
| louvain | 4 |
| leiden | 4 |
Simple similarity metrics¶
AMI and NMI help summarize how similar each assignment is to the known karate-club split. They are label-comparison metrics and do not depend on the actual numeric community IDs.
from sklearn.metrics import adjusted_mutual_info_score, normalized_mutual_info_score
comparisons = []
for method in ["infomap", "louvain", "leiden"]:
if method not in df:
continue
comparisons.append(
{
"method": method,
"AMI vs truth": adjusted_mutual_info_score(df["club"], df[method]),
"NMI vs truth": normalized_mutual_info_score(df["club"], df[method]),
}
)
metrics = pd.DataFrame(comparisons)
metrics
| method | AMI vs truth | NMI vs truth | |
|---|---|---|---|
| 0 | infomap | 0.551082 | 0.568380 |
| 1 | louvain | 0.463752 | 0.489967 |
| 2 | leiden | 0.566666 | 0.587850 |
Annotate vertices for downstream igraph use¶
Use module_attribute and flow_attribute when you want Infomap results stored directly on the igraph vertices for plotting, filtering, or export.
annotated = graph.copy()
infomap.find_igraph_communities(
annotated,
edge_weights="weight",
module_attribute="infomap",
flow_attribute="infomap_flow",
trials=20,
seed=123,
)
pd.DataFrame(
{
"node": annotated.vs["name"],
"infomap": annotated.vs["infomap"],
"infomap_flow": annotated.vs["infomap_flow"],
}
).head()
| node | infomap | infomap_flow | |
|---|---|---|---|
| 0 | 0 | 0 | 0.102564 |
| 1 | 1 | 0 | 0.057692 |
| 2 | 2 | 0 | 0.064103 |
| 3 | 3 | 0 | 0.038462 |
| 4 | 4 | 1 | 0.019231 |
Citation¶
If you use Infomap in published work, cite the Infomap software and the map equation literature. See the repository and Infomap user guide for the current recommended references.