Python图论与网络可视化——网络结构、路径分析与生物代谢通路

发布于:2025-06-08 ⋅ 阅读:(18) ⋅ 点赞:(0)

Python图论与网络可视化——网络结构、路径分析与生物代谢通路

本节将深入探索图论与网络的可视化技术,涵盖网络结构展示、最短路径动态演示及生物代谢通路分析。通过NetworkX构建网络模型,结合Matplotlib实现可视化,展现图论在多领域的应用价值。


一、前言:图论与网络的重要性

图论以**顶点(节点)边(连接)**为核心,构建复杂系统的抽象模型,广泛应用于计算机科学、社交网络、生物信息学等领域。其核心价值在于将实体关系转化为可计算、可分析的结构,例如:

  • 社交网络:用户为节点,关注关系为边
  • 代谢通路:代谢物为节点,酶催化反应为边
  • 交通网络:地点为节点,路线为边
图论应用
计算机科学
生命科学
工程系统
算法设计
数据结构
代谢网络
蛋白质互作
交通规划
电力网络

二、基本概念:图论基础

2.1 图的组成要素

通过可视化展示图的核心元素:

  • 顶点:表示实体(如用户、分子)
  • :表示关系(如连接、反应)
  • 权重:量化关系强度(如距离、反应速率)
  • :节点的连接数量
# 简化代码:核心功能说明
def plot_graph_elements():
    # 创建图,添加节点和边
    G = nx.Graph()
    G.add_nodes_from(..., attributes)
    G.add_edges_from(..., weights)
    
    # 可视化设置
    pos = 指定节点位置
    nx.draw_networkx_nodes(..., 按类型着色)
    nx.draw_networkx_edges(..., 按权重渲染)
    添加图例和文字说明

2.2 图的分类体系

根据结构特征分为:

  • 随机网络(Erdős-Rényi):边随机连接
  • 小世界网络(Watts-Strogatz):高聚类+短路径
  • 无标度网络(Barabási-Albert):幂律度分布
  • 二分图:节点分为两类,边仅跨类连接

2.3 关键分析指标

指标 物理意义 应用场景
度中心性 节点的直接影响力 社交网络关键节点识别
介数中心性 节点在最短路径中的枢纽作用 交通网络瓶颈分析
接近中心性 节点到其他节点的效率 应急响应中心选址

三、网络可视化与中心性分析

3.1 网络类型对比可视化

通过子图对比不同网络结构:

# 简化代码:网络类型可视化
def plot_network_types():
    创建随机网络、小世界网络等
    为每个网络设置布局并绘制
    添加标题和统一图例

3.2 中心性度量可视化

以空手道俱乐部网络为例,对比三种中心性:

  • 度中心性:节点大小反映连接数
  • 介数中心性:颜色深浅反映枢纽重要性
  • 接近中心性:高值节点位于网络核心
# 简化代码:中心性可视化
def plot_centrality_measures(G):
    计算三种中心性
    按中心性值设置节点大小和颜色
    分面板展示并添加颜色条

四、最短路径动态演示:Dijkstra算法

通过动画直观展示算法步骤:

  1. 初始化:起点设为黄色,其余为灰色
  2. 迭代过程:优先队列选择当前最短节点,更新邻居距离
  3. 路径高亮:红色标记最终路径,隐藏无效距离标签
# 简化代码:动画核心逻辑
def visualize_dijkstra(G, source, target):
    初始化距离和优先队列
    while 队列不为空:
        取出当前节点,标记为绿色
        更新邻居节点距离,标记为黄色
    重建路径并添加红色高亮帧
    定义动画更新函数,动态渲染节点和边

优化细节

  • 调整布局参数k=0.8/np.sqrt(len(G.nodes))避免节点重叠
  • 仅显示有限距离值(隐藏d=inf
  • 增加动画间隔至1秒,便于观察

五、跨学科案例:糖酵解代谢通路分析

5.1 网络构建

  • 节点类型
    • 底物(蓝色):Glucose
    • 中间产物(绿色):Glucose-6P, Fructose-6P
    • 酶(橙色):Hexokinase, Phosphofructokinase
  • 边类型
    • 底物→酶:消耗关系
    • 酶→产物:生成关系

5.2 关键分析

  • 最短路径:从葡萄糖到丙酮酸的代谢路径
  • 中心性分析
    • 介数中心性最高节点:ATP、关键酶
    • 连接度最高酶:Phosphoglycerate kinase(连接4个代谢物)
# 简化代码:代谢网络分析
def analyze_metabolic_network(G):
    计算最短路径并打印
    输出介数中心性前5节点
    提取高连接度酶并展示

六、成果与教学价值

生成文件说明

文件名 内容描述
graph_elements.png 图论基本元素可视化
network_types.png 四种网络类型对比
centrality_measures.png 中心性度量可视化
dijkstra_animation.gif 最短路径动态演示
metabolic_network.png 糖酵解通路网络
metabolic_pathway_animation.gif 代谢路径动态分析

6.1 图论基本元素图(graph_elements.png)

在这里插入图片描述

图形说明

  1. 核心元素展示

    • 顶点(节点)
      • 编号1-5的圆形节点,分别标注“顶点”“边”“权重”“度”“路径”
      • 颜色区分类型:浅蓝色(基本元素)、紫色(属性)、绿色(度量)、橙色(结构)
    • :带箭头的曲线,标注“连接”等关系,宽度反映权重大小(如1-2边权重3,线条最粗)
    • 权重:边旁数字(如1-3边权重2)
    • :隐含于节点连接数(如节点1连接3条边,度为3)
  2. 布局与设计

    • 采用自定义坐标布局(pos参数),节点分布于左上至右下,便于边的清晰展示
    • 图例位于右上角,标注节点类型对应的颜色
  3. 文字说明

    • 右侧文本框总结关键概念,使用星号列表(避免特殊符号字体问题)

6.2 网络类型对比图(network_types.png)

在这里插入图片描述

图形说明

  1. 四宫格布局

    • 随机网络(左上)
      • 节点随机连接,无明显中心节点,度分布均匀
      • 布局:弹簧布局(spring_layout),节点分散
    • 小世界网络(右上)
      • 局部聚类(如三角形结构)与长距离连接并存,模拟真实网络(如社交圈)
      • 布局:环形布局(circular_layout),凸显聚类特征
    • 无标度网络(左下)
      • 存在少数高连接度枢纽节点(如中心节点连接8条边),符合幂律分布
      • 布局:弹簧布局,枢纽节点居中
    • 二分图(右下)
      • 节点分为上下两层(用户与产品),边仅跨层连接,无同层边
      • 布局:分层布局,用户在上、产品在下
  2. 视觉统一

    • 所有子图使用浅蓝色节点、灰色边,节点大小统一为300像素
    • 标题字体大小14,子图间距适中(tight_layout

6.3 中心性度量图(centrality_measures.png)

在这里插入图片描述

图形说明

  1. 三面板对比

    • 度中心性(左)
      • 节点大小与连接数成正比,中心节点(如节点1)最大
      • 颜色映射:Blues色系,深色表示高度值
    • 介数中心性(中)
      • 节点颜色越深,作为最短路径枢纽的频率越高(如节点9为紫色,介数最高)
      • 应用场景:识别网络中的关键桥梁节点
    • 接近中心性(右)
      • 节点越大,到其他节点的平均距离越短(如节点0位于中心,接近中心性最高)
      • 颜色映射:Reds色系,浅色表示高效率
  2. 技术细节

    • 基于空手道俱乐部网络(34节点),布局统一为弹簧布局
    • 节点大小范围:500-5000像素,颜色条标注中心性值范围

6.4 最短路径动画(dijkstra_animation.gif)

在这里插入图片描述

动画说明

  1. 动态流程

    • 初始化(0秒):起点“A”为黄色,其余节点灰色,距离标签显示“d=inf”
    • 搜索阶段(1-4秒)
      • 黄色节点表示当前处理节点(如A→C→D→F)
      • 绿色节点表示已访问节点,距离标签更新为有限值(如C节点d=2)
    • 路径高亮(5-6秒)
      • 红色节点和边标注最短路径(A→C→D→F),路径长度6
  2. 优化细节

    • 节点间距调整:k=0.8/np.sqrt(len(G.nodes)),避免密集节点重叠
    • 标签优化:仅显示有效距离(隐藏“d=inf”),位置自适应调整(如右侧节点标签左移)
    • 动画参数:帧率1fps,每帧间隔1秒,清晰展示每一步更新

6.5 糖酵解代谢通路图(metabolic_network.png)

在这里插入图片描述

图形说明

  1. 节点类型与颜色

    • 底物(浅蓝色):Glucose(起点)
    • 中间产物(浅绿色):如Glucose-6P、Fructose-1,6BP
    • 酶(橙色圆形):如Hexokinase、Phosphofructokinase
    • 辅因子(金色):ATP、ADP、NAD+
    • 产物(鲑红色):Pyruvate(终点)
  2. 边与反应

    • 有向边表示反应方向:
      • 底物→酶:消耗关系(如Glucose→Hexokinase)
      • 酶→产物:生成关系(如Hexokinase→Glucose-6P)
    • 边标签:“consumed_by”“produced_by”
  3. 布局与注释

    • 弹簧布局(k=0.5),酶节点居中,代谢物围绕分布
    • 左侧文本框标注9个关键步骤(如葡萄糖磷酸化、丙酮酸生成)
    • 右侧文本框总结生物学意义:净产2 ATP、生成NADH

6.6 代谢路径动画(metabolic_pathway_animation.gif)

在这里插入图片描述

动画说明

  1. 路径演示

    • 起点“Glucose”→终点“Pyruvate”
    • 最短路径:Glucose→Hexokinase→ADP→Pyruvate kinase→Pyruvate
    • 红色高亮节点和边,路径长度4
  2. 动态特征

    • 酶节点(Hexokinase、Pyruvate kinase)作为中间枢纽
    • 距离标签显示代谢物转化步数,隐藏无效节点标签
    • 最终帧突出显示产物Pyruvate和能量分子ATP

使用建议

  1. 图片排版

    • 动画GIF可嵌入文档正文,静态图片以居中方式排列
    • 每个图片下方添加带编号的标题和说明,如“图1 图论基本元素示意图”
  2. 对比分析

    • 结合网络类型图,对比随机网络与无标度网络的度分布差异
    • 通过中心性图,解释为何介数中心性高的节点适合作为网络控制枢纽
  3. 教学应用

    • 代谢通路图可配合生物课程,讲解糖酵解的酶催化机制
    • 最短路径动画可用于算法课,动态拆解Dijkstra算法的贪心策略

需要调整说明内容或补充特定细节,请随时告知!

教学要点

  1. 图论基础:理解顶点、边、权重的数学定义
  2. 网络分析:掌握不同网络类型的结构特征与中心性计算
  3. 算法实现:Dijkstra算法的动态演示与优化技巧
  4. 跨学科应用:图论在生物代谢通路中的建模与分析方法

通过可视化与算法结合,图论成为连接数学理论与实际问题的桥梁,尤其在复杂系统分析中展现出强大的建模能力。

七、完整代码(可直接运行)

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.animation as animation
from matplotlib.colors import ListedColormap
from queue import PriorityQueue
from numba import njit

# 设置中文字体
plt.rcParams["font.sans-serif"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 正确显示负号
plt.rcParams["mathtext.fontset"] = "cm"  # 设置数学字体


def plot_graph_elements(output_path="graph_elements.png"):
    """可视化图的基本元素"""
    plt.figure(figsize=(12, 8), dpi=130)

    # 创建图
    G = nx.Graph()

    # 添加节点
    G.add_nodes_from(
        [
            (1, {"label": "顶点", "type": "基本元素"}),
            (2, {"label": "边", "type": "基本元素"}),
            (3, {"label": "权重", "type": "属性"}),
            (4, {"label": "度", "type": "度量"}),
            (5, {"label": "路径", "type": "结构"}),
        ]
    )

    # 添加边
    G.add_edges_from(
        [
            (1, 2, {"weight": 3, "label": "连接"}),
            (1, 3, {"weight": 2}),
            (1, 4, {"weight": 1}),
            (2, 5, {"weight": 4}),
            (3, 4, {"weight": 2}),
            (4, 5, {"weight": 3}),
        ]
    )

    # 节点位置
    pos = {1: (0.2, 0.8), 2: (0.4, 0.6), 3: (0.1, 0.4), 4: (0.3, 0.2), 5: (0.6, 0.4)}

    # 绘制节点
    node_types = set(nx.get_node_attributes(G, "type").values())
    colors = plt.cm.tab10(range(len(node_types)))
    type_color_map = {t: colors[i] for i, t in enumerate(node_types)}

    node_colors = [type_color_map[G.nodes[n]["type"]] for n in G.nodes]

    nx.draw_networkx_nodes(G, pos, node_size=1500, node_color=node_colors, alpha=0.8)

    # 绘制边
    edge_weights = [G.edges[e]["weight"] for e in G.edges]
    nx.draw_networkx_edges(
        G, pos, width=3, alpha=0.6, edge_color=edge_weights, edge_cmap=plt.cm.Blues
    )

    # 绘制标签
    node_labels = nx.get_node_attributes(G, "label")
    nx.draw_networkx_labels(
        G, pos, labels=node_labels, font_size=12, font_weight="bold"
    )

    edge_labels = nx.get_edge_attributes(G, "label")
    nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=10)

    # 添加图例
    for t, color in type_color_map.items():
        plt.scatter([], [], c=[color], s=200, label=t, alpha=0.8)
    plt.legend(loc="upper right", title="节点类型")

    # 设置标题
    plt.title("图的基本元素与属性", fontsize=18, pad=20)
    plt.axis("off")

    # 添加概念说明(使用*替代•)
    plt.text(
        0.7,
        0.9,
        "关键概念:\n"
        "* 顶点: 实体对象\n"
        "* 边: 实体间关系\n"
        "* 权重: 关系强度\n"
        "* 度: 节点连接数\n"
        "* 路径: 节点序列",
        transform=plt.gca().transAxes,
        fontsize=12,
        bbox=dict(facecolor="white", alpha=0.8),
    )

    plt.savefig(output_path, bbox_inches="tight")
    plt.close()
    print(f"图论基本元素图已保存至: {output_path}")


def plot_network_types(output_path="network_types.png"):
    """可视化不同网络类型"""
    plt.figure(figsize=(15, 5), dpi=130)

    # 创建不同类型的网络
    networks = {
        "随机网络": nx.gnp_random_graph(15, 0.2, seed=42),
        "小世界网络": nx.watts_strogatz_graph(15, 4, 0.3, seed=42),
        "无标度网络": nx.barabasi_albert_graph(15, 2, seed=42),
        "完全网络": nx.complete_graph(15),
    }

    for i, (name, G) in enumerate(networks.items()):
        plt.subplot(1, len(networks), i + 1)
        pos = nx.spring_layout(G, seed=42)
        nx.draw(
            G,
            pos,
            with_labels=True,
            node_color="skyblue",
            node_size=300,
            font_size=9,
            font_weight="bold",
            edge_color="gray",
        )
        plt.title(name, fontsize=14)
        plt.axis("off")

    plt.tight_layout()
    plt.savefig(output_path, bbox_inches="tight")
    plt.close()
    print(f"网络类型图已保存至: {output_path}")


def plot_centrality_measures(G, output_path="centrality_measures.png"):
    """可视化不同中心性度量"""
    plt.figure(figsize=(16, 12), dpi=130)

    # 计算不同的中心性
    degree_centrality = nx.degree_centrality(G)
    betweenness_centrality = nx.betweenness_centrality(G)
    closeness_centrality = nx.closeness_centrality(G)
    eigenvector_centrality = nx.eigenvector_centrality(G)

    centrality_measures = {
        "度中心性": degree_centrality,
        "介数中心性": betweenness_centrality,
        "接近中心性": closeness_centrality,
        "特征向量中心性": eigenvector_centrality,
    }

    pos = nx.spring_layout(G, seed=42)

    for i, (name, centrality) in enumerate(centrality_measures.items(), 1):
        plt.subplot(2, 2, i)
        node_size = [v * 2000 for v in centrality.values()]
        node_color = list(centrality.values())

        # 保存节点绘制的返回值,用于创建colorbar
        nodes = nx.draw_networkx_nodes(
            G,
            pos,
            node_size=node_size,
            cmap=plt.cm.viridis,
            node_color=node_color,
            alpha=0.8,
        )
        nx.draw_networkx_edges(G, pos, alpha=0.5)
        nx.draw_networkx_labels(G, pos, font_size=9)

        plt.title(f"{name}", fontsize=14)
        plt.axis("off")

        # 使用保存的nodes对象创建colorbar
        plt.colorbar(nodes, label="中心性值")

    plt.tight_layout()
    plt.savefig(output_path, bbox_inches="tight")
    plt.close()
    print(f"中心性度量图已保存至: {output_path}")


@njit
def compute_mandelbrot(width, height, x_min, x_max, y_min, y_max, max_iter):
    """使用Numba加速计算Mandelbrot集合"""
    # 创建结果数组
    iterations = np.zeros((height, width), dtype=np.int32)

    # 计算每个像素对应的复平面坐标
    for y in range(height):
        for x in range(width):
            # 将像素坐标映射到复平面
            real = x_min + (x / (width - 1)) * (x_max - x_min)
            imag = y_min + (y / (height - 1)) * (y_max - y_min)

            # 初始化复数c和z
            c = complex(real, imag)
            z = complex(0, 0)

            # 迭代计算
            for i in range(max_iter):
                if abs(z) > 2.0:
                    iterations[y, x] = i
                    break
                z = z * z + c
            else:
                iterations[y, x] = max_iter

    return iterations


def plot_mandelbrot(
    output_path="mandelbrot.png",
    width=800,
    height=600,
    x_min=-2.0,
    x_max=1.0,
    y_min=-1.5,
    y_max=1.5,
    max_iter=100,
):
    """绘制Mandelbrot集合"""
    # 计算Mandelbrot集合
    iterations = compute_mandelbrot(width, height, x_min, x_max, y_min, y_max, max_iter)

    # 创建自定义颜色映射
    colors = [
        (0, 0, 0),
        (0.1, 0.1, 0.3),
        (0.2, 0.2, 0.6),
        (0.4, 0.4, 0.8),
        (0.6, 0.6, 1.0),
        (0.8, 0.8, 1.0),
        (1.0, 1.0, 1.0),
    ]
    cmap = LinearSegmentedColormap.from_list("mandelbrot_cmap", colors, N=max_iter)

    # 绘制图像
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(
        iterations,
        cmap=cmap,
        extent=[x_min, x_max, y_min, y_max],
        interpolation="bilinear",
    )
    plt.colorbar(label="迭代次数")
    plt.title("Mandelbrot集合", fontsize=16)
    plt.xlabel("实部", fontsize=12)
    plt.ylabel("虚部", fontsize=12)

    plt.savefig(output_path, bbox_inches="tight")
    plt.close()
    print(f"Mandelbrot集合图已保存至: {output_path}")


def visualize_dijkstra(
    G, source, target, output_path="dijkstra_animation.gif", node_size=500
):
    """
    Dijkstra算法最短路径动态演示

    参数:
    G: 网络图
    source: 起点
    target: 终点
    output_path: 输出文件路径
    node_size: 节点大小,默认为500
    """
    # 初始化
    dist = {node: float("infinity") for node in G.nodes}
    dist[source] = 0
    previous = {node: None for node in G.nodes}
    visited = set()

    # 使用优先队列
    queue = PriorityQueue()
    queue.put((0, source))

    # 节点位置 - 使用更适合密集网络的布局算法
    pos = nx.spring_layout(
        G, seed=42, k=0.8 / np.sqrt(len(G.nodes))
    )  # 调整k值增加节点间距

    # 创建图形
    fig, ax = plt.subplots(figsize=(12, 10), dpi=150)  # 增加图形尺寸

    # 颜色映射
    cmap = ListedColormap(["lightgray", "yellow", "green", "red"])

    # 初始绘图
    node_colors = {node: "lightgray" for node in G.nodes}
    node_colors[source] = "yellow"

    # 绘制节点和边
    nodes = nx.draw_networkx_nodes(
        G,
        pos,
        node_color=list(node_colors.values()),
        node_size=node_size,
        alpha=0.8,
        ax=ax,
    )
    edges = nx.draw_networkx_edges(G, pos, edge_color="gray", alpha=0.5, ax=ax)
    labels = nx.draw_networkx_labels(G, pos, font_size=9, ax=ax)

    # 初始化距离标签 - 优化显示位置
    dist_labels = {}
    for node in G.nodes:
        x, y = pos[node]
        # 根据节点周围空间调整标签位置
        label_x = x + 0.05 if x < 0.5 else x - 0.05
        label_y = y + 0.05 if y < 0.5 else y - 0.05

        dist_labels[node] = ax.text(
            label_x,
            label_y,
            f"d={dist[node]}" if dist[node] != float("inf") else "",
            ha="center",
            va="center",
            fontsize=8,
            bbox=dict(facecolor="white", alpha=0.7, boxstyle="round,pad=0.1"),
        )

    ax.set_title("Dijkstra算法: 寻找最短路径", fontsize=16)
    ax.set_axis_off()

    # 收集帧数据
    frames = []
    frames.append(
        {"node_colors": node_colors.copy(), "dist": dist.copy(), "edge_colors": None}
    )

    # Dijkstra算法执行过程
    while not queue.empty():
        current_dist, current_node = queue.get()
        if current_node in visited:
            continue
        visited.add(current_node)
        node_colors[current_node] = "green"
        if current_node == target:
            break
        for neighbor in G.neighbors(current_node):
            if neighbor in visited:
                continue
            weight = G.edges[current_node, neighbor].get("weight", 1)
            new_dist = current_dist + weight
            if new_dist < dist[neighbor]:
                dist[neighbor] = new_dist
                previous[neighbor] = current_node
                queue.put((new_dist, neighbor))
                node_colors[neighbor] = "yellow"
        frames.append(
            {
                "node_colors": node_colors.copy(),
                "dist": dist.copy(),
                "edge_colors": None,
            }
        )

    # 重建路径
    path = []
    current = target
    while current is not None:
        path.append(current)
        current = previous[current]
    path.reverse()

    # 添加路径高亮帧
    for i in range(1, len(path) + 1):
        path_segment = path[:i]
        path_colors = node_colors.copy()
        for node in path_segment:
            path_colors[node] = "red"
        edge_colors = [
            (
                "red"
                if (
                    u in path_segment
                    and v in path_segment
                    and abs(path_segment.index(u) - path_segment.index(v)) == 1
                )
                else "gray"
            )
            for u, v in G.edges
        ]
        frames.append(
            {
                "node_colors": path_colors,
                "dist": dist.copy(),
                "edge_colors": edge_colors,
            }
        )

    # 动画更新函数
    def update(frame_idx):
        ax.clear()
        frame = frames[frame_idx]

        # 绘制节点和边
        node_colors_frame = frame["node_colors"]
        nodes = nx.draw_networkx_nodes(
            G,
            pos,
            node_color=list(node_colors_frame.values()),
            node_size=node_size,
            alpha=0.8,
            ax=ax,
        )

        edge_colors = frame["edge_colors"] or "gray"
        edges = nx.draw_networkx_edges(G, pos, edge_color=edge_colors, alpha=0.5, ax=ax)

        labels = nx.draw_networkx_labels(G, pos, font_size=9, ax=ax)

        # 更新距离标签 - 只显示有限值
        for node in G.nodes:
            dist_val = frame["dist"].get(node, float("inf"))
            if dist_val != float("inf"):  # 只显示有限距离值
                dist_labels[node].set_text(f"d={dist_val}")
                ax.add_artist(dist_labels[node])
            else:
                dist_labels[node].set_text("")  # 隐藏无限大值

        # 设置标题
        status = f"当前节点: {list(G.nodes)[frame_idx % len(G.nodes)]}"
        ax.set_title(f"Dijkstra算法: {status}", fontsize=16)
        ax.set_axis_off()

        return ax.get_children()

    # 创建动画
    anim = animation.FuncAnimation(
        fig, update, frames=len(frames), interval=1000, blit=False
    )

    # 保存GIF
    anim.save(output_path, writer="pillow", fps=1)
    plt.close()
    print(f"最短路径动画已保存至: {output_path}")

    return path, dist[target]


def metabolic_pathway_network(output_path="metabolic_network.png"):
    """构建并可视化代谢通路网络"""
    # 创建代谢网络
    G = nx.DiGraph()

    # 添加代谢物节点
    metabolites = {
        "Glucose": {"type": "substrate", "color": "skyblue"},
        "Glucose-6P": {"type": "intermediate", "color": "lightgreen"},
        "Fructose-6P": {"type": "intermediate", "color": "lightgreen"},
        "Fructose-1,6BP": {"type": "intermediate", "color": "lightgreen"},
        "GAP": {"type": "intermediate", "color": "lightgreen"},
        "DHAP": {"type": "intermediate", "color": "lightgreen"},
        "1,3-BPG": {"type": "intermediate", "color": "lightgreen"},
        "3PG": {"type": "intermediate", "color": "lightgreen"},
        "2PG": {"type": "intermediate", "color": "lightgreen"},
        "PEP": {"type": "intermediate", "color": "lightgreen"},
        "Pyruvate": {"type": "product", "color": "salmon"},
        "ATP": {"type": "cofactor", "color": "gold"},
        "ADP": {"type": "cofactor", "color": "gold"},
        "NAD+": {"type": "cofactor", "color": "violet"},
        "NADH": {"type": "cofactor", "color": "violet"},
        "Pi": {"type": "cofactor", "color": "lightgray"},
        "H2O": {"type": "cofactor", "color": "lightblue"},
    }

    for met, attrs in metabolites.items():
        G.add_node(met, **attrs)

    # 添加反应边
    reactions = [
        # 糖酵解途径
        {
            "substrates": ["Glucose", "ATP"],
            "products": ["Glucose-6P", "ADP"],
            "enzyme": "Hexokinase",
        },
        {
            "substrates": ["Glucose-6P"],
            "products": ["Fructose-6P"],
            "enzyme": "Phosphoglucoisomerase",
        },
        {
            "substrates": ["Fructose-6P", "ATP"],
            "products": ["Fructose-1,6BP", "ADP"],
            "enzyme": "Phosphofructokinase",
        },
        {
            "substrates": ["Fructose-1,6BP"],
            "products": ["GAP", "DHAP"],
            "enzyme": "Aldolase",
        },
        {
            "substrates": ["DHAP"],
            "products": ["GAP"],
            "enzyme": "Triose phosphate isomerase",
        },
        {
            "substrates": ["GAP", "Pi", "NAD+"],
            "products": ["1,3-BPG", "NADH", "H+"],
            "enzyme": "Glyceraldehyde-3-P dehydrogenase",
        },
        {
            "substrates": ["1,3-BPG", "ADP"],
            "products": ["3PG", "ATP"],
            "enzyme": "Phosphoglycerate kinase",
        },
        {
            "substrates": ["3PG"],
            "products": ["2PG"],
            "enzyme": "Phosphoglycerate mutase",
        },
        {"substrates": ["2PG"], "products": ["PEP", "H2O"], "enzyme": "Enolase"},
        {
            "substrates": ["PEP", "ADP"],
            "products": ["Pyruvate", "ATP"],
            "enzyme": "Pyruvate kinase",
        },
    ]

    # 添加边
    for rxn in reactions:
        enzyme = rxn["enzyme"]
        # 确保酶节点有type属性
        G.add_node(enzyme, type="enzyme", color="orange")

        # 底物到酶
        for sub in rxn["substrates"]:
            G.add_edge(sub, enzyme, weight=1, label="consumed_by")

        # 酶到产物
        for prod in rxn["products"]:
            G.add_edge(enzyme, prod, weight=1, label="produced_by")

    # 设置布局
    pos = nx.spring_layout(G, seed=42, k=0.5)

    # 创建图像
    plt.figure(figsize=(16, 12), dpi=150)

    # 按类型绘制节点
    # 先获取所有节点的type属性,确保不包含None
    node_types = set()
    for _, attrs in G.nodes(data=True):
        if "type" in attrs:
            node_types.add(attrs["type"])

    type_colors = {
        "substrate": "skyblue",
        "intermediate": "lightgreen",
        "product": "salmon",
        "cofactor": "gold",
        "enzyme": "orange",
    }

    for node_type in node_types:
        # 使用get方法获取type属性,默认为None
        nodes = [n for n, attrs in G.nodes(data=True) if attrs.get("type") == node_type]
        if nodes:  # 确保有节点属于该类型
            nx.draw_networkx_nodes(
                G,
                pos,
                nodelist=nodes,
                node_color=type_colors.get(node_type, "gray"),  # 默认灰色
                node_size=1500,
                alpha=0.8,
                label=node_type.capitalize(),
            )

    # 绘制边
    nx.draw_networkx_edges(G, pos, width=1.5, alpha=0.6, edge_color="gray")

    # 绘制节点标签
    nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold")

    # 绘制酶反应标签
    enzyme_edges = [
        e
        for e in G.edges
        if G.nodes[e[0]].get("type") == "enzyme"
        or G.nodes[e[1]].get("type") == "enzyme"
    ]
    enzyme_labels = {
        (u, v): G.nodes[u].get("type") == "enzyme"
        and G.nodes[v].get("type") != "enzyme"
        for u, v in enzyme_edges
    }

    # 添加图例
    plt.legend(loc="upper right", title="节点类型", fontsize=12)

    # 设置标题
    plt.title("糖酵解代谢通路网络", fontsize=20, pad=20)
    plt.axis("off")

    # 添加代谢通路说明
    plt.text(
        0.05,
        0.05,
        "糖酵解关键步骤:\n"
        "1. 葡萄糖磷酸化\n"
        "2. 果糖-6-磷酸生成\n"
        "3. 果糖-1,6-二磷酸生成\n"
        "4. 甘油醛-3-磷酸生成\n"
        "5. 1,3-二磷酸甘油酸生成\n"
        "6. 3-磷酸甘油酸生成\n"
        "7. 2-磷酸甘油酸生成\n"
        "8. 磷酸烯醇式丙酮酸生成\n"
        "9. 丙酮酸生成",
        transform=plt.gca().transAxes,
        fontsize=12,
        bbox=dict(facecolor="white", alpha=0.8),
    )

    # 添加生物学意义
    plt.text(
        0.7,
        0.05,
        "生物学意义:\n"
        "* 能量产生: 净生成2 ATP\n"
        "* 前体提供: 为其他通路提供中间产物\n"
        "* 氧化还原: 产生2 NADH",
        transform=plt.gca().transAxes,
        fontsize=12,
        bbox=dict(facecolor="white", alpha=0.8),
    )

    plt.savefig(output_path, bbox_inches="tight")
    plt.close()
    print(f"代谢通路网络图已保存至: {output_path}")

    return G


def analyze_metabolic_network(G, source="Glucose", target="Pyruvate"):
    """分析代谢网络的关键路径"""
    # 计算最短路径
    path, length = visualize_dijkstra(
        G, source, target, output_path="metabolic_pathway_animation.gif"
    )

    print(f"从 {source}{target} 的最短路径:")
    print(" -> ".join(path))
    print(f"路径长度: {length}")

    # 计算中心性
    betweenness = nx.betweenness_centrality(G)
    top_nodes = sorted(betweenness.items(), key=lambda x: x[1], reverse=True)[:5]

    print("\n介数中心性最高的节点:")
    for node, score in top_nodes:
        print(f"{node}: {score:.4f}")

    # 关键酶分析 - 安全获取酶节点
    enzymes = [n for n, attrs in G.nodes(data=True) if attrs.get("type") == "enzyme"]
    if not enzymes:
        print("\n未找到酶节点!")
        return

    enzyme_degrees = {e: G.degree(e) for e in enzymes}
    top_enzymes = sorted(enzyme_degrees.items(), key=lambda x: x[1], reverse=True)[:3]

    print("\n连接度最高的酶:")
    for enzyme, degree in top_enzymes:
        print(f"{enzyme}: 连接{degree}个代谢物")


if __name__ == "__main__":
    # 1. 图论基本元素
    plot_graph_elements(output_path="graph_elements.png")

    # 2. 网络类型可视化
    plot_network_types(output_path="network_types.png")

    # 3. 中心性度量
    G_example = nx.karate_club_graph()
    plot_centrality_measures(G_example, output_path="centrality_measures.png")

    # 4. 最短路径演示
    # 创建带权图
    G_weighted = nx.Graph()
    G_weighted.add_edges_from(
        [
            ("A", "B", {"weight": 4}),
            ("A", "C", {"weight": 2}),
            ("B", "D", {"weight": 5}),
            ("C", "D", {"weight": 1}),
            ("C", "E", {"weight": 3}),
            ("D", "F", {"weight": 2}),
            ("E", "F", {"weight": 6}),
        ]
    )

    visualize_dijkstra(G_weighted, "A", "F", output_path="dijkstra_animation.gif")

    # 5. 代谢通路网络
    metabolic_G = metabolic_pathway_network(output_path="metabolic_network.png")
    analyze_metabolic_network(metabolic_G, source="Glucose", target="Pyruvate")


网站公告

今日签到

点亮在社区的每一天
去签到