如何用NetworkX和Pyvis实现有向图双向边的单箭头可视化
解决Pyvis绘制双向关系单条双向箭头的问题
要实现单向关系显示单箭头、双向关系显示单条双向箭头的效果,不需要用NetworkX的DiGraph添加两条边,而是可以通过无向图存储关系类型+Pyvis手动控制边箭头的方式实现,具体步骤如下:
核心思路
用NetworkX的无向图(Graph)存储所有角色关系,给每条边添加relation_type(区分单向/双向)和label(关系描述)属性,然后在Pyvis中遍历边,根据关系类型设置箭头样式:
- 单向关系:设置
arrows="to"(仅目标节点显示箭头) - 双向关系:设置
arrows="to,from"(两端都显示箭头)
代码实现
1. 准备关系数据CSV
假设你的CSV文件(love_actually_relations.csv)结构如下:
source,target,relation_type,label Mark,Juliet,one_way,爱慕 Mark,Peter,two_way,挚友 Peter,Juliet,two_way,已婚
2. 完整代码
import networkx as nx from pyvis.network import Network import pandas as pd # 加载关系数据 df = pd.read_csv("love_actually_relations.csv") # 构建NetworkX无向图,存储边的关系属性 G = nx.Graph() for _, row in df.iterrows(): G.add_edge( row["source"], row["target"], relation_type=row["relation_type"], label=row["label"] ) # 初始化Pyvis有向网络图 net = Network(directed=True, height="800px", width="100%") # 添加所有节点 net.add_nodes(G.nodes()) # 遍历边,设置箭头样式和关系标签 for u, v, attrs in G.edges(data=True): rel_type = attrs["relation_type"] rel_label = attrs["label"] if rel_type == "two_way": # 双向关系:添加带双向箭头的边 net.add_edge(u, v, label=rel_label, arrows="to,from") else: # 单向关系:添加带单向箭头的边 net.add_edge(u, v, label=rel_label, arrows="to") # 启用物理布局优化节点位置 net.barnes_hut() # 保存为可交互的HTML文件 net.write_html("love_actually_relations.html")
关键细节说明
- Pyvis的
arrows参数支持多种配置:"to"(目标箭头)、"from"(源箭头)、"to,from"(双向箭头) - 用无向图存储关系可以避免重复添加双向边,同时通过属性保留关系类型信息
- 启用
barnes_hut布局可以让节点分布更合理,避免重叠
备选方案(基于已有的DiGraph)
如果已经用DiGraph构建了包含双向边的图,可以先去重反向边,再修改箭头样式:
import networkx as nx from pyvis.network import Network # 假设已有的DiGraph G G = nx.DiGraph() # 示例添加边 G.add_edge("Mark", "Juliet", label="爱慕") G.add_edge("Mark", "Peter", label="挚友") G.add_edge("Peter", "Mark", label="挚友") G.add_edge("Peter", "Juliet", label="已婚") G.add_edge("Juliet", "Peter", label="已婚") # 找出并删除重复的双向边 processed = set() bidirectional_pairs = [] for u, v in G.edges(): if (v, u) in G.edges() and (u, v) not in processed and (v, u) not in processed: bidirectional_pairs.append((u, v)) G.remove_edge(v, u) processed.add((u, v)) # 转换为Pyvis网络并设置箭头 net = Network(directed=True) net.from_nx(G) # 给双向边设置双向箭头 for edge in net.edges: u, v = edge["from"], edge["to"] if (u, v) in bidirectional_pairs: edge["arrows"] = "to,from" net.write_html("love_actually_relations.html")
内容的提问来源于stack exchange,提问作者Lou
相关产品推荐
相关产品推荐

