networkx - 根据边属性改变颜色/宽度 - 结果不一致

64 投票
3 回答
114109 浏览
提问于 2025-04-19 13:05

我成功地画出了图表,但在测试时发现以下两行代码的结果不一致:

colors = [h.edge[i][j]['color'] for (i,j) in h.edges_iter()]
widths = [h.edge[i][j]['width'] for (i,j) in h.edges_iter()]
nx.draw_circular(h, edge_color=colors, width=widths)

这种方法的输出结果很稳定,而下面这行代码的颜色和大小却不符合边的顺序:

colors = list(nx.get_edge_attributes(h,'color').values())
widths = list(nx.get_edge_attributes(h,'width').values())
nx.draw_circular(h, edge_color=colors, width=widths)

不过,我觉得上面这两行代码都依赖于函数调用来根据边的顺序返回属性。为什么结果会不同呢?

我觉得用 h[][][] 来访问属性有点笨拙;有没有可能用点的方式来访问,比如 edge.color for edge in h.edges()

还是说我漏掉了什么?

3 个回答

0

如果你想避免手动添加边缘的颜色、透明度或宽度,你可以试试这个函数,它可能会对你有帮助:

def rgb_to_hex(rgb):
    return '#%02x%02x%02x' % rgb

adjacency_matrix = np.array([[0, 0, 0.5], [1, 0, 1], [1, 0.5, 0]]))
n_graphs = 5
fig, axs = plt.subplots(1, len(n_graphs), figsize=(19,2.5)) 

for graph in range(n_graphs):   

    pos = {0: (1, 0.9), 1: (0.9, 1), 2: (1.1, 1)} 

    # draw DAG graph from adjacency matrix 
    gr = nx.from_numpy_matrix(adjacency_matrix, create_using=nx.DiGraph)
    weights = nx.get_edge_attributes(gr, "weight")
  
    # adding nodes 
    all_rows = range(0, adjacency_matrix.shape[0])
    for n in all_rows:
        gr.add_node(n)
    
    # getting edges 
    edges = gr.edges()
      
    # weight and color of edges 
    scaling_factor = 4 # to emphasise differences 
    alphas = [weights[edge] * scaling_factor for edge in edges]
    colors = [rgb_to_hex(tuple(np.repeat(int(255 * (1- 
    weights[edge])),3))) for edge in edges]
    
    # draw graph 
    nx.draw(gr, 
            pos, 
            ax=axs[graph],
            edgecolors='black', 
            node_color='white', 
            node_size=2000, 
            labels={0: "A", 1: "B", 2: "C"},
            font_weight='bold',
            linewidths=2,
            with_labels=True,
            connectionstyle="arc3,rad=0.15",
            edge_color=colors,
            width=alphas)

  
plt.tight_layout()
28

字典是用来存储NetworkX图的基本数据结构。从Python 3.7版本开始,字典会保持插入的顺序。这意味着我们可以放心地使用nx.get_edge_attributes来获取边的属性,因为每次运行Graph.edges()时,边的顺序都是一样的(这个函数在内部会被get_edge_attributes调用)。

所以在绘图的时候,我们可以直接从get_edge_attributes返回的结果中设置属性,比如edge_color(边的颜色)和width(边的宽度)。下面是一个例子:

G = nx.Graph()
G.add_edge(0,1,color='r',weight=2)
G.add_edge(1,2,color='g',weight=4)
G.add_edge(2,3,color='b',weight=6)
G.add_edge(3,4,color='y',weight=3)
G.add_edge(4,0,color='m',weight=1)

colors = nx.get_edge_attributes(G,'color').values()
weights = nx.get_edge_attributes(G,'weight').values()

pos = nx.circular_layout(G)
nx.draw(G, pos, 
        edge_color=colors, 
        width=list(weights),
        with_labels=True,
        node_color='lightgreen')

enter image description here

95

传递给绘图函数的边的顺序是很重要的。如果你不指定顺序(使用 edges 这个关键词),那么就会得到 G.edges() 的默认顺序。为了安全起见,最好像这样明确给出这个参数:

import networkx as nx

G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)

pos = nx.circular_layout(G)

edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]

nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)

这样会得到类似下面的输出: 在这里输入图片描述

撰写回答