生物医学网络科学Biomedical Network Science
Example09 / 10

网络干预分析(NIA)——把"治疗分组"作为网络中的一个节点

所属专题
症状网络与网络精神病学
下载
Notebook · .py

模拟一项随机对照试验(模拟数据,仅用于演示方法):治疗只直接改善”睡眠问题”, 睡眠改善再通过”疲乏”间接改善”情绪低落”。治疗后把分组变量(0/1)与症状放在同一网络中估计偏相关。 输出解读:分组与三个症状的简单相关都不为 0(总效应),但在偏相关网络中, 只有”分组–睡眠问题”有明显的边——条件于其他症状后,治疗对疲乏与情绪的关联消失, 提示其效应可能经由睡眠传递。这是 Blanken 等 (2019) NIA 的基本逻辑; 横截面的单个时间点不能确定先后,原研究用逐周重复测量来看效应如何在网络中展开。

Output输出图

网络干预分析(NIA)——把"治疗分组"作为网络中的一个节点:输出图 1

Output文字输出

症状          与分组的相关     与分组的偏相关
睡眠问题           -0.37         -0.34
疲乏             -0.16          0.02
情绪低落           -0.11          0.03
担忧             -0.08         -0.01

Code代码

# 模拟一项随机对照试验(模拟数据,仅用于演示方法):治疗只直接改善"睡眠问题",
# 睡眠改善再通过"疲乏"间接改善"情绪低落"。治疗后把分组变量(0/1)与症状放在同一网络中估计偏相关。
# 输出解读:分组与三个症状的简单相关都不为 0(总效应),但在偏相关网络中,
# 只有"分组–睡眠问题"有明显的边——条件于其他症状后,治疗对疲乏与情绪的关联消失,
# 提示其效应可能经由睡眠传递。这是 Blanken 等 (2019) NIA 的基本逻辑;
# 横截面的单个时间点不能确定先后,原研究用逐周重复测量来看效应如何在网络中展开。
import numpy as np

rng = np.random.default_rng(5)
n = 600
treat = np.repeat([0, 1], n // 2)
sleep = 1.0 - 0.8 * treat + rng.standard_normal(n)
fatigue = 0.6 * sleep + rng.standard_normal(n)
mood = 0.5 * fatigue + 0.2 * sleep + rng.standard_normal(n)
worry = 0.4 * mood + rng.standard_normal(n)
names = ["分组(治疗=1)", "睡眠问题", "疲乏", "情绪低落", "担忧"]
X = np.column_stack([treat, sleep, fatigue, mood, worry])

R = np.corrcoef(X, rowvar=False)
K = np.linalg.inv(np.cov(X, rowvar=False))
d = np.sqrt(np.diag(K))
P = -K / np.outer(d, d)
print(f"{'症状':<8}{'与分组的相关':>10}{'与分组的偏相关':>12}")
for j in range(1, 5):
    print(f"{names[j]:<8}{R[0, j]:>12.2f}{P[0, j]:>14.2f}")

import networkx as nx
import matplotlib.pyplot as plt

def draw_signed(ax, M, labels, thr, title, node_color="#DDDDDD", node_size=180, pos=None):
    """边宽 ∝ |权重|,蓝色为正边、红色为负边。"""
    n = len(labels)
    pos = nx.circular_layout(range(n)) if pos is None else pos
    G = nx.Graph(); G.add_nodes_from(range(n))
    G.add_weighted_edges_from((i, j, M[i, j]) for i in range(n) for j in range(i + 1, n) if abs(M[i, j]) > thr)
    ws = [d["weight"] for _, _, d in G.edges(data=True)]
    nx.draw_networkx_edges(G, pos, ax=ax, width=[max(1.0, 8 * abs(w)) for w in ws],
                           edge_color=["#4477AA" if w > 0 else "#CC6677" for w in ws])
    nx.draw_networkx_nodes(G, pos, ax=ax, node_size=node_size, node_color=node_color, edgecolors="k")
    lab_pos = {k: (1.22 * np.asarray(v)) for k, v in pos.items()}
    nx.draw_networkx_labels(G, lab_pos, labels=dict(enumerate(labels)), ax=ax, font_size=9)
    ax.plot([], [], color="#4477AA", lw=2, label="positive")
    ax.plot([], [], color="#CC6677", lw=2, label="negative")
    ax.legend(loc="lower right", fontsize=7, frameon=False)
    ax.set_title(title, fontsize=10); ax.set_aspect("equal"); ax.axis("off")
    ax.set_xlim(-1.45, 1.45); ax.set_ylim(-1.45, 1.45)

abbr = ["Treat", "Slp", "Fat", "Sad", "Wry"]
fig, axes = plt.subplots(1, 2, figsize=(10, 4.2))
x = np.arange(1, 5)
axes[0].bar(x - 0.2, R[0, 1:], 0.4, color="#BBBBBB", label="correlation with treatment")
axes[0].bar(x + 0.2, P[0, 1:], 0.4, color="#4477AA", label="partial correlation")
axes[0].axhline(0, color="k", lw=0.6)
axes[0].set_xticks(x, abbr[1:])
axes[0].set(ylabel="Association with treatment", title="Total vs direct association")
axes[0].legend(fontsize=8)
P0 = P.copy(); np.fill_diagonal(P0, 0)
draw_signed(axes[1], P0, abbr, 0.1, "Partial correlation network (|pcor| > 0.1)",
            node_color=["#EE8866"] + ["#DDDDDD"] * 4)
plt.tight_layout(); plt.show()

Run在浏览器中运行

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

网络干预分析(NIA)——把"治疗分组"作为网络中的一个节点