Pyvis添加大量边时hierarchical布局显示异常排查
pyvis三分网络分层布局失效问题
问题表现
- 仅向网络添加配置了
level层级属性的节点时,渲染效果符合分层预期 - 当网络规模达到约300个节点、700条边时,添加全部边后所有节点被压缩排列为一条直线,分层布局完全失效;节点、边数量较少时该问题不会出现
- 小规模场景可正常运行的测试代码如下:
from pyvis.network import Network import networkx as nx import matplotlib.pyplot as plt ddd = {'a', 'b', 'c'} ccc = {'p', 'q', 'r'} yyy = {'x', 'y', 'z'} edge_list_tuples = [('a', 'q'), ('b', 'p'), ('b', 'q'), ('p', 'y'), ('r', 'y'), ('r', 'z'), ('x', 'x')] g = nx.Graph() pyvs = Network(notebook=True, height = "100%", width = "100%", layout=True) g.add_nodes_from(yyy, bipartite="yy") g.add_nodes_from(ccc, bipartite="cc") g.add_nodes_from(ddd, bipartite="dd") g.add_edges_from(edge_list_tuples) color_map = nx.get_node_attributes(g, "bipartite") for key in color_map: if color_map[key] == "dd": color_map[key] = "yellow" lvl = 3 if color_map[key] == "cc": color_map[key] = "green" lvl = 2 if color_map[key] == "yy": color_map[key] = "blue" lvl = 1 pyvs.add_node(key, color=color_map[key], level=lvl) pyvs.add_edges(g.edges()) pyvs.show_buttons(filter_=['physics']) pyvs.show("output_network.html")
- 业务数据场景下可稳定复现问题的代码如下:
from pyvis.network import Network import networkx as nx import matplotlib.pyplot as plt import xlrd file = "./so-factors.xls" ddd = set() ccc = set() yyy = set() edge_list_tuples = [] book = xlrd.open_workbook(file) sheet1 = book.sheet_by_index(0) for row in range(sheet1.nrows-1): data = sheet1.row_slice(row+1) ddd.add(data[0].value) ccc.add(data[1].value) edge_list_tuples.append((data[0].value, data[1].value)) sheet2 = book.sheet_by_index(1) for row in range(sheet2.nrows-1): data = sheet2.row_slice(row+1) ccc.add(data[0].value) yyy.add(data[1].value) edge_list_tuples.append((data[0].value, data[1].value)) g = nx.Graph() pyvs = Network(notebook=True, height = "100%", width = "100%", layout=True) g.add_nodes_from(yyy, bipartite="yy") g.add_nodes_from(ccc, bipartite="cc") g.add_nodes_from(ddd, bipartite="dd") g.add_edges_from(edge_list_tuples) color_map = nx.get_node_attributes(g, "bipartite") for key in color_map: if color_map[key] == "dd": color_map[key] = "yellow" lvl = 3 if color_map[key] == "cc": color_map[key] = "green" lvl = 2 if color_map[key] == "yy": color_map[key] = "blue" lvl = 1 pyvs.add_node(key, color=color_map[key], level=lvl) pyvs.add_edges(g.edges()) pyvs.show_buttons(filter_=['physics']) pyvs.show("./output-gitignore/my_output_network.html")
问题根源
- 初始化时传入
layout=True仅会让pyvis在渲染初始阶段,按照节点的level属性分配y轴初始坐标,不会锁死节点的层级位置 - pyvis默认使用的
barnesHut物理求解器没有分层约束逻辑,当节点、边数量增多后,边的弹簧拉力、节点中心引力的总强度会盖过初始位置的偏移量,把所有节点往画布中心轴线拉扯,最终挤成一条直线 - 小规模网络下力的总强度低,初始层级坐标不会被完全拉平,因此看起来布局正常。
修复方案
在调用show()方法前追加如下配置,替换物理求解器、关闭会拉扯节点的中心引力、明确分层布局规则:
pyvs.set_options(""" { "layout": { "hierarchical": { "enabled": true, "levelSeparation": 180, "nodeSpacing": 100, "direction": "UD", "sortMethod": "directed" } }, "physics": { "hierarchicalRepulsion": { "centralGravity": 0, "springLength": 120, "springConstant": 0.01, "nodeDistance": 130, "damping": 0.1 }, "solver": "hierarchicalRepulsion" } } """)
配置说明:
- 显式开启分层布局,设置固定的层间距、同层节点间距
- 将默认的
barnesHut求解器替换为hierarchicalRepulsion,该求解器会严格遵守节点的level属性约束,不会跨层拉扯节点 - 将中心引力设为0,避免节点被往画布中心拖拽压缩
- 调低边的弹簧系数,降低边对节点位置的拉扯强度
- 上述参数中的间距、弹簧系数可以根据实际节点大小、显示需求微调,只要保持求解器为
hierarchicalRepulsion、中心引力为0,就不会出现节点被压成直线的问题。
内容的提问来源于stack exchange,提问作者Rys
相关产品推荐
相关产品推荐

