Having nodes with differing lengths of attributes,

2019-08-23 12:38发布

问题:

In NetworkX 'm trying to accomplish the following:

  • within one graph create 'mother-nodes' and 'children-nodes', where children-nodes have only 1 attribute, and mother-nodes have several (4).
  • create edges between mother-nodes and children-nodes if at least one attribute (key-value pair) is the same,
  • create an edge only between a mother-node and children-node: even if two mother-nodes have one of 4 overlapping attributes, there should not be an edge between the two

So far I have the first part working, and on the second one michaelg has been very helpful, but there is still an error.

import networkx as nx
from itertools import product

# Mother-nodes
M = [('E_%d' % h, {'a': i, 'b': j, 'c': k, 'd': l})
 for h, (i, j, k, l) in enumerate(product(range(2), repeat=4), start=1)]

# children-nodes
a = [ (  'a_%d' % i, {'a' : i}) for i in range(0,2)  ]
b = [ (  'b_%d' % i, {'b' : i}) for i in range(0,2)  ]
c = [ (  'c_%d' % i, {'c' : i}) for i in range(0,2)  ]
d = [ (  'd_%d' % i, {'d' : i}) for i in range(0,2)  ]

# graph containing both
M_c = nx.Graph()
M_c.add_nodes_from(M)
ls_children = [a, b, c , d]
for ls_c in ls_children:
    M_c.add_nodes_from(ls_c)

# what it looks like so far
list(M_c.nodes(data=True))[0:20]

[('E_9', {'a': 1, 'b': 0, 'c': 0, 'd': 0}),
 ('d_0', {'d': 0}),
 ('E_10', {'a': 1, 'b': 0, 'c': 0, 'd': 1}),
 ('b_0', {'b': 0}),
 ('E_2', {'a': 0, 'b': 0, 'c': 0, 'd': 1}),
 ('E_1', {'a': 0, 'b': 0, 'c': 0, 'd': 0}),
 ('c_1', {'c': 1}),
 ...
    ] 

And then the second part, which generates an error:

for start in M_c.nodes(data=True):
    for end in M_c.nodes(data=True):
        for attr in list(start[1].keys()):
            if start[1][attr]:
                if end[1][attr]:
                    if start[1][attr] == end[1][attr]:
                        M_c.add_edge(start[0], end[0] )
    # Adding an else and continue statement does not affect the error, 
    # even adding three of them, for each if statement
    #        else:
    #            continue

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-32ae2a6095e5> in <module>()
      3         for attr in list(start[1].keys()):
      4             if start[1][attr]:
----> 5                 if end[1][attr]:
      6                     if start[1][attr] == end[1][attr]:
      7                         M_c.add_edge(start[0], end[0] )

KeyError: 'a'

I am perhaps overlooking something - any suggestions are greatly appreciated.

EDIT-1:

As suggested by ducminh I ran:

for mother_node in M:
    for child_node in chain(a, b, c, d):
        if child_node[1].items() <= mother_node[1].items():
            M_c.add_edge(child_node, mother_node)

Which returned this error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-24f1a24a49e8> in <module>()
      2     for child_node in chain(a, b, c, d):
      3         if child_node[1].items() <= mother_node[1].items():
----> 4             M_c.add_edge(child_node, mother_node)
      5 

/usr/local/lib/python3.5/dist-packages/networkx/classes/graph.py in add_edge(self, u_of_edge, v_of_edge, **attr)
    873         u, v = u_of_edge, v_of_edge
    874         # add nodes
--> 875         if u not in self._node:
    876             self._adj[u] = self.adjlist_inner_dict_factory()
    877             self._node[u] = {}

TypeError: unhashable type: 'dict'

回答1:

Verify the presence of the attribute in the end node

Since you iterate on the attributes of the start node, you also need to verify its existence in the end node:

Here is a modified code:

for start in M_c.nodes(data=True):
for end in M_c.nodes(data=True):
    for attr in list(start[1].keys()):
        # verify that the attribute is in the end node
        if attr in end[1]:
            if start[1][attr] == end[1][attr]:
                M_c.add_edge(start[0], end[0] )


回答2:

If a node is a mother node, it always has all 4 attributes a, b, c, d, while a child node only has one attribute. So it might be the case that the dict start[1] has the key attr, while end[1] does not.

To correctly add edges between mother nodes and child nodes, we need to iterate over all possible pairs (mother node, child node), then check if the dict of attributes of the child node is a sub-dict of that of the mother node.

from itertools import chain

for mother_node in M:
    for child_node in chain(a, b, c, d):
        if child_node[1].items() <= mother_node[1].items():
            M_c.add_edge(child_node[0], mother_node[0])