具有权重和类型相关节点和边的网络图

时间:2021-02-09 08:22:12

标签: python matplotlib networkx

我有以下从 .xlsx 文件导入的简化数据库

df = pd.DataFrame({ 'from':['A','A','B','B','C','C'], 'to':['B', 'C','A','C,'A','B'],'weight':['10','5','25','2','4','8'],
                    'type':['typeA','typeA','typeB','typeB','typeC','typeC]})

与上面相同的数据库,为了清楚起见:

from:   A     A      B     B     C     C       
to:     B     C      A     C     A     B      
weight: 10    5      25    15    2     4          
type:   typeA typeA typeB typeB typeC typeC

我想创建一个具有特征的网络图表

  • 节点
    • 不同的颜色取决于类型
    • 根据重量增加尺寸,例如节点 A 大小与 (10 +5) 成正比
  • 边缘
    • 不同的颜色取决于类型
    • 根据单个重量增加宽度,例如边AB宽度与10成正比,边AC宽度与5成正比

到目前为止,我只能制作未考虑此标准的网络图

G=nx.nx.from_pandas_edgelist(df, 'from', 'to', 'type', create_using=nx.Graph())
nx.draw(G, with_labels=True, node_size=1500, node_color="skyblue", pos=nx.fruchterman_reingold_layout(G))

如何使代码动态化并指定它以确保它符合标准,例如添加新数据或更改权重?

2 个答案:

答案 0 :(得分:0)

要从 pandas 边列表中动态获取图形,您可以使用 pandas itertuples
以下是它的基本工作原理:

for row in df.itertuples():
    
    n = row[0]      # 'from' is a reserved word so 'row.from' returns Syntax Error
    u = row.to
    w = row.weight
    t = row.type
    
    G.add_node(n, weight = w, type_ = t)
    G.add_node(u, weight = w, type_ = t) 
    G.add_edge(n, u, weight = w, type_ = t) # weight of edge

现在让我们添加一些打印件让您知道数据何时发生变化:

for row in df.itertuples():
    
    u = row[1]      # 'from' is a reserved word so 'row.from' returns Syntax Error
    v = row.to
    w = row.weight
    t = row.type
    
    if u in G:
        new_data = {'weight':w, 'type_':t}
        if G.nodes(data=True)[u] != new_data:
            print("Node {} data update: {} {}".format(u, G.nodes(data=True)[u], new_data))
            G.nodes()[u]["weight"] = w
            G.nodes()[u]["type_"] = t
    else:
        G.add_node(u, weight = w, type_ = t)
        
    if v in G:
        new_data = {'weight':w, 'type_':t}
        if G.nodes(data=True)[v] != new_data:
            print("Node {} data update: {} {}".format(v, G.nodes(data=True)[v], new_data))
            G.nodes()[v]["weight"] = w
            G.nodes()[v]["type_"] = t
              
    else:    
        G.add_node(v, weight = w, type_ = t) 
    
    G.add_edge(u, v, weight = w, type_ = t)

现在,按照你假装的方式绘制网络:
每种类型都有一种颜色:

type_to_color = {
    "typeA": "blue",
    "typeB": "red",
    "typeC": "green"
}

节点颜色和权重可以这样获得:

n_colors = [type_to_color[u[1]] for u in g.nodes(data="type_")] # u[1] must be either typeA, typeB, typeC
n_sizes = [int(u[1]) for u in g.nodes(data="weight")]   # you may want to scale these a little e.g  n_sizes = [int(u[1])*10 for u in g.nodes(data="weight")]

可以像这样获得边缘颜色和权重:

edge_widths = [int(g[u][v]['weight']) for u,v in g.edges()]
edge_colors = [type_to_color[g[u][v]['type_']] for u,v in g.edges()]

接下来,像这样绘制网络:

nx.draw(g, with_labels = True, node_color = n_colors, edge_color=edge_colors ,node_size=n_sizes, width=edge_widths)

完整代码如下:

def get_graph_from_pandas(df, v = False):
        
    
    G = nx.Graph() 
    
    for row in df.itertuples():
        
        u = row[1]      # 'from' is a reserved word so 'row.from' returns Syntax Error
        v = row.to
        w = row.weight
        t = row.type
        
        if u in G:
            new_data = {'weight':w, 'type_':t}
            if G.nodes(data=True)[u] != new_data:
                print("Node {} data update: {} {}".format(u, G.nodes(data=True)[u], new_data))
                G.nodes()[u]["weight"] = w
                G.nodes()[u]["type_"] = t
        else:
            G.add_node(u, weight = w, type_ = t)
            
        if v in G:
            new_data = {'weight':w, 'type_':t}
            if G.nodes(data=True)[v] != new_data:
                print("Node {} data update: {} {}".format(v, G.nodes(data=True)[v], new_data))
                G.nodes()[v]["weight"] = w
                G.nodes()[v]["type_"] = t
                  
        else:    
            G.add_node(v, weight = w, type_ = t) 
        
        G.add_edge(u, v, weight = w, type_ = t)
     
    return G

def draw_graph(g):
    type_to_color = {
        "typeA": "blue",
        "typeB": "red",
        "typeC": "green",
        None:"black"
    }
    
    print([u[1] for u in g.nodes(data="type_")])
    n_colors = [type_to_color[u[1]] for u in g.nodes(data="type_")] # u[1] must be either typeA, typeB, typeC
    n_sizes = [int(u[1]) for u in g.nodes(data="weight")] # you may want to scale these a little e.g  n_sizes = [int(u[1])*10 for u in g.nodes(data="weight")]
    
    print(n_sizes)
    
    
    edge_widths = [int(g[u][v]['weight']) for u,v in g.edges()] # you may want to scale these a little e.g  edge_widths = [int(g[u][v]['weight'])*0.1 for u,v in g.edges()]
    edge_colors = [type_to_color[g[u][v]['type_']] for u,v in g.edges()]
    
    
    nx.draw(g, with_labels = True, node_color = n_colors, edge_color=edge_colors ,node_size=n_sizes, width=edge_widths)
    
    return g

有任何问题,请留言;)

答案 1 :(得分:0)

这是 different example 的链接

这是您的示例:

import pandas as pd
import networkx as nx

df = pd.DataFrame({'from':['A','A','B','B','C','C'], 
                   'to':['B', 'C','A','C','A','B'],
                   'weight':['10','5','25','2','4','8'],
                   'type':['typeA','typeA','typeB','typeB','typeC','typeC']})

df['weight'] = df['weight'].astype(int)
nodeweight = (df.groupby('from')['weight'].sum()*10).to_dict()
G=nx.nx.from_pandas_edgelist(df, 'from', 'to', ['type', 'weight'], create_using=nx.Graph())
edgeweights = [i[2]['weight'] for  i in G.edges(data=True)]
nodelist = list(G.nodes)
edgelabels = {(i[0],i[1]):f'{i[0]}{i[1]}' for i in G.edges()}
nodesize = [nodeweight[i] for i in nodelist]
colorlist=[*'rbg']
pos = nx.fruchterman_reingold_layout(G)
nx.draw_networkx_nodes(G, pos, node_size=nodesize, node_color=colorlist, labels=True);
nx.draw_networkx_edges(G, pos, width=edgeweights);
nx.draw_networkx_labels(G, pos, font_size=10, font_family='sans-serif', font_color='white');
nx.draw_networkx_edge_labels(G,pos,edge_labels=edgelabels,font_color='red');

输出:

enter image description here