NetworkX中connectionstyle=arc3绘制环形图出现弧反转问题求助
解决NetworkX环形网络图中弧线边反转的问题
问题描述
用NetworkX绘制节点按环形排列的网络图,设置connectionstyle="arc3,rad=.5"让边显示为弧线,但发现像10→1这类从大数指向小数的边,弧线会反转(与其他边的弯曲方向相反)。已固定节点的环形位置,调整边的连接列表后,反转问题仅转移到其他跨首尾的边上。需要让程序识别环形闭环特性,统一所有弧线的弯曲方向。
原代码
import random from math import cos, sin import matplotlib import networkx as nx from matplotlib import pyplot as plt from pyvis.network import Network import scipy as sp def get_coordinates_in_circle(n, scale): return_list = [] for i in range(n): theta = float(i) / n * 2 * 3.141592654 y = cos(theta) x = sin(theta) return_list.append((x * scale, y * scale)) # print(return_list) return return_list def draw_number(length): """determines a random index number for selection.""" from_index = random.randint(0, length) to_index = random.randint(0, length) return from_index, to_index def netw(NodesN): G = nx.Graph() node_list = [] fixed_positions = {} # dict with two of the positions set for i in range(1, NodesN + 1): node_list.append(i) CirclePos = get_coordinates_in_circle(NodesN, 1) # jump by 1 from_list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] to_list1 = [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] # jump by 2 from_list2 = [1,3,5,7,9] to_list2 = [3,5,7,9,1] # jump by 3 from_list3 = [1,4,7,10,3 ] to_list3 = [4,7,10,3,6] from_list = from_list1 to_list = to_list1 for i in range(len(node_list)): G.add_node(node_list[i]) fixed_positions[i+1] = CirclePos[i] for j in range(len(from_list)): G.add_edges_from([(from_list[j], to_list[j])]) fixed_nodes = fixed_positions.keys() pos = nx.spring_layout(G, pos=fixed_positions, fixed=fixed_nodes) # pos = nx.kamada_kawai_layout(G, pos=fixed_positions) nx.draw_networkx_nodes(G,pos) nx.draw_networkx_labels(G,pos) nx.draw_networkx_edges(G,pos,arrows=True,connectionstyle=f"arc3,rad=.5") # nx.draw_networkx(G,pos) plt.show() netw(10)
问题原因
NetworkX的arc3连接样式基于两点的直线连线计算弧的弯曲方向,无法自动识别环形的闭环结构。对于跨首尾的节点(如10→1),两点的直线连线会穿过环形中心,导致弧线向与其他边相反的方向弯曲。
解决方案
通过手动计算每条边的弯曲方向,基于节点在环形上的顺时针/逆时针位置动态调整rad参数的正负,确保所有弧线向同一侧(如环形外侧)弯曲。
修改后的代码
import random from math import cos, sin import matplotlib import networkx as nx from matplotlib import pyplot as plt from pyvis.network import Network import scipy as sp def get_coordinates_in_circle(n, scale): return_list = [] for i in range(n): theta = float(i) / n * 2 * 3.141592654 y = cos(theta) x = sin(theta) return_list.append((x * scale, y * scale)) return return_list def draw_number(length): """determines a random index number for selection.""" from_index = random.randint(0, length) to_index = random.randint(0, length) return from_index, to_index def netw(NodesN): G = nx.DiGraph() # 改为有向图更符合箭头需求,修正原代码的逻辑矛盾 node_list = [] fixed_positions = {} for i in range(1, NodesN + 1): node_list.append(i) CirclePos = get_coordinates_in_circle(NodesN, 1) # jump by 1 from_list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] to_list1 = [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] # jump by 2 from_list2 = [1,3,5,7,9] to_list2 = [3,5,7,9,1] # jump by 3 from_list3 = [1,4,7,10,3 ] to_list3 = [4,7,10,3,6] from_list = from_list1 to_list = to_list1 for i in range(len(node_list)): G.add_node(node_list[i]) fixed_positions[i+1] = CirclePos[i] for j in range(len(from_list)): G.add_edge(from_list[j], to_list[j]) # 用add_edge简化代码 # 直接使用固定位置,避免spring_layout的不必要微调 pos = fixed_positions nx.draw_networkx_nodes(G, pos) nx.draw_networkx_labels(G, pos) # 动态计算每条边的弧线方向,统一弯曲方向 for u, v in G.edges(): u_x, u_y = pos[u] v_x, v_y = pos[v] # 通过向量叉积判断v在u的顺时针方向,调整rad正负 cross_product = u_x * v_y - u_y * v_x rad = 0.5 if cross_product < 0 else -0.5 nx.draw_networkx_edges(G, pos, edgelist=[(u, v)], arrows=True, connectionstyle=f"arc3,rad={rad}", arrowstyle='->') plt.show() netw(10)
关键修改点
- 修正图类型:将
nx.Graph()改为nx.DiGraph(),匹配箭头绘制的有向边逻辑。 - 简化位置设置:直接使用
fixed_positions作为节点位置,去掉spring_layout的微调,确保节点严格按环形排列。 - 动态调整弧线方向:通过向量叉积判断节点间的顺时针/逆时针关系,设置
rad的正负值,让所有弧线向环形外侧统一弯曲,彻底解决跨首尾边的反转问题。
内容的提问来源于stack exchange,提问作者Archibald Addelton
相关产品推荐
相关产品推荐

