Run community detection. Returns {node_id: community_id}. Tries Leiden (graspologic) first — best quality. Falls back to Louvain (built into networkx) if graspologic is not installed. resolution > 1.0 → more, smaller communities. resolution < 1.0 → fewer, larger communities. O
(G: nx.Graph, resolution: float = 1.0)
| 20 | |
| 21 | |
| 22 | def _partition(G: nx.Graph, resolution: float = 1.0) -> dict[str, int]: |
| 23 | """Run community detection. Returns {node_id: community_id}. |
| 24 | |
| 25 | Tries Leiden (graspologic) first — best quality. |
| 26 | Falls back to Louvain (built into networkx) if graspologic is not installed. |
| 27 | |
| 28 | resolution > 1.0 → more, smaller communities. |
| 29 | resolution < 1.0 → fewer, larger communities. |
| 30 | |
| 31 | Output from graspologic is suppressed to prevent ANSI escape codes |
| 32 | from corrupting terminal scroll buffers on Windows PowerShell 5.1. |
| 33 | """ |
| 34 | stable = nx.Graph() |
| 35 | stable.add_nodes_from(sorted(G.nodes(), key=str)) |
| 36 | edge_rows = sorted( |
| 37 | G.edges(data=True), |
| 38 | key=lambda row: ( |
| 39 | str(row[0]), |
| 40 | str(row[1]), |
| 41 | json.dumps(row[2], sort_keys=True, ensure_ascii=False, default=str), |
| 42 | ), |
| 43 | ) |
| 44 | for src, tgt, attrs in edge_rows: |
| 45 | stable.add_edge(src, tgt, **attrs) |
| 46 | |
| 47 | try: |
| 48 | from graspologic.partition import leiden |
| 49 | lsig = inspect.signature(leiden).parameters |
| 50 | kwargs: dict = {} |
| 51 | if "random_seed" in lsig: |
| 52 | kwargs["random_seed"] = 42 |
| 53 | if "trials" in lsig: |
| 54 | kwargs["trials"] = 1 |
| 55 | if "resolution" in lsig: |
| 56 | kwargs["resolution"] = resolution |
| 57 | # Suppress graspologic output to prevent ANSI escape codes from |
| 58 | # corrupting PowerShell 5.1 scroll buffer (issue #19) |
| 59 | old_stderr = sys.stderr |
| 60 | try: |
| 61 | sys.stderr = io.StringIO() |
| 62 | with _suppress_output(): |
| 63 | result = leiden(stable, **kwargs) |
| 64 | finally: |
| 65 | sys.stderr = old_stderr |
| 66 | return result |
| 67 | except ImportError: |
| 68 | pass |
| 69 | |
| 70 | # Fallback: networkx louvain (available since networkx 2.7). |
| 71 | # Inspect kwargs to stay compatible across NetworkX versions — max_level |
| 72 | # was added in a later release and prevents hangs on large sparse graphs. |
| 73 | kwargs: dict = {"seed": 42, "threshold": 1e-4, "resolution": resolution} |
| 74 | if "max_level" in inspect.signature(nx.community.louvain_communities).parameters: |
| 75 | kwargs["max_level"] = 10 |
| 76 | communities = nx.community.louvain_communities(stable, **kwargs) |
| 77 | return {node: cid for cid, nodes in enumerate(communities) for node in nodes} |
| 78 | |
| 79 |
no test coverage detected