{"id":6680,"date":"2017-08-17T16:10:42","date_gmt":"2017-08-17T20:10:42","guid":{"rendered":"https:\/\/qxf2.com\/blog\/?p=6680"},"modified":"2017-08-17T16:41:41","modified_gmt":"2017-08-17T20:41:41","slug":"drawing-weighted-graphs-with-networkx","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/drawing-weighted-graphs-with-networkx\/","title":{"rendered":"Weighted graphs using NetworkX"},"content":{"rendered":"<p>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 <a href=\"https:\/\/networkx.github.io\/\">NetworkX<\/a> 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.<\/p>\n<figure id=\"attachment_6689\" aria-describedby=\"caption-attachment-6689\" style=\"width: 594px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-6689\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/sample_weighted_graph.png\" alt=\"A weighted graph using NetworkX\" width=\"594\" height=\"401\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/sample_weighted_graph.png 594w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/sample_weighted_graph-300x203.png 300w\" sizes=\"auto, (max-width: 594px) 100vw, 594px\" \/><figcaption id=\"caption-attachment-6689\" class=\"wp-caption-text\">A weighted graph using NetworkX and PyPlot<\/figcaption><\/figure>\n<p>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 <a href=\"https:\/\/networkx.github.io\/documentation\/networkx-1.9\/examples\/drawing\/weighted_graph.html\">weighted graphs<\/a> 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.<\/p>\n<p>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.<\/p>\n<p><b>NOTE:<\/b> The approach outlined here works well for a small set of nodes. I have not tried it on a large network.<\/p>\n<hr>\n<h3> The data<\/h3>\n<p>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:<\/p>\n<p>1. Karpov &#8211; Kasparov: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=15940&amp;pid2=20719\">170 classical games<\/a><br \/>\n2. Karpov &#8211; Kramnik: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=20719&amp;pid2=12295\">15 classical games<\/a><br \/>\n3. Karpov &#8211; Anand: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=20719&amp;pid2=12088\">45 classical games<\/a><br \/>\n4. Kasparov &#8211; Kramnik: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=12295&amp;pid2=15940\">49 classical games<\/a><br \/>\n5. Kasparov &#8211; Anand: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=12088&amp;pid2=15940\">51 classical games<\/a><br \/>\n6. Kramnik &#8211; Anand: <a href=\"http:\/\/www.chessgames.com\/perl\/chess.pl?pid=12295&amp;pid2=12088\">91 classical games<\/a><\/p>\n<hr>\n<h3> Drawing weighted edges with NetworkX<\/h3>\n<p>I won&#8217;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.<\/p>\n<p>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:<br \/>\na) Iterate through the graph nodes to gather all the weights<br \/>\nb) Get unique weights<br \/>\nc) Loop through the unique weights and plot any edges that match the weight<br \/>\nd) Normalize the weights (I did num_nodes\/sum(all_weights)) so that no edge is too thick<br \/>\ne) Make changes to the weighting (I used a scalar multiplier) so the graph looks good<\/p>\n<p><b>a) Iterate through the graph nodes to gather all the weights<\/b><\/p>\n<pre lang=\"python\">\r\n    for (node1,node2,data) in G.edges(data=True):\r\n        all_weights.append(data['weight']) #we'll use this when determining edge thickness<\/pre>\n<p><b>b) Get unique weights<\/b><\/p>\n<pre lang=\"python\">\r\n    unique_weights = list(set(all_weights))<\/pre>\n<p><b>c) Loop through the unique weights and plot any edges that match the weight<\/b><\/p>\n<pre lang=\"python\">\r\n    #4 c. Plot the edges - one by one!\r\n    for weight in unique_weights:\r\n        #4 d. Form a filtered list with just the weight you want to draw\r\n        weighted_edges = [(node1,node2) for (node1,node2,edge_attr) in G.edges(data=True) if edge_attr['weight']==weight]\r\n        width = weight\r\n        nx.draw_networkx_edges(G,pos,edgelist=weighted_edges,width=width)<\/pre>\n<p><b>d) Normalize the weights<\/b><br \/>\nI did num_nodes\/sum(all_weights) so that no edge is too thick<\/p>\n<pre lang=\"python\">\r\n        #4 e. I think multiplying by [num_nodes\/sum(all_weights)] makes the graphs edges look cleaner\r\n        width = weight*len(node_list)\/sum(all_weights)<\/pre>\n<figure id=\"attachment_6681\" aria-describedby=\"caption-attachment-6681\" style=\"width: 815px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-6681\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_1.png\" alt=\"Weighted graph NetworkX\" width=\"815\" height=\"615\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_1.png 815w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_1-300x226.png 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_1-768x580.png 768w\" sizes=\"auto, (max-width: 815px) 100vw, 815px\" \/><figcaption id=\"caption-attachment-6681\" class=\"wp-caption-text\">Normalized weights (scalar multiplier = 1.0)<\/figcaption><\/figure>\n<p>But the resulting graph had very thin edges. So I decided to multiply all thickness by a factor of 5.<\/p>\n<p><b>e) Make changes to the weighting<\/b><br \/>\nI used a scalar multiplier of 5 so the graph looks good<\/p>\n<pre lang=\"python\">\r\n        #4 e. I think multiplying by [num_nodes\/sum(all_weights)] makes the graphs edges look cleaner\r\n        width = weight*len(node_list)*5.0\/sum(all_weights)<\/pre>\n<figure id=\"attachment_6682\" aria-describedby=\"caption-attachment-6682\" style=\"width: 815px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-6682\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_5.png\" alt=\"Weighted edges denoted by different graph thickness (NetworkX)\" width=\"815\" height=\"615\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_5.png 815w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_5-300x226.png 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2017\/08\/chess_legends_weight_5-768x580.png 768w\" sizes=\"auto, (max-width: 815px) 100vw, 815px\" \/><figcaption id=\"caption-attachment-6682\" class=\"wp-caption-text\">Normalized weights (scalar multiplier = 5.0)<\/figcaption><\/figure>\n<p>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.<\/p>\n<h3> Code for a network graph with different edge weights<\/h3>\n<p>I&#8217;ve added detailed comments to the code here. If you are new to NetworkX, it should help you get started quickly.<\/p>\n<pre lang=\"python\">\r\n\"\"\"\r\nAn example of drawing a weighted graph using the NetworkX module\r\nThis is sample code and not indicative of how Qxf2 writes Python code\r\n\r\n---------------\r\nI. The problem:\r\n---------------\r\nI will be plotting how often these four world chess champions played each other:\r\na) Anatoly Karpov\r\nb) Gary Kasparov\r\nc) Vladimir Kramnik\r\nd) Vishwanathan Anand\r\n\r\n-------------------------\r\nII. Technical references: \r\n-------------------------\r\n1. https:\/\/networkx.github.io\/documentation\/networkx-1.9\/examples\/drawing\/weighted_graph.html\r\n2. https:\/\/stackoverflow.com\/questions\/28372127\/add-edge-weights-to-plot-output-in-networkx\r\n\r\n-----------------------------------------\r\nIII. Reference for data (as of Aug 2017):\r\n-----------------------------------------\r\n1. Karpov - Kasparov: 170 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=15940&pid2=20719\r\n\r\n2. Karpov - Kramnik: 15 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=20719&pid2=12295\r\n\r\n3. Karpov - Anand: 45 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=20719&pid2=12088\r\n\r\n4. Kasparov - Kramnik: 49 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=12295&pid2=15940\r\n\r\n5. Kasparov - Anand: 51 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=12088&pid2=15940\r\n\r\n6. Kramnik - Anand: 91 classical games\r\nhttp:\/\/www.chessgames.com\/perl\/chess.pl?pid=12295&pid2=12088\r\n\"\"\"\r\n\r\n#1. Import pyplot and nx\r\nimport matplotlib.pyplot as plt\r\nimport networkx as nx\r\n\r\n\r\ndef plot_weighted_graph():\r\n    \"Plot a weighted graph\"\r\n\r\n    #2. Add nodes\r\n    G = nx.Graph() #Create a graph object called G\r\n    node_list = ['Karpov','Kasparov','Kramnik','Anand']\r\n    for node in node_list:\r\n        G.add_node(node)\r\n\r\n    #Note: You can also try a spring_layout\r\n    pos=nx.circular_layout(G) \r\n    nx.draw_networkx_nodes(G,pos,node_color='green',node_size=7500)\r\n\r\n    #3. If you want, add labels to the nodes\r\n    labels = {}\r\n    for node_name in node_list:\r\n        labels[str(node_name)] =str(node_name)\r\n    nx.draw_networkx_labels(G,pos,labels,font_size=16)\r\n\r\n\r\n    #4. Add the edges (4C2 = 6 combinations)\r\n    #NOTE: You usually read this data in from some source\r\n    #To keep the example self contained, I typed this out\r\n    G.add_edge(node_list[0],node_list[1],weight=170) #Karpov vs Kasparov\r\n    G.add_edge(node_list[0],node_list[2],weight=15) #Karpov vs Kramnik\r\n    G.add_edge(node_list[0],node_list[3],weight=45) #Karpov vs Anand\r\n    G.add_edge(node_list[1],node_list[2],weight=49) #Kasparov vs Kramnik\r\n    G.add_edge(node_list[1],node_list[3],weight=51) #Kasparov vs Anand\r\n    G.add_edge(node_list[2],node_list[3],weight=91) #Kramnik vs Anand\r\n\r\n    all_weights = []\r\n    #4 a. Iterate through the graph nodes to gather all the weights\r\n    for (node1,node2,data) in G.edges(data=True):\r\n        all_weights.append(data['weight']) #we'll use this when determining edge thickness\r\n\r\n    #4 b. Get unique weights\r\n    unique_weights = list(set(all_weights))\r\n\r\n    #4 c. Plot the edges - one by one!\r\n    for weight in unique_weights:\r\n        #4 d. Form a filtered list with just the weight you want to draw\r\n        weighted_edges = [(node1,node2) for (node1,node2,edge_attr) in G.edges(data=True) if edge_attr['weight']==weight]\r\n        #4 e. I think multiplying by [num_nodes\/sum(all_weights)] makes the graphs edges look cleaner\r\n        width = weight*len(node_list)*3.0\/sum(all_weights)\r\n        nx.draw_networkx_edges(G,pos,edgelist=weighted_edges,width=width)\r\n\r\n    #Plot the graph\r\n    plt.axis('off')\r\n    plt.title('How often have they played each other?')\r\n    plt.savefig(\"chess_legends.png\") \r\n    plt.show() \r\n\r\n#----START OF SCRIPT\r\nif __name__=='__main__':\r\n    plot_weighted_graph()<\/pre>\n<hr>\n<h3>References:<\/h3>\n<p>1. <a href=\"https:\/\/networkx.github.io\/documentation\/networkx-1.9\/examples\/drawing\/weighted_graph.html\">NetworkX documentation on weighted graphs<\/a><br \/>\n2. <a href=\"https:\/\/stackoverflow.com\/questions\/28372127\/add-edge-weights-to-plot-output-in-networkx\">A StackOverflow answer that does not use NetworkX<\/a><\/p>\n<hr>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[142,18],"tags":[],"class_list":["post-6680","post","type-post","status-publish","format-standard","hentry","category-data-visualization","category-python"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/6680","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=6680"}],"version-history":[{"count":22,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/6680\/revisions"}],"predecessor-version":[{"id":6725,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/6680\/revisions\/6725"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=6680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=6680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=6680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}