生物医学网络科学Biomedical Network Science
Example13 / 15

节点在社团中的角色——模块内度 z 分数与参与系数 P

所属专题
社团结构与社区发现
下载
Notebook · .py

Guimerà 与 Amaral(2005)用两个量刻画节点角色: 模块内度 z 分数 z_i = (κ_i - mean κ_{s_i}) / std κ_{s_i},κ_i 为 i 连向本社团的边数; 参与系数 P_i = 1 - Σ_s (k_is / k_i)^2,k_is 为 i 连向社团 s 的边数。 z ≥ 2.5 记为枢纽,再按 P 划分 7 种角色(R1–R7)。这里在空手道网络的 Louvain 划分上计算。 输出:管理者(33)与教练(0)的 z 最高(约 2.1–2.3),但在只有 34 个节点的网络里达不到 z ≥ 2.5 的枢纽门槛, 按原始阈值都被归为 R2——说明这套阈值是为大网络设计的;P 较高的节点连向多个社团,是“连接者”。

Output输出图

节点在社团中的角色——模块内度 z 分数与参与系数 P:输出图 1

Output文字输出

社团数 4
节点 33  度 17  z =  2.27  P = 0.57  R2 外围
节点  0  度 16  z =  2.14  P = 0.54  R2 外围
节点 32  度 12  z =  1.91  P = 0.40  R2 外围
节点  1  度  9  z =  1.43  P = 0.20  R2 外围
节点  5  度  4  z =  1.22  P = 0.38  R2 外围
参与系数最高的 3 个节点: [(28, 0.67), (27, 0.62), (31, 0.61)]

Code代码

# Guimerà 与 Amaral(2005)用两个量刻画节点角色:
#   模块内度 z 分数 z_i = (κ_i - mean κ_{s_i}) / std κ_{s_i},κ_i 为 i 连向本社团的边数;
#   参与系数 P_i = 1 - Σ_s (k_is / k_i)^2,k_is 为 i 连向社团 s 的边数。
# z ≥ 2.5 记为枢纽,再按 P 划分 7 种角色(R1–R7)。这里在空手道网络的 Louvain 划分上计算。
# 输出:管理者(33)与教练(0)的 z 最高(约 2.1–2.3),但在只有 34 个节点的网络里达不到 z ≥ 2.5 的枢纽门槛,
#       按原始阈值都被归为 R2——说明这套阈值是为大网络设计的;P 较高的节点连向多个社团,是“连接者”。
import networkx as nx
import numpy as np

K = nx.karate_club_graph()
G = nx.Graph(K.edges())
comms = nx.community.louvain_communities(G, seed=1)
lab = {v: i for i, c in enumerate(comms) for v in c}

kappa = {v: sum(lab[u] == lab[v] for u in G[v]) for v in G}
z = {}
for c in comms:
    vals = np.array([kappa[v] for v in c], float)
    sd = vals.std() if vals.std() > 0 else 1.0
    for v in c:
        z[v] = (kappa[v] - vals.mean()) / sd
P = {}
for v in G:
    k_is = np.bincount([lab[u] for u in G[v]], minlength=len(comms))
    P[v] = 1 - ((k_is / G.degree(v)) ** 2).sum()


def role(zv, pv):
    if zv < 2.5:
        return "R1 极端外围" if pv <= 0.05 else "R2 外围" if pv <= 0.62 else "R3 非枢纽连接者" if pv <= 0.80 else "R4 非枢纽无归属"
    return "R5 局部枢纽" if pv <= 0.30 else "R6 连接枢纽" if pv <= 0.75 else "R7 无归属枢纽"


print("社团数 %d" % len(comms))
for v in sorted(G, key=lambda x: -z[x])[:5]:
    print("节点 %2d  度 %2d  z = %5.2f  P = %.2f  %s" % (v, G.degree(v), z[v], P[v], role(z[v], P[v])))
print("参与系数最高的 3 个节点:", [(v, round(float(P[v]), 2)) for v in sorted(G, key=lambda x: -P[x])[:3]])

# 作图:z–P 角色平面,虚线为 Guimerà–Amaral 的角色边界
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6.4, 4.2))
ax.scatter([P[v] for v in G], [z[v] for v in G], c=[lab[v] for v in G], cmap="Set2", s=[20 + 5 * G.degree(v) for v in G],
           edgecolors="0.3")
for v in sorted(G, key=lambda x: -z[x])[:3] + sorted(G, key=lambda x: -P[x])[:2]:
    ax.annotate(str(v), (P[v], z[v]), textcoords="offset points", xytext=(5, 3), fontsize=9)
ax.axhline(2.5, color="#d6604d", ls="--", lw=1)
for x in [0.05, 0.62, 0.80]:
    ax.axvline(x, color="0.6", ls=":", lw=1)
ax.set_xlabel("participation coefficient P"); ax.set_ylabel("within-module degree z")
ax.set_title("Node roles: no node reaches the hub threshold z = 2.5", fontsize=10)
plt.tight_layout(); plt.show()

Run在浏览器中运行

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

节点在社团中的角色——模块内度 z 分数与参与系数 P