【问题标题】:Merge nodes that share attributes合并共享属性的节点
【发布时间】:2020-05-11 06:09:37
【问题描述】:

已编辑

我真的需要 Networkx/graph 专家的帮助。

假设我有以下数据框,我想将这些数据框转换为图表。然后我想根据描述和优先级属性将这两个图与相应的节点进行映射。

df1

From   description    To         priority 
10        Start      20, 50         1
20        Left       40             2 
50        Bottom     40             2
40        End        -              1

df2

From   description    To      priority 
60        Start      70,80       1
70        Left       80, 90      2 
80        Left       100         2
90        Bottom     100         2
100        End        -          1

我刚刚转换了两个数据框并创建了一个图表(g1 和 g2)。

然后我尝试根据节点的描述和优先级匹配节点一次。例如10/60, 40/100, 50/90,但不是20/70, 20/80, and 70/8020 有三个条件要映射,这不是我想要的。因为我只想映射节点一次,除非我想将它们作为单个节点并将节点标记为红色以区分。

一个节点应该只映射一次意味着,例如,如果我想映射10,它在第一个图上具有优先级1和描述Start,然后在第二个图上找到相同的优先级和描述图形。为此,有 60 个。除了 60 之外,没有其他节点。但如果我们在第一张图上取 20,它的优先级为 2,描述为 left。在第二张图中,有两个节点的优先级为2,描述为left,即70 and 80。这会造成混乱。我不能像20/70 and 20/80 那样将 20 映射两次。但我想将它们作为单个节点放在示例图中,如下所示。

我期待以下结果。

为了得到上面的结果,我用下面的python代码试了一下。

mapped_list= []

for node_1, data_1 in g1.nodes(data=True):
    for node_2, data_2 in g2.nodes(data=True):

        if (((g1.node[node_1]['priority']) == (g2.node[node_2]['priority'])) & 
                    ((g1.node[node_1]['description']) == (g2.node[node_2]['description']))
                   ):
                    if (node_1 in mapped_list) & (node_2 in mapped_list): // check of if the node exist on the mapped_list
                        pass
                    else:    
                        name = str(node_1) + '/' + str(node_2)
                        mapped_list.append((data_1["priority"], data_1["descriptions"], node_1, name))
                        mapped_list.append((data_2["priority"], data_2["descriptions"], node_2, name))

谁能帮我实现图/graph/上显示的上述结果?任何帮助表示赞赏。

【问题讨论】:

    标签: python graph mapping networkx


    【解决方案1】:

    我要解决这个问题的方法是构建一个新图,采用两个图的 nx.union,然后将 startend em> 使用contracted_nodes 共享属性的节点。

    让我们从数据框创建两个图表开始:

    df1 = df1.drop('To',1).join(df1.To.str.replace(' ','').str.split(',').explode())
    df2 = df2.drop('To',1).join(df2.To.str.replace(' ','').str.split(',').explode())
    
    g1 = nx.from_pandas_edgelist(df1.iloc[:-1,[0,3]].astype(int), 
                                 source='From', target='To', create_using=nx.DiGraph)
    g2 = nx.from_pandas_edgelist(df2.iloc[:-1,[0,3]].astype(int), 
                                 source='From', target='To', create_using=nx.DiGraph)
    
    df1_node_ix = df1.assign(graph='graph1').set_index('From').rename_axis('nodes')
    nx.set_node_attributes(g1, values=df1_node_ix.description.to_dict(), 
                           name='description')
    nx.set_node_attributes(g1, values=df1_node_ix.priority.to_dict(), 
                           name='priority')
    nx.set_node_attributes(g1, values=df1_node_ix.graph.to_dict(), 
                           name='graph')
    
    df2_node_ix = df2.assign(graph='graph2').set_index('From').rename_axis('nodes')
    nx.set_node_attributes(g2, values=df2_node_ix.description.to_dict(), 
                           name='description')
    nx.set_node_attributes(g2, values=df2_node_ix.priority.to_dict(), 
                           name='priority')
    nx.set_node_attributes(g2, values=df2_node_ix.graph.to_dict(), 
                           name='graph')
    

    现在通过两个图的nx.union,我们有:

    g3 = nx.union(g1,g2)
    
    from networkx.drawing.nx_agraph import graphviz_layout
    plt.figure(figsize=(8,5))
    pos=graphviz_layout(g3, prog='dot')
    nx.draw(g3, pos=pos, 
            with_labels=True, 
            node_size=1500, 
            node_color='red', 
            arrowsize=20)
    

    我们现在可以做的是想出一些数据结构,我们以后可以使用它来轻松地组合共享属性的节点对。为此,我们可以按节点的描述对节点进行排序。对它们进行排序将使我们能够使用itertools.groupby 对连续相等的节点对进行分组,然后我们可以使用nx.contrated_nodes 轻松组合它们,然后在同一个先前的图上覆盖。可以使用nx.relabel_nodes:按照问题中指定的方式重新标记节点:

    from itertools import groupby
    
    g3_node_view = g3.nodes(data=True)
    sorted_by_descr = sorted(g3_node_view, key=lambda x: x[1]['description'])
    node_colors = dict()
    colors = {'Bottom':'saddlebrown', 'Start':'lightblue', 
              'Left':'green', 'End':'lightblue'}
    all_graphs = {'graph1', 'graph2'}
    
    for _, grouped_by_descr in groupby(sorted_by_descr, 
                                       key=lambda x: x[1]['description']):
        for _, group in groupby(grouped_by_descr, key=lambda x: x[1]['priority']):
            grouped_nodes = list(group)
            nodes = [i[0] for i in grouped_nodes]
            graphs = {i[1]['graph'] for i in grouped_nodes}
    
            # check if there are two nodes that share attributes
            # and both belong to different graphs
            if len(nodes)==2 and graphs==all_graphs:
                # contract both nodes and update graph
                g3 = nx.contracted_nodes(g3, *nodes)
                # define new contracted node name and relabel
                new_node = '/'.join(map(str, nodes))
                g3 = nx.relabel_nodes(g3, {nodes[0]:new_node})
                node_colors[new_node] = colors[grouped_nodes[0][1]['description']]
            else:
                for node in nodes:    
                    node_colors[node] = 'red'
    

    这会给:

    plt.figure(figsize=(10,7))
    pos=graphviz_layout(g3, prog='dot')
    nx.draw(g3, pos=pos, 
            with_labels=True, 
            node_size=2500, 
            nodelist=node_colors.keys(),
            node_color=node_colors.values(), 
            arrowsize=20)
    

    【讨论】:

    • 所以你只想组合不同图表中的那些? @kapital
    • 好的,我想我知道了,很快就会更新。与此同时,我认为如果你分享一个更完整的例子来反映我们讨论过的一些观点会更容易@kapital
    • 好的,这是另一个(希望是最后一个)问题,因为有一条边连接 60->70 60->80,输出中不应该也是这样吗也有图? @kapital
    • 我已经将我之前的内容改编为我们一直在评论的内容。我相信使用contracted_nodes 是这里的方法并简化了很多。如果您想了解更多详细信息,请告诉我@kapital
    • 如果不是这种情况,只需将“graph”属性添加到节点 @Kapital nx.set_node_attributes(g1, values=df1_node_ix.graph.to_dict(), name='graph'),用于图 1 或 2
    猜你喜欢
    • 2016-04-23
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2013-11-18
    相关资源
    最近更新 更多