如何用Python创建带平行边的有向加权图并解决NetworkX可视化报错
问题描述
我正在开展一个项目,需要用Python创建支持节点间带不同权重平行边的有向加权图,目前使用networkx库和Matplotlib进行可视化。
我的目标:
- 创建带有平行边(同一节点间多条边)的有向图;
- 为这些边分配随机权重;
- 可视化该图并显示带权重的边标签。
我编写的代码:
import random import networkx as nx import matplotlib.pyplot as plt def create_graph(n_nodes, alpha = 0.5): G = nx.MultiDiGraph() G.add_nodes_from(range(n_nodes)) for i in range(n_nodes): for j in range(i+1,n_nodes): if random.random() < alpha: weight=random.randint(1,10) G.add_edge(i, j, weight=weight) if random.random() < alpha: weight=random.randint(1,10) G.add_edge(j, i, weight=weight) return G def display_graph(G): pos = nx.spring_layout(G) weight_labels = nx.get_edge_attributes(G, 'weight') nx.draw(G, pos, with_labels=True, node_color='skyblue', edge_color='gray', node_size=700) nx.draw_networkx_edge_labels(G, pos, edge_labels=weight_labels) plt.show() n_nodes = 5 G = create_graph(n_nodes, alpha = 0.5) display_graph(G)
运行时出现错误:
networkx.exception.NetworkXError: draw_networkx_edge_labels does not support multiedges.
我需要解决两个问题:
- 如何解决该错误,正确可视化带平行边的有向加权图?
- 在networkx中有没有更好的创建和可视化带平行边的有向图的方法?
解决方案
问题1:修复可视化边标签的错误
draw_networkx_edge_labels不支持MultiDiGraph的核心原因是它无法区分平行边的唯一键(key)。要解决这个问题,需要手动遍历每条包含键信息的边,计算标签位置并绘制。
修改后的display_graph函数如下:
def display_graph(G): pos = nx.spring_layout(G) # 绘制节点和边,用弧形显示平行边避免重叠 nx.draw(G, pos, with_labels=True, node_color='skyblue', edge_color='gray', node_size=700, connectionstyle='arc3,rad=0.1') # 手动绘制每条边的权重标签 for u, v, key, weight in G.edges(data='weight', keys=True): # 计算边的中间位置 x_u, y_u = pos[u] x_v, y_v = pos[v] x_mid = (x_u + x_v) / 2 y_mid = (y_u + y_v) / 2 # 根据边的键值添加偏移,避免平行边标签重叠 offset = 0.05 * key # 按边的方向调整偏移方向 if x_u < x_v: x_mid += offset else: x_mid -= offset if y_u < y_v: y_mid += offset else: y_mid -= offset # 绘制带背景的标签,提升可读性 plt.text(x_mid, y_mid, str(weight), fontsize=10, ha='center', va='center', bbox=dict(facecolor='white', edgecolor='none', alpha=0.7)) plt.show()
关键改动说明:
- 用
connectionstyle='arc3,rad=0.1'让平行边以弧形展示,避免直线重叠; - 遍历边时带上
keys=True,获取每条平行边的唯一标识; - 给不同键的标签设置不同偏移量,确保标签不重叠;
- 用
plt.text替代不支持多图的draw_networkx_edge_labels,手动绘制标签。
问题2:NetworkX中创建和可视化平行边有向图的优化方法
创建图的优化
你的create_graph函数逻辑没问题,可以简化为更清晰的写法,效果和原代码一致:
def create_graph(n_nodes, alpha=0.5): G = nx.MultiDiGraph() G.add_nodes_from(range(n_nodes)) # 生成所有有序节点对(i≠j) node_pairs = [(i,j) for i in range(n_nodes) for j in range(n_nodes) if i != j] for u, v in node_pairs: if random.random() < alpha: G.add_edge(u, v, weight=random.randint(1,10)) return G
可视化的进阶技巧
可以通过分组绘制平行边,给不同边设置不同颜色和弧度,提升区分度:
def display_graph_advanced(G): pos = nx.spring_layout(G, seed=42) # 固定种子保证布局稳定 # 绘制节点和节点标签 nx.draw_networkx_nodes(G, pos, node_color='skyblue', node_size=700) nx.draw_networkx_labels(G, pos, font_size=12) # 给不同键的平行边设置不同弧度和颜色 edge_rad_map = {0: 0.1, 1: -0.1, 2: 0.2} # 可根据实际边数调整 for key in set(k for _,_,k in G.edges): # 筛选当前键对应的所有边 edges = [(u, v) for u, v, k in G.edges if k == key] rad = edge_rad_map.get(key, 0.05 * key) # 绘制当前组的边 nx.draw_networkx_edges(G, pos, edgelist=edges, connectionstyle=f'arc3,rad={rad}', edge_color=f'C{key}', arrowsize=20) # 绘制当前组边的标签 for u, v, k, weight in G.edges(data='weight', keys=True): if k == key: x_mid = (pos[u][0] + pos[v][0])/2 + 0.08*rad y_mid = (pos[u][1] + pos[v][1])/2 + 0.08*rad plt.text(x_mid, y_mid, str(weight), fontsize=10, bbox=dict(facecolor='white', alpha=0.8)) plt.box(False) plt.show()
这个版本的优势:
- 固定布局种子,每次运行的图结构一致,方便调试;
- 不同平行边用不同颜色和弧度区分,视觉更清晰;
- 标签位置和边的弧度对应,排版更协调。
内容的提问来源于stack exchange,提问作者Nirmal Sankalana
相关产品推荐
相关产品推荐

