pythonnetworkx

How to contract nodes and apply functions to node attributes


I have a simple graph with the attributes height and area

import networkx as nx
nodes_list = [("A", {"height":10, "area":100}),
              ("B", {"height":12, "area":200}),
              ("C", {"height":8, "area":150}),
              ("D", {"height":9, "area":120})]
G = nx.Graph()
G.add_nodes_from(nodes_list)

edges_list = [("A","B"), ("B","C"), ("C","D")]

G.add_edges_from(edges_list)
nx.draw(G, with_labels=True, node_color="red")

enter image description here

I want to contract nodes and update the attributes with the average of height and the sum of area of the contracted nodes.

There must be some easier way than:

H = nx.contracted_nodes(G, "B","C")
#print(H.nodes["B"])
#{'height': 12, 'area': 200, 'contraction': {'C': {'height': 8, 'area': 150}}}

#Calc the average of node B and C's heights
new_height = (H.nodes["B"]["height"] + H.nodes["B"]["contraction"]["C"]["height"])/2 #10

#Calc the sum of of node B and C's areas
new_area = H.nodes["B"]["area"] + H.nodes["B"]["contraction"]["C"]["area"]

#Drop old attributes
del(H.nodes["B"]["height"], H.nodes["B"]["area"], H.nodes["B"]["contraction"])

nx.set_node_attributes(H, {"B":{"height":new_height, "area":new_area}})

print(H.nodes["B"])
#{'height': 10.0, 'area': 350}

Can I get networkx to average height and sum area, every time I contract multiple nodes?


Solution

  • I don't think there is a builtin way, especially since you have custom aggregation functions.

    Here is an alternative way, using a custom function:

    def contract_merge(G, n1, n2):
        H = nx.contracted_nodes(G, n1, n2)
        d_n1 = H.nodes[n1]
        d_n2 = d_n1.pop('contraction')[n2]
        d_n1['height'] = (d_n1['height']+d_n2['height'])/2
        d_n1['area'] = d_n1['area']+d_n2['area']
        nx.set_node_attributes(H, {n1: d_n1})
        return H
    
    H = contract_merge(G, 'B', 'C')
    print(H.nodes(data=True))
    

    Output:

    NodeDataView({'A': {'height': 10, 'area': 100},
                  'B': {'height': 10.0, 'area': 350},
                  'D': {'height': 9, 'area': 120}})