合并共享属性的节点

时间:2020-05-11 06:09:37

标签: python graph mapping networkx

已编辑

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

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

df1

[[79 44 24 67 53 90  0 57 74 86]
 [47 17 24 81 16 12 22 52 63 70]
 [17 94 71 76 23 66 76 59 77 19]
 [17 43 12 90 27 28 27 89 39 20]
 [22 49 38 49 56 70 77 40 71 13]
 [29 73  2 45 30  2 88 65 65 88]
 [79 15 31 44 19  2 74  4  7 35]
 [89 67 76 66  4 14 63 29 90  6]
 [86 51 61 17 79 78 64 67 33 63]
 [45 16 30 93 75 42 86 93 63 84]]

[[False False False False False False False False False False]
 [False False False False False False False False False False]
 [ True False False False False False False False False  True]
 [ True False  True False  True  True  True False False  True]
 [ True False  True False False False False False False  True]
 [ True False  True  True  True  True False False False False]
 [False  True  True  True  True  True False  True  True  True]
 [False  True False  True  True  True  True  True False  True]
 [False  True  True  True  True  True  True  True  True  True]
 [ True  True  True False  True  True  True False  True  True]]

df2

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

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

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

一个节点仅应映射一次,例如,如果我要映射10,则它在第一张图上具有优先级20和描述1,然后找到相同的优先级,并且第二张图上的说明。为此,这里有60个。除60之外,没有其他节点。但是,如果我们在第一个图上使用Start,则它具有优先级2和描述20。在第二张图上,有两个节点的优先级为left,描述为2,描述为left。这会造成混乱。我不能像70 and 80这样两次映射20。但是我想将它们作为单个节点放置,如下面的示例图所示。

我期待以下结果。

![enter image description here

要获得上述结果,我尝试了以下python代码。

20/70 and 20/80

任何人都可以帮助我达到/ graph /图所示的上述结果吗?任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:2)

我要解决的方法是,使用两个图的nx.union构建一个新图,然后将 start end“组合”在一起节点,它们使用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)

enter image description here

我们现在所能做的就是提出一些数据结构,稍后我们可以使用它们轻松地组合共享属性的节点对。为此,我们可以按其描述对节点进行排序。对它们进行排序将使我们能够使用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)

enter image description here