如何在Python的NetworkX库中正确绘制有向非对称图的双向边(含不同权重)
如何在Python的NetworkX库中正确绘制有向非对称图的双向边(含不同权重)
我完全懂你的困扰——你明明已经把双向边的正确数据存进有向图里了,但可视化的时候两条边重叠成一条,只能看到一个权重,这确实挺闹心的。其实核心原因是NetworkX默认的绘图逻辑会把双向边画在同一条直线上,所以看起来就像只有一条边,但你的数据其实完全没问题(从你打印G[4][3]和G[3][4]的结果就能确认这一点)。
下面给你一个简单有效的解决方案,通过给双向边添加弯曲效果来分开它们,这样就能同时显示两条边的不同权重了:
修改后的完整代码
import networkx as nx import numpy as np import matplotlib.pyplot as plt # 用你提供的前10个城市数据作为示例矩阵 distance_matrix = np.array([ [0, np.inf, 1033.836, 2954.445, np.inf, 570.7902, np.inf, 5201.642, 927.6648, np.inf], [np.inf, 0, 846.4284, 2988.993, 1739.0, 586.6539, 556.8027, 4718.087, 992.3883, np.inf], [1065.751, np.inf, 0, 3713.848, 2328.803, 1293.154, 922.5469, 5968.72, 1660.567, 917.3977], [3093.118, 2721.323, 3738.992, 0, 1386.044, 2593.299, 2926.249, 2167.597, 2188.789, 2756.88], [1547.838, 1609.13, 2530.782, 1509.116, 0, 1071.186, np.inf, 3437.752, 624.6915, 1348.369], [541.9545, 603.5713, 1270.946, 2354.782, 1026.145, 0, 204.1122, 4501.91, 436.0267, 401.6861], [459.8273, 581.1262, 1058.695, 2634.319, 1264.167, np.inf, 0, np.inf, 717.3868, 300.6823], [4815.335, 5330.784, 5322.427, 2153.888, 3612.119, 4600.471, 5372.185, 0, 3998.066, 5173.963], [789.3162, 901.9897, 1795.072, 2139.049, 635.1849, 434.8522, 738.8418, 4598.177, 0, 775.1959], [794.9219, 244.2613, np.inf, 2964.827, 1463.854, 388.5041, 297.126, 5215.893, 845.9431, 0] ]) G = nx.DiGraph(directed=True) num_nodes = 10 # 保留你原来的加边逻辑,数据是对的 for i in range(num_nodes): for j in range(num_nodes): if (distance_matrix[i][j] != 0) and (distance_matrix[i][j] != np.inf): G.add_edge(i, j, weight=distance_matrix[i][j]) # 保留你用的随机布局(带seed保证一致性),也可以换成spring_layout试试 pos = nx.random_layout(G, seed=6) edge_labels = {key: round(value, 3) for key, value in nx.get_edge_attributes(G, "weight").items()} base_options = { 'node_color': 'lightblue', 'node_size': 100, 'width': 1, 'arrowstyle': '->', 'arrowsize': 12, } plt.figure(figsize=(10, 10)) # 拆分绘图步骤,精准控制每部分样式 # 1. 先画节点和节点标签 nx.draw_networkx_nodes(G, pos, node_color=base_options['node_color'], node_size=base_options['node_size']) nx.draw_networkx_labels(G, pos, font_size=8) # 2. 区分单向边和双向边,避免重复处理 bidirectional_edges = [(u, v) for u, v in G.edges() if u < v and (v, u) in G.edges()] unidirectional_edges = [edge for edge in G.edges() if not ((edge[0], edge[1]) in bidirectional_edges or (edge[1], edge[0]) in bidirectional_edges)] # 3. 画单向边(默认直线) nx.draw_networkx_edges(G, pos, edgelist=unidirectional_edges, width=base_options['width'], arrowstyle=base_options['arrowstyle'], arrowsize=base_options['arrowsize']) # 4. 画双向边,给两个方向设置相反的弯曲度,避免重叠 for u, v in bidirectional_edges: # u→v的边向右弯曲 nx.draw_networkx_edges(G, pos, edgelist=[(u, v)], width=base_options['width'], arrowstyle=base_options['arrowstyle'], arrowsize=base_options['arrowsize'], connectionstyle="arc3,rad=0.1") # v→u的边向左弯曲 nx.draw_networkx_edges(G, pos, edgelist=[(v, u)], width=base_options['width'], arrowstyle=base_options['arrowstyle'], arrowsize=base_options['arrowsize'], connectionstyle="arc3,rad=-0.1") # 5. 最后画边标签 nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=5) plt.title("Distance Matrix Visualized as Graph (With Bidirectional Edges)") plt.show()
关键修改点说明
- 拆分绘图流程:不再用
nx.draw_networkx一次性画完所有元素,而是拆分节点、单向边、双向边分别绘制,这样能精准控制双向边的样式。 - 弯曲的双向边:通过
connectionstyle="arc3,rad=±0.1"让双向边向相反方向弯曲,rad的值可以根据你的图的大小调整,数值越大弯曲程度越明显,正负号控制弯曲方向。 - 区分单向/双向边:提前筛选出双向边,避免重复处理,确保每条双向边的两个方向都能被单独设置样式。
这样修改后,你就能看到3→4和4→3的两条边分别带着自己的权重显示出来了,不会再重叠在一起~
备注:内容来源于stack exchange,提问作者Himanshu
相关产品推荐
相关产品推荐

