Drawing graphs with graphviz.

Nodes

Dot[source]

Dot(defaults=None, rankdir='LR', directed=True, compound=True, **kwargs)

Create a pydot.Dot graph with fastai/fastdot style defaults

uniq_name[source]

uniq_name(o)

quote[source]

quote(x, q='"')

Surround x with "

add_mapping[source]

add_mapping(graph_item, obj)

Node[source]

Node(obj, **kwargs)

Create a pydot.Node with a unique name

pydot uses the same name-based approach to identifying graph items as graphviz. However we would rather use python objects. Therefore, we patch pydot to use unique names.

g = Dot()
a = Node('a')
g.add_node(a)
g
G n713399e83ee74a898bc2db1b950e8f31 a

If a 2-tuple is passed to add_node, then the 2nd element becomes the tooltip. You can also pass any kwargs that are accepted by graphviz.

g = Dot()
g.add_node(Node(['a', "My tooltip"], fillcolor='pink'))
g
G na7dcdc7162834efeb835f6c69f74406a a

Keyword args can also be arbitrary functions, which will called with the node's label.

g = Dot()
o = 'a'
g.add_node(Node(o, fillcolor=lambda o:'pink'))
g
G n13b05b380bd34df1a785259bfbb30968 a

object2graph[source]

object2graph(o)

Get graph item representing o

object2graph(o).get_fillcolor()
'"pink"'

Colors

The callable kwargs functionality can be used to map labels to colors in a consistent way..

obj2node_color[source]

obj2node_color(cm, minalpha, rangealpha, o)

Create a consistent mapping from objects to colors, using colormap cm

graph_colors1 = partial(obj2node_color, plt.get_cmap('rainbow'), 30, 160)
graph_colors2 = partial(obj2node_color, plt.get_cmap('tab20'), 30, 160)

These predefined color mapping functions provide a good range of colors and readable text.

g = Dot()
g.add_node(Node('a', fillcolor=graph_colors1))
g.add_node(Node('b', fillcolor=graph_colors1))
g
G n9e9a4d2d36974a7d96649490097a3bd6 a nf1188698e93f4107af2a55482ba80fed b
g = Dot()
g.add_node(Node('a', fillcolor=graph_colors2))
g.add_node(Node('b', fillcolor=graph_colors2))
g
G n2626d36f158444a9b4435ad74ff0aaf5 a nef3ae735fb4e4e6184c5c9e29977f9b2 b

We'll use the former color function as our default. You can change it by simply modifying node_defaults.

Clusters and Items

Cluster[source]

Cluster(obj='', **kwargs)

Create a pydot.Cluster with a unique name

g = Dot()
sg = Cluster('clus', tooltip='Cluster tooltip')
sg.add_node(Node(['a', "My tooltip"]))
sg.add_node(Node('b'))
g.add_subgraph(sg)
g
G cluster_n0a5c753056c34b718edf77a0a90d74a8 clus n1bfad22d28c34f138da84a8b557a26e6 a na7635fd80b404d5195595b829e74f42b b

Graph.nodes[source]

Graph.nodes()

ith node in Graph

Graph.__getitem__[source]

Graph.__getitem__(i)

ith node in Graph

You can subscript into a Graph's Nodes by index:

print(sg[0].get_label())
"a"

Graph.add_item[source]

Graph.add_item(item, **kwargs)

Add a Cluster, Node, or Edge to the Graph

There's no good reason to have different methods for adding clusters vs nodes (as pydot requires), so we provide a single method.

g = Dot()
sg = Cluster('clus')
g.add_item(sg)
sg.add_item('a')
g
G cluster_n89db517fafdd4a7b8b699640cf52def6 clus n58f72ebf06524a80b35a155afdf6d7cc a

Graph.add_items[source]

Graph.add_items(*items, **kwargs)

Add items the Graph

graph_items[source]

graph_items(*items, **kwargs)

Add items to a new pydot.Dot

sg1 = Cluster('clus')
sg1.add_items('n1', 'n2')
sg2 = Cluster()
sg2.add_item('n')
graph_items(sg1,sg2)
G cluster_n0d26e8dee5c947d4aea2c1b989d3110e clus cluster_n77fe9765c64944b99de2cc77f4cf7ec7 nccec5177e2c148cea7ffae383bf8ecaf n1 nfa173949581b4fb684ebd89462d5cdbd n2 n064e4fa84f704de285d66e75a49d78d8 n

Edges

Graph.first[source]

Graph.first()

First node in Graph, searching subgraphs recursively as needed

Graph.last[source]

Graph.last()

Lastt node in Graph, searching subgraphs recursively as needed

Node.with_compass[source]

Node.with_compass(compass=None)

Graph.with_compass[source]

Graph.with_compass(compass=None)

Node.connect[source]

Node.connect(item, compass1=None, compass2=None, **kwargs)

Connect two nodes or clusters

Graph.connect[source]

Graph.connect(item, compass1=None, compass2=None, **kwargs)

Connect two nodes or clusters

sg2 = Cluster('clus2')
n1 = sg2.add_item('n1', fillcolor='pink')
n2 = sg2.add_item('n2', fillcolor='lightblue')
sg2.add_item(n1.connect(n2))

sg1 = Cluster('clus1')
sg1.add_item(sg2)

a,b = Node('a'),Node('b')
edges = a.connect(b),a.connect(a),sg1.connect(b),sg2[0].connect(a)
g = Dot()
g.add_items(sg1, a, b, *edges)
g
G cluster_n8cdeddb4d91b4cbaa99b2afed0f9b0f8 clus1 cluster_ne170407d7be54af690c13a328bc67d9c clus2 n8e1bf3e3aae14e79949b75f114d84d42 n1 n381fdd653dbb4ab3aa85315008f62684 n2 n8e1bf3e3aae14e79949b75f114d84d42->n381fdd653dbb4ab3aa85315008f62684 nc52b35d9bdc64eaab7b6e58ffbb546c7 a n8e1bf3e3aae14e79949b75f114d84d42->nc52b35d9bdc64eaab7b6e58ffbb546c7 n58853ea47818402e90f45421aa532e72 b n381fdd653dbb4ab3aa85315008f62684->n58853ea47818402e90f45421aa532e72 nc52b35d9bdc64eaab7b6e58ffbb546c7->nc52b35d9bdc64eaab7b6e58ffbb546c7 nc52b35d9bdc64eaab7b6e58ffbb546c7->n58853ea47818402e90f45421aa532e72

object_connections[source]

object_connections(conns)

Create connections between all pairs in conns

This is a shortcut for creating connections between objects that are already in a graph.

a,b = 'a','b'
g = graph_items(a, b)
g.add_items(*object_connections([(a,b)]))
g
G n02325fca8c7647308f780bd1b1441606 a n2503a3d8752043d794ddaa7b63edf841 b n02325fca8c7647308f780bd1b1441606->n2503a3d8752043d794ddaa7b63edf841

Sequential

Since it's common to want to connect a series sequentially, we provide some simple shortcuts for this functionality.

graph_edges_seq[source]

graph_edges_seq(items)

Add edges between each pair of nodes in items

Graph.add_edges_seq[source]

Graph.add_edges_seq(items)

Add edges between each pair of nodes in items

g = Dot()
its = g.add_items('a','b','c')
g.add_edges_seq(its)
g
G n8b28659431fe4923802ff1130edfc4ff a n997f4c965d5c450baaf1fba2273769f4 b n8b28659431fe4923802ff1130edfc4ff->n997f4c965d5c450baaf1fba2273769f4 na2ad2acd7fc244089959ddf879d94740 c n997f4c965d5c450baaf1fba2273769f4->na2ad2acd7fc244089959ddf879d94740

seq_cluster[source]

seq_cluster(items, cluster_label='', **kwargs)

g = Dot()
g.add_item(seq_cluster(['a','b','c'], 'clust'))
g.add_item(seq_cluster(['1','2','c'], 'clust2'))
g
G cluster_nb8bdb480a7ac407c804c73a19a1405bd clust cluster_ndc26a1cee66341d791378f4042203d58 clust2 nc29c62c0a6c04a869b20fad997ae89cc a n8d2c3ac7ed8a43fba39b91139fc11d6c b nc29c62c0a6c04a869b20fad997ae89cc->n8d2c3ac7ed8a43fba39b91139fc11d6c ne1015339dd8c4921a178f5bd0fff1157 c n8d2c3ac7ed8a43fba39b91139fc11d6c->ne1015339dd8c4921a178f5bd0fff1157 n361627be6c67404f9d229cb56dd97797 1 n43ec3e239d1d4790862276d9842aacd0 2 n361627be6c67404f9d229cb56dd97797->n43ec3e239d1d4790862276d9842aacd0 nd3f8d67d80e442d98bbb8eecbfa28551 c n43ec3e239d1d4790862276d9842aacd0->nd3f8d67d80e442d98bbb8eecbfa28551
g = Dot()
g.add_item(seq_cluster(['a','b','c'], 'clust'))
g
G cluster_nbbc4dc71e6404e4ba679cef33c0fd865 clust nc2370d1af45d406680d57cbdaecf3b20 a nbb4413a8c7d649d3b19abd2158a954ff b nc2370d1af45d406680d57cbdaecf3b20->nbb4413a8c7d649d3b19abd2158a954ff n59062744ff0f4ced9ccb05dca0112226 c nbb4413a8c7d649d3b19abd2158a954ff->n59062744ff0f4ced9ccb05dca0112226
sg1 = seq_cluster(['a','b','c'], 'clust1')
sg2 = seq_cluster(['a1','a2',sg1], 'clust2')
g = Dot()
g.add_item(sg2)
g
G cluster_nfc64d12dd6e740e6b3aa5a2737f840e7 clust2 cluster_n35368dadf7534fcd81cbae76e8f33d59 clust1 n6a75ae056c28436eae26a80f7fe18911 a1 n758b0aabfe7e478a96630b2b74c62315 a2 n6a75ae056c28436eae26a80f7fe18911->n758b0aabfe7e478a96630b2b74c62315 n09b3dadebfb8421abd0aba5de888a696 a n758b0aabfe7e478a96630b2b74c62315->n09b3dadebfb8421abd0aba5de888a696 nd3e21a8eb3754225a92ef6da20161d17 b n09b3dadebfb8421abd0aba5de888a696->nd3e21a8eb3754225a92ef6da20161d17 na9f8a6609da44ba6829774d6722c7d23 c nd3e21a8eb3754225a92ef6da20161d17->na9f8a6609da44ba6829774d6722c7d23
sg1 = seq_cluster(['inp'], 'clust1')
sg2 = seq_cluster(['a','b','c'], 'clust2')
sg2.add_items(sg1.connect(sg2[-1]), sg1.connect(sg2))
g = Dot()
g.add_items(sg1,sg2)
g
G cluster_n6caa5b91b6c84786a43efe167a8f77e0 clust1 cluster_n2fbb87b714ce4246a774f4124e4c9fb0 clust2 nc28559323f334baf81ebc3ec1736b615 inp nb549d152fb124143b2a16acf37ca4a29 a nc28559323f334baf81ebc3ec1736b615->nb549d152fb124143b2a16acf37ca4a29 n6ffabfa8c3864e6b931f8bfde5760539 c nc28559323f334baf81ebc3ec1736b615->n6ffabfa8c3864e6b931f8bfde5760539 nec1eccb0648d458e8149b43dc2948f86 b nb549d152fb124143b2a16acf37ca4a29->nec1eccb0648d458e8149b43dc2948f86 nec1eccb0648d458e8149b43dc2948f86->n6ffabfa8c3864e6b931f8bfde5760539

Point[source]

Point(label='pnt', **kwargs)

Create a Node with a 'point' shape

sg = Cluster('clus')
a,b,c = sg.add_items('a','b','c')
p = sg.add_item(Point())
sg.add_item(p.connect(c))
sg.add_items(p.connect(a), a.connect(b), b.connect(c))

g = Dot()
g.add_items(sg)
g
G cluster_n89ad911232c44b95a9f811153efbbebc clus n6b827779d551463fa495cc006405808c a n00a71041057e48bba332a85c24861ac4 b n6b827779d551463fa495cc006405808c->n00a71041057e48bba332a85c24861ac4 n421febd9368f485290e3e3ed896d951a c n00a71041057e48bba332a85c24861ac4->n421febd9368f485290e3e3ed896d951a n019e00165911436590e80633d4dcfc41 n019e00165911436590e80633d4dcfc41->n6b827779d551463fa495cc006405808c n019e00165911436590e80633d4dcfc41->n421febd9368f485290e3e3ed896d951a