生物医学网络科学Biomedical Network Science
Example03 / 09

社团发现、模块度与结果稳定性

所属专题
网络分析流程与 NetworkX 实践
下载
Notebook · .py

在空手道俱乐部网络上运行 Louvain(固定种子)与贪心模块度算法,比较社团数和模块度 Q; 再用 10 个不同种子运行 Louvain,报告 Q 的范围;最后用归一化互信息(NMI) 把 Louvain 划分与俱乐部的真实分裂(节点属性 ‘club’)对照,并按社团着色画图。 解读:Louvain 有随机性,应固定种子以便复算,并报告多次运行的范围;NMI = 1 表示两种划分完全一致。

Output输出图

社团发现、模块度与结果稳定性:输出图 1

Output文字输出

Louvain  社团数 = 4, Q = 0.444
Greedy   社团数 = 3, Q = 0.411
Louvain 10 个种子:Q 范围 0.427 – 0.445
Louvain 与真实分裂的 NMI = 0.600

Code代码

# 在空手道俱乐部网络上运行 Louvain(固定种子)与贪心模块度算法,比较社团数和模块度 Q;
# 再用 10 个不同种子运行 Louvain,报告 Q 的范围;最后用归一化互信息(NMI)
# 把 Louvain 划分与俱乐部的真实分裂(节点属性 'club')对照,并按社团着色画图。
# 解读:Louvain 有随机性,应固定种子以便复算,并报告多次运行的范围;NMI = 1 表示两种划分完全一致。
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

G = nx.karate_club_graph()
truth = [G.nodes[v]["club"] for v in G]

def nmi(labels_a, labels_b):
    """归一化互信息(NMI),1 表示两种划分完全一致。"""
    a = np.unique(labels_a, return_inverse=True)[1]
    b = np.unique(labels_b, return_inverse=True)[1]
    n = len(a)
    C = np.zeros((a.max() + 1, b.max() + 1))
    for i, j in zip(a, b):
        C[i, j] += 1
    P = C / n; pa = P.sum(1); pb = P.sum(0)
    nz = P > 0
    I = (P[nz] * np.log(P[nz] / np.outer(pa, pb)[nz])).sum()
    Ha = -(pa[pa > 0] * np.log(pa[pa > 0])).sum(); Hb = -(pb[pb > 0] * np.log(pb[pb > 0])).sum()
    return 2 * I / (Ha + Hb)

comms_louvain = nx.community.louvain_communities(G, seed=1)
comms_greedy = nx.community.greedy_modularity_communities(G)
for name, cs in [("Louvain", comms_louvain), ("Greedy", comms_greedy)]:
    print("%-8s 社团数 = %d, Q = %.3f" % (name, len(cs), nx.community.modularity(G, cs)))

Qs = [nx.community.modularity(G, nx.community.louvain_communities(G, seed=s)) for s in range(10)]
print("Louvain 10 个种子:Q 范围 %.3f – %.3f" % (min(Qs), max(Qs)))

label = {v: i for i, c in enumerate(comms_louvain) for v in c}
print("Louvain 与真实分裂的 NMI = %.3f" % nmi([label[v] for v in G], truth))

pos = nx.spring_layout(G, seed=3)
nx.draw_networkx(G, pos, node_color=[label[v] for v in G], cmap="tab10", node_size=200, font_size=7)
plt.axis("off"); plt.title("Louvain communities"); plt.show()

Run在浏览器中运行

首次加载 Python 环境约需 20–40 秒;可以修改参数后重新运行。

社团发现、模块度与结果稳定性