Weighted graphs using NetworkX

I wanted to draw a network of nodes and use the thickness of the edges between the nodes to denote some information. Since I had used NetworkX a long time ago for drawing network graphs, I decided to use it again. This was going to be a one off visualization. So I did not want to spend too much time studying NetworkX.

A weighted graph using NetworkX
A weighted graph using NetworkX and PyPlot

I started by searching Google Images and then looked on StackOverflow for drawing weighted edges using NetworkX. Surprisingly neither had useful results. The NetworkX documentation on weighted graphs was a little too simplistic. It also annoyed me that their example/image will not immediately catch the eye of someone performing an image search like I did.

So I am writing this post and adding a couple of images in the hope that it helps people looking for a quick solution to drawing weighted graphs with NetworkX.

NOTE: The approach outlined here works well for a small set of nodes. I have not tried it on a large network.


The data

I like chess. So let us pretend I will be plotting how often Karpov, Kasparov, Kramnik and Anand played each other in classical chess. The data (as of Aug 2017) looks like this:

1. Karpov – Kasparov: 170 classical games
2. Karpov – Kramnik: 15 classical games
3. Karpov – Anand: 45 classical games
4. Kasparov – Kramnik: 49 classical games
5. Kasparov – Anand: 51 classical games
6. Kramnik – Anand: 91 classical games


Drawing weighted edges with NetworkX

I won’t go over the process of adding nodes, edges and labels to a graph. I assume you know that. If you are new to NetworkX, just read through the well-commented code in the next section.

Instead, I will focus on how to draw edges of different thickness. The process of drawing edges of different thickness between nodes looks like this:
a) Iterate through the graph nodes to gather all the weights
b) Get unique weights
c) Loop through the unique weights and plot any edges that match the weight
d) Normalize the weights (I did num_nodes/sum(all_weights)) so that no edge is too thick
e) Make changes to the weighting (I used a scalar multiplier) so the graph looks good

a) Iterate through the graph nodes to gather all the weights

    for (node1,node2,data) in G.edges(data=True):
        all_weights.append(data['weight']) #we'll use this when determining edge thickness

b) Get unique weights

    unique_weights = list(set(all_weights))

c) Loop through the unique weights and plot any edges that match the weight

    #4 c. Plot the edges - one by one!
    for weight in unique_weights:
        #4 d. Form a filtered list with just the weight you want to draw
        weighted_edges = [(node1,node2) for (node1,node2,edge_attr) in G.edges(data=True) if edge_attr['weight']==weight]
        width = weight
        nx.draw_networkx_edges(G,pos,edgelist=weighted_edges,width=width)

d) Normalize the weights
I did num_nodes/sum(all_weights) so that no edge is too thick

        #4 e. I think multiplying by [num_nodes/sum(all_weights)] makes the graphs edges look cleaner
        width = weight*len(node_list)/sum(all_weights)
Weighted graph NetworkX
Normalized weights (scalar multiplier = 1.0)

But the resulting graph had very thin edges. So I decided to multiply all thickness by a factor of 5.

e) Make changes to the weighting
I used a scalar multiplier of 5 so the graph looks good

        #4 e. I think multiplying by [num_nodes/sum(all_weights)] makes the graphs edges look cleaner
        width = weight*len(node_list)*5.0/sum(all_weights)
Weighted edges denoted by different graph thickness (NetworkX)
Normalized weights (scalar multiplier = 5.0)

Much better! I can quickly see that Karpov and Kasparov played each other many times. While Kramnik and Anand played each other quite a few times too. Given their respective ages and peaks, that makes sense.

Code for a network graph with different edge weights

I’ve added detailed comments to the code here. If you are new to NetworkX, it should help you get started quickly.

"""
An example of drawing a weighted graph using the NetworkX module
This is sample code and not indicative of how Qxf2 writes Python code
 
---------------
I. The problem:
---------------
I will be plotting how often these four world chess champions played each other:
a) Anatoly Karpov
b) Gary Kasparov
c) Vladimir Kramnik
d) Vishwanathan Anand
 
-------------------------
II. Technical references: 
-------------------------
1. https://networkx.github.io/documentation/networkx-1.9/examples/drawing/weighted_graph.html
2. https://stackoverflow.com/questions/28372127/add-edge-weights-to-plot-output-in-networkx
 
-----------------------------------------
III. Reference for data (as of Aug 2017):
-----------------------------------------
1. Karpov - Kasparov: 170 classical games
http://www.chessgames.com/perl/chess.pl?pid=15940&pid2=20719
 
2. Karpov - Kramnik: 15 classical games
http://www.chessgames.com/perl/chess.pl?pid=20719&pid2=12295
 
3. Karpov - Anand: 45 classical games
http://www.chessgames.com/perl/chess.pl?pid=20719&pid2=12088
 
4. Kasparov - Kramnik: 49 classical games
http://www.chessgames.com/perl/chess.pl?pid=12295&pid2=15940
 
5. Kasparov - Anand: 51 classical games
http://www.chessgames.com/perl/chess.pl?pid=12088&pid2=15940
 
6. Kramnik - Anand: 91 classical games
http://www.chessgames.com/perl/chess.pl?pid=12295&pid2=12088
"""
 
#1. Import pyplot and nx
import matplotlib.pyplot as plt
import networkx as nx
 
 
def plot_weighted_graph():
    "Plot a weighted graph"
 
    #2. Add nodes
    G = nx.Graph() #Create a graph object called G
    node_list = ['Karpov','Kasparov','Kramnik','Anand']
    for node in node_list:
        G.add_node(node)
 
    #Note: You can also try a spring_layout
    pos=nx.circular_layout(G) 
    nx.draw_networkx_nodes(G,pos,node_color='green',node_size=7500)
 
    #3. If you want, add labels to the nodes
    labels = {}
    for node_name in node_list:
        labels[str(node_name)] =str(node_name)
    nx.draw_networkx_labels(G,pos,labels,font_size=16)
 
 
    #4. Add the edges (4C2 = 6 combinations)
    #NOTE: You usually read this data in from some source
    #To keep the example self contained, I typed this out
    G.add_edge(node_list[0],node_list[1],weight=170) #Karpov vs Kasparov
    G.add_edge(node_list[0],node_list[2],weight=15) #Karpov vs Kramnik
    G.add_edge(node_list[0],node_list[3],weight=45) #Karpov vs Anand
    G.add_edge(node_list[1],node_list[2],weight=49) #Kasparov vs Kramnik
    G.add_edge(node_list[1],node_list[3],weight=51) #Kasparov vs Anand
    G.add_edge(node_list[2],node_list[3],weight=91) #Kramnik vs Anand
 
    all_weights = []
    #4 a. Iterate through the graph nodes to gather all the weights
    for (node1,node2,data) in G.edges(data=True):
        all_weights.append(data['weight']) #we'll use this when determining edge thickness
 
    #4 b. Get unique weights
    unique_weights = list(set(all_weights))
 
    #4 c. Plot the edges - one by one!
    for weight in unique_weights:
        #4 d. Form a filtered list with just the weight you want to draw
        weighted_edges = [(node1,node2) for (node1,node2,edge_attr) in G.edges(data=True) if edge_attr['weight']==weight]
        #4 e. I think multiplying by [num_nodes/sum(all_weights)] makes the graphs edges look cleaner
        width = weight*len(node_list)*3.0/sum(all_weights)
        nx.draw_networkx_edges(G,pos,edgelist=weighted_edges,width=width)
 
    #Plot the graph
    plt.axis('off')
    plt.title('How often have they played each other?')
    plt.savefig("chess_legends.png") 
    plt.show() 
 
#----START OF SCRIPT
if __name__=='__main__':
    plot_weighted_graph()

References:

1. NetworkX documentation on weighted graphs
2. A StackOverflow answer that does not use NetworkX


Arunkumar Muralidharan

I want to find out what conditions produce remarkable software. A few years ago, I chose to work as the first professional tester at a startup. I successfully won credibility for testers and established a world-class team. I have lead the testing for early versions of multiple products. Today, I run Qxf2 Services. Qxf2 provides software testing services for startups. If you are interested in what Qxf2 offers or simply want to talk about testing, you can contact me at: mak@qxf2.com. I like testing, math, chess and dogs.

Be First to Comment

Leave a Reply

Your email address will not be published.