103

I'm trying to produce a flow diagram of a tree structure. I've been able to create representative graphs with networkx, but I need a way to show the tree structure when I output a plot. I'm using matplotlib.pylab to plot the graph.

I need to show the data in a structure similar to what is shown here. Although I don't have sub-graphs.

How can I guarantee a structure like that?

Examples for the unbelievers:

Various NetworkX layouts

I've been able to show the graphs with pylab and graphviz, but neither offer the tree structure I'm looking for. I've tried every layout networkx has to offer, but none of them show a hierarchy. I've just not sure what options/mode to give it OR if I need to use weights. Any suggestions would help a bunch.

@jterrace:

Here's a rough outline of what I used to produce the plots above. I've added some labels, but other than that it's the same.

import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()

G.add_node("ROOT")

for i in xrange(5):
    G.add_node("Child_%i" % i)
    G.add_node("Grandchild_%i" % i)
    G.add_node("Greatgrandchild_%i" % i)

    G.add_edge("ROOT", "Child_%i" % i)
    G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
    G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)

plt.title("draw_networkx")
nx.draw_networkx(G)

plt.show()
max
  • 2,346
  • 4
  • 26
  • 34

5 Answers5

144

If you use a directed graph then the Graphviz dot layout will do something like you want with the tree. Here is some code similar to the above solutions that shows how to do that

import networkx as nx
from networkx.drawing.nx_agraph import graphviz_layout
import matplotlib.pyplot as plt
G = nx.DiGraph()

G.add_node("ROOT")

for i in range(5):
    G.add_node("Child_%i" % i)
    G.add_node("Grandchild_%i" % i)
    G.add_node("Greatgrandchild_%i" % i)

    G.add_edge("ROOT", "Child_%i" % i)
    G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
    G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)

# write dot file to use with graphviz
# run "dot -Tpng test.dot >test.png"
nx.nx_agraph.write_dot(G,'test.dot')

# same layout using matplotlib with no labels
plt.title('draw_networkx')
pos=graphviz_layout(G, prog='dot')
nx.draw(G, pos, with_labels=False, arrows=False)
plt.savefig('nx_test.png')

Graphviz output

NetworkX/Matplotlib output

UPDATED

Here is a version updated for networkx-2.0 (and with upcoming networkx-2.1 draws arrows too).

import networkx as nx
from networkx.drawing.nx_agraph import write_dot, graphviz_layout
import matplotlib.pyplot as plt
G = nx.DiGraph()

G.add_node("ROOT")

for i in range(5):
    G.add_node("Child_%i" % i)
    G.add_node("Grandchild_%i" % i)
    G.add_node("Greatgrandchild_%i" % i)

    G.add_edge("ROOT", "Child_%i" % i)
    G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
    G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)

# write dot file to use with graphviz
# run "dot -Tpng test.dot >test.png"
write_dot(G,'test.dot')

# same layout using matplotlib with no labels
plt.title('draw_networkx')
pos =graphviz_layout(G, prog='dot')
nx.draw(G, pos, with_labels=False, arrows=True)
plt.savefig('nx_test.png')

enter image description here

Smart Manoj
  • 5,230
  • 4
  • 34
  • 59
Aric
  • 24,511
  • 5
  • 78
  • 77
  • 1
    Ah ha! So all I needed was a directed graph with the 'dot' layout. I knew it was something very small. Thanks a lot Aric! – max Jul 14 '12 at 21:37
  • Is there any good way to label the nodes ascendingly? By that I mean, I create a graph `g = nx.full_rary_tree(2, 10)` If I print the edges I get: `[(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), ... ]` but it will visualize them in a different order... – CodeKingPlusPlus Nov 06 '13 at 05:26
  • What ordering do you want? See bfs_edges() and dfs_edges(). – Aric Nov 06 '13 at 13:15
  • 1
    PyGrapviz works with Python 3 and this code will work with Python 3. – Aric Apr 13 '15 at 12:10
  • 3
    also if you encounter any problem installing `pygraphviz` in regular ways, try `pip install --install-option="--include-path=/usr/local/include/" --install-option="--library-path=/usr/local/lib/" pygraphviz` – Rotail Feb 23 '18 at 19:42
  • 2
    @Rotail This worked for me after I installed `graphviz` (in my case using `brew install graphviz`). – Shivendra Sep 24 '18 at 05:36
  • ModuleNotFoundError: No module named 'networkx.drawing'; 'networkx' is not a package. networkx already installed – tribbloid Dec 29 '18 at 01:42
  • `graphviz_layout(G, prog='dot')` gives `OSError: Format: "dot" not recognized. Use one of: ` error. – quest Apr 12 '19 at 09:50
  • I tried so many different ways to make a large graph. This was by far the best. – Vince Miller Sep 10 '19 at 14:38
11

You can use pygraphviz to get close:

>>> import pygraphviz
>>> import networkx
>>> import networkx as nx
>>> G = nx.Graph()
>>> G.add_node("ROOT")
>>> for i in xrange(5):
...     G.add_node("Child_%i" % i)
...     G.add_node("Grandchild_%i" % i)
...     G.add_node("Greatgrandchild_%i" % i)
...     G.add_edge("ROOT", "Child_%i" % i)
...     G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
...     G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)

>>> A = nx.to_agraph(G)
>>> A.layout('dot', args='-Nfontsize=10 -Nwidth=".2" -Nheight=".2" -Nmargin=0 -Gfontsize=8')
>>> A.draw('test.png')

Result: enter image description here

Note I copied the graphviz options from the link you posted above. I'm not sure why the 4th child is drawn on top instead of in strictly vertical format. Maybe someone who knows more about the Graphviz options can help with that.

jterrace
  • 64,866
  • 22
  • 157
  • 202
  • thanks. This was exactly what I was seeing when I tried it. I find it somewhat strange why it produced something like this. – max Jul 14 '12 at 21:35
  • 5
    Note that in version 1.11 of networkx the api changed. The `to_agraph` function is now located in `nx.nx_agraph.to_agraph`. – m00am Feb 16 '16 at 11:05
  • 2
    Is there a way to make sure that child are always below their parents? – Dror Aug 06 '19 at 09:50
  • I don't understand where you have used that imported `pygraphviz`. – Rahat Zaman Jan 26 '23 at 08:12
3

You can use grandalf for a python-only solution, if you don't want to install graphviz.

Also, this type of visualization is called a layered graph drawing or Sugiyama-style graph drawing, which can display many kinds of graphs, including non-trees.

See my answer to a different question for details and implementation.

phlaxyr
  • 923
  • 1
  • 8
  • 22
1

See also the hierarchical structures created by @Abdallah Sobehy, w/o the need to use any extra module/library (like 'pygraphviz'), at preserving the left and right child while printing python graphs using networkx

Maybe the graph is not so nice as the above, but it does the requested job!

Apostolos
  • 3,115
  • 25
  • 28
0

You could do it manually just with only networkx. Traverse tree and for each children coordinate x change as if right parent.x+1/np.log(y_coord + 1) if left parent.x-1... and y = parent.y - 1 Or any other algorithm to get coord and eg. See image here.

import networkx as nx
import matplotlib.pyplot as plt
coord = {0: [8, 0], 2: [9, -1], 1: [8, -2], 4: [10, -2], 3: [9, -3], 5: [11, -3]}
eg = [[0, 2], [2, 1], [2, 4], [4, 3], [4, 5]]
G=nx.Graph()
G.add_edges_from(eg) 
nx.draw(G, with_labels=True, node_size=1500, node_color="skyblue", pos=coord) 
plt.show()