生物医学网络科学Biomedical Network Science
Example01 / 07

疾病基因是否聚成模块(最大连通分支 + 度匹配随机基线)

所属专题
疾病模块、网络邻近度与药物重定位
下载
Notebook · .py

计算疾病基因诱导子图的最大连通分支(LCC)大小,与 300 组“度匹配”的随机基因集比较,得到 z 分数。 度匹配:把节点按度从小到大装箱(每箱至少 40 个节点),随机集合中每个基因都从原基因所在的度箱中无放回抽取。 输出解读:疾病基因的 LCC 明显大于随机期望(z 远大于 2),支持“疾病模块”。 若改为从全网均匀抽取随机基因,随机集合的度偏低、更难相连,z 会被人为抬高。

Output输出图

疾病基因是否聚成模块(最大连通分支 + 度匹配随机基线):输出图 1

Output文字输出

观察 LCC = 18,度匹配随机期望 = 2.8 ± 1.5,z = 10.3,经验 p = 0.003

Code代码

# 计算疾病基因诱导子图的最大连通分支(LCC)大小,与 300 组“度匹配”的随机基因集比较,得到 z 分数。
# 度匹配:把节点按度从小到大装箱(每箱至少 40 个节点),随机集合中每个基因都从原基因所在的度箱中无放回抽取。
# 输出解读:疾病基因的 LCC 明显大于随机期望(z 远大于 2),支持“疾病模块”。
# 若改为从全网均匀抽取随机基因,随机集合的度偏低、更难相连,z 会被人为抬高。
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt

def make_network(seed=14):
    G = nx.barabasi_albert_graph(1000, 2, seed=seed)
    rng = np.random.default_rng(seed)
    # 从一个中等度数的种子出发,每步把“与当前模块连边最多”的邻居之一加入,得到一个局部聚集的疾病模块
    start = int(rng.choice([n for n in G if 4 <= G.degree(n) <= 8]))
    module = {start}
    while len(module) < 25:
        frontier = {v for u in module for v in G[u]} - module
        score = {v: sum(1 for u in G[v] if u in module) for v in frontier}
        best = max(score.values())
        cand = sorted(v for v in frontier if score[v] == best)
        module.add(int(rng.choice(cand)))
    # 模拟“数据不完整”:随机丢掉模块中 5 个基因的身份(只知道 20 个疾病基因)
    known = sorted(rng.choice(sorted(module), size=20, replace=False).tolist())
    return G, known

def degree_bins(G, min_bin=40):
    """按度从小到大排列节点,相邻度值合并,直到每个箱至少有 min_bin 个节点。返回 节点 -> 箱内节点列表。"""
    by_deg = {}
    for n, d in G.degree():
        by_deg.setdefault(d, []).append(n)
    bins, cur = [], []
    for d in sorted(by_deg):
        cur += by_deg[d]
        if len(cur) >= min_bin:
            bins.append(cur); cur = []
    if cur:
        bins[-1] += cur
    return {n: b for b in bins for n in b}

def lcc_size(G, nodes):
    H = G.subgraph(nodes)
    return max(len(c) for c in nx.connected_components(H)) if len(H) else 0

def sample_matched(rng, nodes, node2bin):
    """为 nodes 中每个节点从其度箱中抽一个替身,整体不重复。"""
    chosen = set()
    for n in nodes:
        pool = [v for v in node2bin[n] if v not in chosen]
        chosen.add(int(rng.choice(pool)))
    return sorted(chosen)

G, disease = make_network()
node2bin = degree_bins(G)
rng = np.random.default_rng(0)

obs = lcc_size(G, disease)
rand_sizes = [lcc_size(G, sample_matched(rng, disease, node2bin)) for _ in range(300)]
mu, sd = np.mean(rand_sizes), np.std(rand_sizes)
p_emp = (1 + sum(s >= obs for s in rand_sizes)) / (len(rand_sizes) + 1)
print(f"观察 LCC = {obs},度匹配随机期望 = {mu:.1f} ± {sd:.1f},z = {(obs - mu) / sd:.1f},经验 p = {p_emp:.3f}")

plt.figure(figsize=(5, 3))
plt.hist(rand_sizes, bins=range(0, max(rand_sizes + [obs]) + 2), color="gray", alpha=0.7, label="degree-matched random")
plt.axvline(obs, color="r", label="disease genes")
plt.xlabel("largest connected component size"); plt.ylabel("count"); plt.legend(); plt.tight_layout(); plt.show()

Run在浏览器中运行

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

疾病基因是否聚成模块(最大连通分支 + 度匹配随机基线)