Hi, for my latest college assignment I had to find a way to visualize data that is interrelated. For instance, my application generated the following data:
A --> B B --> C B --> D
And I needed a way to generate pretty graphs without too much headache!
I am already using wxPython for the application’s UI (it saved me a lot of time, and I learned a lot in the process, even implemented my own clone of the Aero Wizard layout used in Windows Vista and 7), so I tried to look for a way to integrate some other super powered library into my app. I quickly remembered a couple of projects that used Graphviz to generate visualizations of Django model definitions: DjangoGraphviz, and django-graphviz. I even hacked one of them to generate visualizations where each app had it’s own color. But these were complicated projects, because they generated some funky XML (correct me if I’m wrong) files that where later fed to graphviz and you had to mess a little with config options.
I wanted something simpler… lucky me, I stumbled into the wonderful project pydot. It was just what I needed, after a couple of experiments I could quickly generate “fantastic” visualizations with almost no extra effort. I’m using this on my Fedora 11 x86_64 box, so I just had to install pydot and easy_install handled the dependencies (it automatically downloaded and installed pyparsing), I am not sure what the requirements for Windows are… specially to get this running with py2exe, I’ll be blogging about that later.
In any case, let me provide you with a quick “getting started” tutorial guide on how this works.
Example 1: “Easy as Pie”
Ok, let’s try an easy one, suppose you have a set of data that you want to represent in a hierarchical way… Say: King, Lords and Vassals… Let’s try to graph that with as little code as we can:
See this code formatted in dpaste.org: http://dpaste.org/YQAZ/
# -*- coding: utf-8 -*- """ pydot example 1 @author: Federico Cáceres @url: http://pythonhaven.wordpress.com/2009/12/09/generating_graphs_with_pydot """ import pydot # import pydot or you're not going to get anywhere my friend :D # first you create a new graph, you do that with pydot.Dot() graph = pydot.Dot(graph_type='graph') # the idea here is not to cover how to represent the hierarchical data # but rather how to graph it, so I'm not going to work on some fancy # recursive function to traverse a multidimensional array... # I'm going to hardcode stuff... sorry if that offends you # let's add the relationship between the king and vassals for i in range(3): # we can get right into action by "drawing" edges between the nodes in our graph # we do not need to CREATE nodes, but if you want to give them some custom style # then I would recomend you to do so... let's cover that later # the pydot.Edge() constructor receives two parameters, a source node and a destination # node, they are just strings like you can see edge = pydot.Edge("king", "lord%d" % i) # and we obviosuly need to add the edge to our graph graph.add_edge(edge) # now let us add some vassals vassal_num = 0 for i in range(3): # we create new edges, now between our previous lords and the new vassals # let us create two vassals for each lord for j in range(2): edge = pydot.Edge("lord%d" % i, "vassal%d" % vassal_num) graph.add_edge(edge) vassal_num += 1 # ok, we are set, let's save our graph into a file graph.write_png('example1_graph.png') # and we are done!
Simple, huh? You should have a graph like this:
Now… that looks pretty boring, right? Let’s try something some more… colorful and with a different kind of graph.
Example 2: “Still Easy as Pie”
Ok, on the last example we made an undirected graph, let’s create a directed one now, and let’s also add some colors and labels on the edges between nodes. Say we have four nodes: A, B, C and D, were A points to B, B points to C, C points to D and D points back to A… let’s try that, ok?
See this code formatted in dpaste.org: http://dpaste.org/dSyE/
# -*- coding: utf-8 -*- """ pydot example 2 @author: Federico Cáceres @url: http://pythonhaven.wordpress.com/2009/12/09/generating_graphs_with_pydot """ import pydot # this time, in graph_type we specify we want a DIrected GRAPH graph = pydot.Dot(graph_type='digraph') # in the last example, we did no explicitly create nodes, we just created the edges and # they automatically placed nodes on the graph. Unfortunately, this way we cannot specify # custom styles for the nodes (although you CAN set a default style for all objects on # the graph...), so let's create the nodes manually. # creating nodes is as simple as creating edges! node_a = pydot.Node("Node A", style="filled", fillcolor="red") # but... what are all those extra stuff after "Node A"? # well, these arguments define how the node is going to look on the graph, # you can find a full reference here: # http://www.graphviz.org/doc/info/attrs.html # which in turn is part of the full docs in # http://www.graphviz.org/Documentation.php # neat, huh? Let us create the rest of the nodes! node_b = pydot.Node("Node B", style="filled", fillcolor="green") node_c = pydot.Node("Node C", style="filled", fillcolor="#0000ff") node_d = pydot.Node("Node D", style="filled", fillcolor="#976856") #ok, now we add the nodes to the graph graph.add_node(node_a) graph.add_node(node_b) graph.add_node(node_c) graph.add_node(node_d) # and finally we create the edges # to keep it short, I'll be adding the edge automatically to the graph instead # of keeping a reference to it in a variable graph.add_edge(pydot.Edge(node_a, node_b)) graph.add_edge(pydot.Edge(node_b, node_c)) graph.add_edge(pydot.Edge(node_c, node_d)) # but, let's make this last edge special, yes? graph.add_edge(pydot.Edge(node_d, node_a, label="and back we go again", labelfontcolor="#009933", fontsize="10.0", color="blue")) # and we are done graph.write_png('example2_graph.png') # this is too good to be true!
That code generates this graph:
As you can see, the possibilities are endless, using the attributes you can set for each node, edge and other forms supported by the library, you can easily visualize your data that would otherwise be very hard to see.
One more thing, you can set the attributes when initializing the Node/Edge, or you can use the set_xyz method, for instance, instead of doing this:
pydot.Edge(node_d, node_a, label="and back we go again", labelfontcolor="#009933", fontsize="10.0", color="blue")
You could do this:
edge = pydot.Edge(node_d, node_a) edge.set_label("and back we go again") edge.set_labelfontcolor("#009933") edge.set_fontsize("10.0") edge.set_color("blue")
That can come in handy.
Well, I hope you found this useful as I have, and that this mini pydot tutorial helps you with this great library.
Don’t forget to visit these links:
- pydot site: http://code.google.com/p/pydot/
- graphviz site: http://www.graphviz.org/
- graphviz gallery (crazy examples here!): http://www.graphviz.org/Gallery.php
- graphviz documentation: http://www.graphviz.org/Documentation.php
- graphviz documentation on attributes: http://www.graphviz.org/doc/info/attrs.html
- link to another guy’s blog with a great example (Ben Atkin): http://benatkin.com/weblog/2009/04/27/graphing-retweets-with-python-and-graphviz/
Till next time!