You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bokeh的from_networkx中为networkx spring_layout传入初始节点位置

用Pandas DataFrame定义图+在Bokeh中正确传入初始位置到spring_layout的解决方案

我之前也踩过这个位置格式的坑!其实核心就是搞清楚networkx.spring_layout需要的初始位置格式,以及怎么从Pandas DataFrame里把位置数据转成它要的结构。下面一步步给你讲清楚:

1. 先从DataFrame构建NetworkX图

首先我们用两个DataFrame分别存储节点信息(包括初始位置、大小、颜色等属性)和边信息:

import pandas as pd
import networkx as nx
from bokeh.plotting import figure, show, from_networkx
from bokeh.models import Circle, MultiLine, HoverTool

# 节点DataFrame:包含ID、初始位置x/y、节点大小、颜色
nodes_df = pd.DataFrame({
    'node_id': ['A', 'B', 'C', 'D', 'E'],
    'x': [10, 20, 30, 40, 25],
    'y': [5, 15, 5, 15, 25],
    'size': [20, 30, 25, 35, 40],
    'color': ['#ff4444', '#33b5e5', '#99cc00', '#ffbb33', '#aa66cc']
})

# 边DataFrame:包含起点和终点
edges_df = pd.DataFrame({
    'source': ['A', 'A', 'B', 'C', 'E'],
    'target': ['B', 'C', 'D', 'D', 'B']
})

然后把DataFrame的数据导入NetworkX图,同时保留所有节点属性:

G = nx.Graph()

# 逐个添加节点,把DataFrame里的属性一并传入
for _, row in nodes_df.iterrows():
    G.add_node(
        row['node_id'],
        x=row['x'],
        y=row['y'],
        size=row['size'],
        color=row['color']
    )

# 添加所有边
G.add_edges_from(edges_df[['source', 'target']].values)

2. 把初始位置转成spring_layout需要的格式

spring_layout的pos参数要求是一个字典:键是节点ID,值是(x坐标, y坐标)的元组。我们可以直接从NetworkX图的节点属性里提取这个字典:

# 从节点属性中提取初始位置,生成符合要求的字典
initial_pos = {node: (data['x'], data['y']) for node, data in G.nodes(data=True)}

这里要注意:字典的键必须和NetworkX中的节点ID完全匹配,不能有遗漏——如果某个节点不在这个字典里,spring_layout会自动给它分配随机初始位置。

3. 结合Bokeh绘制,传入初始位置到spring_layout

现在我们可以把初始位置传给spring_layout,让算法基于这个位置进行弹簧布局的调整;如果想完全固定节点位置,直接把initial_pos作为布局函数传入from_networkx即可。

情况1:基于初始位置进行弹簧布局调整

# 使用spring_layout,传入初始位置,还可以设置迭代次数、固定节点等参数
# iterations:布局迭代的次数,数值越大布局越稳定
# fixed:指定哪些节点保持初始位置不参与布局调整(比如fixed=['A', 'E'])
layout = nx.spring_layout(G, pos=initial_pos, iterations=150, fixed=None)

# 创建Bokeh绘图对象
p = figure(
    title="Spring Layout with Custom Initial Positions",
    x_range=(-5, 50),
    y_range=(-5, 35),
    tools="pan,wheel_zoom,reset"
)

# 把NetworkX图和布局传入Bokeh的from_networkx
graph_renderer = from_networkx(G, layout)

# 绑定节点属性到Bokeh的glyph
graph_renderer.node_renderer.glyph = Circle(size='size', fill_color='color', fill_alpha=0.8)
# 设置边的样式
graph_renderer.edge_renderer.glyph = MultiLine(line_color='#666666', line_alpha=0.6, line_width=2)

# 添加hover提示工具
hover = HoverTool(tooltips=[("Node ID", "@index")])
p.add_tools(hover)

# 把图添加到绘图对象并显示
p.renderers.append(graph_renderer)
show(p)

情况2:完全固定节点位置(不进行弹簧布局)

如果你不想让spring_layout调整位置,直接用你定义的初始位置,那么可以跳过spring_layout,直接把initial_pos作为布局传入from_networkx:

# 直接使用初始位置字典作为布局
graph_renderer = from_networkx(G, initial_pos)

常见坑点提醒

  • 节点ID必须一致:初始位置字典的键要和NetworkX图中的节点ID完全匹配,否则会出现位置错乱或随机位置的情况。
  • 注意坐标范围:Bokeh的x_range和y_range要覆盖你的节点初始位置,否则节点会被画出画布外。
  • fixed参数的使用:如果只想固定部分节点的位置,把这些节点ID放到fixed列表里,spring_layout会只调整其他节点的位置。

内容的提问来源于stack exchange,提问作者Docuemada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:55:03