Example06 / 10
桥中心性——抑郁与焦虑症状之间的"桥"
构造一个两社团网络(抑郁 5 个症状、焦虑 5 个症状,模拟边权,仅用于演示方法), 按 Jones, Ma & McNally (2021) 的定义计算桥强度(与其他社团节点的边权绝对值之和) 与一步桥预期影响(保留符号),并与普通强度对比。 输出解读:普通强度最高的是”紧张不安”“情绪低落”,桥强度最高的却是”注意力差”“睡眠问题”(“疲乏”次之)—— 它们承担了两个症状群之间的大部分连接,这是共病网络解释中”桥症状”的含义。 有负的跨社团边时(如”自我评价低–易激惹”),桥 EI 与桥强度不同。 桥中心性依赖事先给定的社团划分;划分改变,桥症状也会改变。
Output输出图

Output文字输出
症状 社团 强度 桥强度 桥EI(1) 注意力差 焦虑 0.60 0.40 0.40 睡眠问题 抑郁 0.65 0.35 0.35 疲乏 抑郁 0.80 0.25 0.25 紧张不安 焦虑 0.85 0.20 0.20 过度担忧 焦虑 0.68 0.08 0.08 情绪低落 抑郁 0.83 0.08 0.08 易激惹 焦虑 0.35 0.05 -0.05 自我评价低 抑郁 0.45 0.05 -0.05 难以控制担忧 焦虑 0.75 0.00 0.00 兴趣减退 抑郁 0.60 0.00 0.00 桥强度位于前 20% 的症状(应用研究常用的经验界限,非统计检验): ['睡眠问题', '注意力差'] 按给定划分的模块度 Q = 0.277
Code代码
# 构造一个两社团网络(抑郁 5 个症状、焦虑 5 个症状,模拟边权,仅用于演示方法),
# 按 Jones, Ma & McNally (2021) 的定义计算桥强度(与其他社团节点的边权绝对值之和)
# 与一步桥预期影响(保留符号),并与普通强度对比。
# 输出解读:普通强度最高的是"紧张不安""情绪低落",桥强度最高的却是"注意力差""睡眠问题"("疲乏"次之)——
# 它们承担了两个症状群之间的大部分连接,这是共病网络解释中"桥症状"的含义。
# 有负的跨社团边时(如"自我评价低–易激惹"),桥 EI 与桥强度不同。
# 桥中心性依赖事先给定的社团划分;划分改变,桥症状也会改变。
import numpy as np
import networkx as nx
nodes = ["情绪低落", "兴趣减退", "自我评价低", "睡眠问题", "疲乏", # 抑郁
"过度担忧", "难以控制担忧", "紧张不安", "易激惹", "注意力差"] # 焦虑
community = np.array([0] * 5 + [1] * 5)
edges = {(0, 1): .35, (0, 2): .25, (1, 2): .15, (0, 4): .15, (3, 4): .30, (1, 4): .10,
(5, 6): .40, (5, 7): .20, (6, 7): .25, (7, 8): .20, (6, 9): .10, (8, 9): .10,
(3, 7): .20, (3, 9): .15, (4, 9): .25, (0, 5): .08, (2, 8): -.05}
W = np.zeros((10, 10))
for (i, j), w in edges.items():
W[i, j] = W[j, i] = w
strength = np.abs(W).sum(axis=1)
other = community[:, None] != community[None, :]
bridge_strength = (np.abs(W) * other).sum(axis=1)
bridge_ei1 = (W * other).sum(axis=1)
order = np.argsort(bridge_strength)[::-1]
print(f"{'症状':<8}{'社团':>4}{'强度':>8}{'桥强度':>8}{'桥EI(1)':>9}")
for i in order:
print(f"{nodes[i]:<8}{'抑郁' if community[i] == 0 else '焦虑':>4}{strength[i]:>8.2f}"
f"{bridge_strength[i]:>8.2f}{bridge_ei1[i]:>9.2f}")
cut = np.quantile(bridge_strength, 0.8)
print("桥强度位于前 20% 的症状(应用研究常用的经验界限,非统计检验):",
[nodes[i] for i in range(10) if bridge_strength[i] >= cut])
G = nx.from_numpy_array(np.abs(W))
comms = [set(np.where(community == c)[0]) for c in (0, 1)]
print(f"按给定划分的模块度 Q = {nx.community.modularity(G, comms, weight='weight'):.3f}")
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 = ["Sad", "Anh", "Wth", "Slp", "Fat", "Wry", "Ctl", "Nrv", "Irr", "Con"] # 前 5 个抑郁,后 5 个焦虑
fig, axes = plt.subplots(1, 2, figsize=(10, 4.4))
draw_signed(axes[0], W, abbr, 0, "Depression (orange) and anxiety (blue) symptoms",
node_color=np.where(community == 0, "#EE8866", "#77AADD"),
node_size=60 + 140 * bridge_strength / bridge_strength.max())
axes[0].text(0, -1.42, "node size = bridge strength", ha="center", fontsize=8)
x = np.arange(10)
axes[1].bar(x - 0.2, strength, 0.4, color="#BBBBBB", label="strength")
axes[1].bar(x + 0.2, bridge_strength, 0.4, color="#4477AA", label="bridge strength")
axes[1].set_xticks(x, abbr)
axes[1].set(ylabel="Sum of |edge weights|", title="Bridge symptoms differ from the most central ones")
axes[1].legend(fontsize=8)
plt.tight_layout(); plt.show()Run在浏览器中运行
首次加载 Python 环境约需 20–40 秒;可以修改参数后重新运行。