Example09 / 13
三节点子图普查(triad census)与显著性谱
networkx 的 triadic_census 按 MAN 编码统计 16 类三元组(含不连通的 003、012、102); 其中 030T 即前馈环,021D 为“一个调控者调两个靶”,021U 为“两个调控者共调一个靶”。 模拟一个嵌入了前馈环与单输入模块的调控网络,用保持入度/出度的边交换生成 50 个随机网络, 对 13 类连通三元组计算 Z 分数与显著性谱 SP = Z / ||Z||。 输出解读:030T(FFL)的 Z 显著为正;正式分析应使用更多随机网络并报告经验 p 值。
Output输出图

Output文字输出
021D 真实 454 随机均值 458.2 Z = -0.4 SP = -0.07 021U 真实 419 随机均值 422.3 Z = -0.3 SP = -0.06 021C 真实 839 随机均值 828.8 Z = 0.5 SP = 0.10 111D 真实 1 随机均值 15.6 Z = -1.8 SP = -0.34 111U 真实 5 随机均值 15.4 Z = -1.0 SP = -0.18 030T 真实 33 随机均值 15.5 Z = 4.5 SP = 0.85 030C 真实 2 随机均值 4.4 Z = -1.0 SP = -0.19 120C 真实 1 随机均值 0.3 Z = 1.3 SP = 0.24
Code代码
# networkx 的 triadic_census 按 MAN 编码统计 16 类三元组(含不连通的 003、012、102);
# 其中 030T 即前馈环,021D 为“一个调控者调两个靶”,021U 为“两个调控者共调一个靶”。
# 模拟一个嵌入了前馈环与单输入模块的调控网络,用保持入度/出度的边交换生成 50 个随机网络,
# 对 13 类连通三元组计算 Z 分数与显著性谱 SP = Z / ||Z||。
# 输出解读:030T(FFL)的 Z 显著为正;正式分析应使用更多随机网络并报告经验 p 值。
import numpy as np
import networkx as nx
rng = np.random.default_rng(7)
D = nx.gnm_random_graph(150, 260, directed=True, seed=7) # 模拟数据,仅用于演示方法
for _ in range(25):
x, y, z = rng.choice(150, 3, replace=False); D.add_edges_from([(x, y), (y, z), (x, z)])
for _ in range(5): # 单输入模块:一个 TF 调 6 个靶
tf = int(rng.integers(150)); D.add_edges_from((tf, int(g)) for g in rng.choice(150, 6, replace=False) if g != tf)
D.remove_edges_from(list(nx.selfloop_edges(D)))
def shuffle(D, n_swap, rng):
D = D.copy(); edges = list(D.edges()); done = 0; tries = 0
while done < n_swap and tries < 50 * n_swap:
tries += 1
i, j = rng.integers(len(edges), size=2)
(a, b), (c, d) = edges[i], edges[j]
if len({a, b, c, d}) < 4 or D.has_edge(a, d) or D.has_edge(c, b):
continue
D.remove_edges_from([(a, b), (c, d)]); D.add_edges_from([(a, d), (c, b)])
edges[i], edges[j] = (a, d), (c, b); done += 1
return D
connected = ["021D", "021U", "021C", "111D", "111U", "030T", "030C",
"201", "120D", "120U", "120C", "210", "300"]
real = nx.triadic_census(D)
null = np.array([[nx.triadic_census(shuffle(D, 5 * D.number_of_edges(), rng))[c] for c in connected]
for _ in range(50)])
Z = (np.array([real[c] for c in connected]) - null.mean(0)) / (null.std(0) + 1e-9)
SP = Z / np.linalg.norm(Z)
for c, r, m, z, sp in zip(connected, [real[c] for c in connected], null.mean(0), Z, SP):
if r > 0 or m > 0.5:
print(f"{c:5s} 真实 {r:5d} 随机均值 {m:7.1f} Z = {z:6.1f} SP = {sp:5.2f}")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6.4, 4.2))
ax.bar(connected, SP, color=["C3" if c == "030T" else "0.6" for c in connected])
ax.axhline(0, color="k", lw=0.8)
ax.set_xlabel("connected triad type (MAN code)"); ax.set_ylabel("significance profile SP = Z / ||Z||")
ax.tick_params(axis="x", rotation=45)
ax.set_title("Triad significance profile (red: 030T = feed-forward loop)")
plt.tight_layout(); plt.show()Run在浏览器中运行
首次加载 Python 环境约需 20–40 秒;可以修改参数后重新运行。