Getting Started With Graphviz and Python

Getting started with Graphviz and Python About the graphviz module There are several Python modules for dealing with Gra

Views 143 Downloads 5 File size 102KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

Getting started with Graphviz and Python About the graphviz module There are several Python modules for dealing with Graphviz. The most popular one seems to be pygraphviz. Unfortunately it isn't available for Python 3. So we will use the graphviz module instead. Installation is simple: pip install graphviz

Hello Graphviz First we need to import the graphviz module: import graphviz as gv

Now we can create the graph object g1 and add two nodes A and B as well as an edge to connect the two. g1 = gv.Graph(format='svg') g1.node('A') g1.node('B') g1.edge('A', 'B')

Let's have a look at the the dot code that this will generate behind the scenes: print(g1.source) graph { A B A -- B }

That looks about right. Now it's time to save the graph to disk: filename = g1.render(filename='img/g1') print filename img/g1.svg

As you can see, the graphviz module takes care of appending the correct file extension. Let's have a look at the result: A

B

Directed Graphs To create a directed version of the previous graph we only need to replace the Graph object with a Digraph. g2 = gv.Digraph(format='svg') g2.node('A') g2.node('B') g2.edge('A', 'B') g2.render('img/g2') A

B

A more concise approach The above approach is fine for small graphs but for larger ones we should create a few helper functions. Since we will stick to SVG as our output format there is no need to explicity specifiy it for every graph. import functools graph = functools.partial(gv.Graph, format='svg') digraph = functools.partial(gv.Digraph, format='svg')

Now creating a new graph is as easy as calling graph() g3 = graph()

Next, we would like to specify the nodes and edges as simple data structures instead typing loads of method calls. nodes = ['A', 'B', ('C', {})]

Every node is represented by either a string or a tuple. In the latter case the second element of the tuple holds all attributes of that node as we will see later. The represenation of edges is quite similar: edges = [ ('A', 'B'), ('B', 'C'), (('A', 'C'), {}), ]

To actually create nodes and edges from these data structures we implement the functions add_nodes and add_edges. Note that both functions return the updated graph which will come in handy in just a second. def add_nodes(graph, nodes): for n in nodes: if isinstance(n, tuple): graph.node(n[0], **n[1]) else: graph.node(n) return graph

def add_edges(graph, edges): for e in edges: if isinstance(e[0], tuple): graph.edge(*e[0], **e[1]) else: graph.edge(*e)

return graph

Now we can create graphs in a much more concise way: add_edges( add_nodes(digraph(), ['A', 'B', 'C']), [('A', 'B'), ('A', 'C'), ('B', 'C')] ).render('img/g4') A

B

C

Labels Labeling nodes and edges is as easy as adding an entry to their dictionaries of attributes. add_edges( add_nodes(digraph(), [ ('A', {'label': 'Node A'}), ('B', {'label': 'Node B'}), 'C' ]), [ (('A', 'B'), {'label': 'Edge 1'}), (('A', 'C'), {'label': 'Edge 2'}), ('B', 'C') ] ).render('img/g5') Node A Edge 1 No de B

Edge 2

C

Styling Graphviz let's you style graphs extensively by setting specific attributes. The following example will showcase a few of these attributes. For a complete list of attributes please refer to http://www.graphviz.org/doc/info/attrs.html. First, let's create an unstyled graph: g6 = add_edges( add_nodes(digraph(), [ ('A', {'label': 'Node A'}), ('B', {'label': 'Node B'}), 'C' ]), [ (('A', 'B'), {'label': 'Edge 1'}), (('A', 'C'), {'label': 'Edge 2'}), ('B', 'C') ] )

We will use a simple dictionary to specifiy the attributes that we want to change: styles = { 'graph': { 'label': 'A Fancy Graph', 'fontsize': '16', 'fontcolor': 'white', 'bgcolor': '#333333', 'rankdir': 'BT', }, 'nodes': {

'fontname': 'Helvetica', 'shape': 'hexagon', 'fontcolor': 'white', 'color': 'white', 'style': 'filled', 'fillcolor': '#006699', }, 'edges': { 'style': 'dashed', 'color': 'white', 'arrowhead': 'open', 'fontname': 'Courier', 'fontsize': '12', 'fontcolor': 'white', } }

Now we need another helper function apply_styles to actually apply those attributes to the graph: def apply_styles(graph, styles): graph.graph_attr.update( ('graph' in styles and styles['graph']) or {} ) graph.node_attr.update( ('nodes' in styles and styles['nodes']) or {} ) graph.edge_attr.update(

('edges' in styles and styles['edges']) or {} ) return graph

Let's try it out! g6 = apply_styles(g6, styles) g6.render('img/g6') C

Node B

Edge 2

Edge 1

Node A

A Fancy Graph

Subgraphs Lastly, we will have a look a subgraphs. First, let's create two seperate graphs g7 and g8, each with their own styling. g7 = add_edges( add_nodes(digraph(), [ ('A', {'label': 'Node A'}), ('B', {'label': 'Node B'}), 'C' ]), [ (('A', 'B'), {'label': 'Edge 1'}), (('A', 'C'), {'label': 'Edge 2'}), ('B', 'C') ] )

g8 = apply_styles( add_edges(

add_nodes(digraph(), [ ('D', {'label': 'Node D'}), ('E', {'label': 'Node E'}), 'F' ]), [ (('D', 'E'), {'label': 'Edge 3'}), (('D', 'F'), {'label': 'Edge 4'}), ('E', 'F') ] ), { 'nodes': { 'shape': 'square', 'style': 'filled', 'fillcolor': '#cccccc', } } )

Now we combine the two graphs by making g8 a subgraph of g7 and adding an extra edge to connect the two: g7.subgraph(g8) g7.edge('B', 'E', color='red', weight='2')

Let's have a look at the result: g7.render('img/g7')

Node A Edge 1

Edge 2

Node B

Node D

Edge 3

C

Node E

Edge 4

F

What's next? •Head over to GitHub to get the complete source code. •Improve or extend the script and let me know what you've come up with. •Learn more about Graphviz and the Dot language: http://www.graphviz.org/pdf/dotguide.pdf. © 2014 by matthiaseisen.com