Getting Started With Graphviz and Python

Download as pdf or txt
Download as pdf or txt
You are on page 1of 9

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

}
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

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 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')

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')

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

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

You might also like