Reputation: 1313
I want to plot the following graph using Graphvize:
xx = nx.DiGraph()
xx.add_node("P")
xx.add_node("C0")
xx.add_node("C1")
xx.add_node("I2")
xx.add_node("C3")
xx.add_node("C4")
xx.add_node("I5")
xx.add_node("C6")
xx.add_node("C7")
xx.node["C1"]['pos'] = (2,3)
xx.node["I2"]['pos'] = (4,5)
xx.node["C3"]['pos'] = (6,7)
xx.node["C4"]['pos'] = (6,5)
xx.node["I5"]['pos'] = (4,1)
xx.node["C6"]['pos'] = (6,2)
xx.node["C7"]['pos'] = (6,0)
xx.node["P"]['pos'] = (-2,3)
xx.node["C0"]['pos'] = (0,3)
xx.add_edge("P", "C0")
xx.add_edge("C0", "C1")
xx.add_edge("C1", "I2")
xx.add_edge("I2", "C3")
xx.add_edge("I2", "C4")
xx.add_edge("C1", "I5")
xx.add_edge("I5", "C6")
xx.add_edge("I5", "C7")
layout = dict((n, xx.node[n]["pos"]) for n in xx.nodes_iter())
nx.draw(xx,pos=layout,node_color='white')
nx.write_dot(xx,'66666.dot')
With matplotlitb i geht the right position of all nodes:
With Graphviz a graph without postion.
My question: Is there a possibility to add the correct positions in Graphviz? And is it possible to open the file "66666.dot" directly in python?
Thank you very much for your help!
Upvotes: 3
Views: 1191
Reputation: 949
Since the NetworkX library has had time to update and break backward compatibility and the problem described in the question is still in the new version of the library, I will try to update the example code for NetworkX 2.8.7 variant and provide a solution.
NameError: name 'nx' is not defined
pip install networkx
) and include the library at the beginning of the code:
import networkx as nx
AttributeError: 'DiGraph' object has no attribute 'node'
xx.nodes["C1"]['pos']
instead of xx.node["C1"]['pos']
. Reference: NetworkX Migration guide from 1.X to 2.0AttributeError: 'DiGraph' object has no attribute 'nodes_iter'
xx.nodes()
instead of xx.nodes_iter()
. Reference.AttributeError: module networkx has no attribute write_dot
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
instead of nx.write_dot(xx,'66666.dot')
. Reference: AttributeError: 'module' object has no attribute 'write_dot' for networkx libraryNote: If you do not already have graphviz and
pygraphviz
installed, you may need to install it.pygraphviz
library for Windows can be installed as a precompiled binary downloaded from Unofficial Windows Binaries for Python Extension Packages with commandpip install nameOfDownloadedPackage.whl
.
The code of the example now looks like this:
# Listing 1
import networkx as nx
xx = nx.DiGraph()
xx.add_node("P")
xx.add_node("C0")
xx.add_node("C1")
xx.add_node("I2")
xx.add_node("C3")
xx.add_node("C4")
xx.add_node("I5")
xx.add_node("C6")
xx.add_node("C7")
xx.nodes["C1"]['pos'] = (2,3)
xx.nodes["I2"]['pos'] = (4,5)
xx.nodes["C3"]['pos'] = (6,7)
xx.nodes["C4"]['pos'] = (6,5)
xx.nodes["I5"]['pos'] = (4,1)
xx.nodes["C6"]['pos'] = (6,2)
xx.nodes["C7"]['pos'] = (6,0)
xx.nodes["P"]['pos'] = (-2,3)
xx.nodes["C0"]['pos'] = (0,3)
xx.add_edge("P", "C0")
xx.add_edge("C0", "C1")
xx.add_edge("C1", "I2")
xx.add_edge("I2", "C3")
xx.add_edge("I2", "C4")
xx.add_edge("C1", "I5")
xx.add_edge("I5", "C6")
xx.add_edge("I5", "C7")
layout = dict((n, xx.nodes[n]["pos"]) for n in xx.nodes())
nx.draw(xx,pos=layout,node_color='white')
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
TLDR: Use the format pos="x,y!"
to record the positions of the nodes in the file instead of pos=("x,y")
.
Now that we have a workable example, we can move on to how to plot right position of nodes.
The created file 66666.dot
contains text in dot
language:
strict digraph "" {
P [pos="(-2, 3)"];
C0 [pos="(0, 3)"];
P -> C0;
C1 [pos="(2, 3)"];
C0 -> C1;
I2 [pos="(4, 5)"];
C1 -> I2;
I5 [pos="(4, 1)"];
C1 -> I5;
C3 [pos="(6, 7)"];
I2 -> C3;
C4 [pos="(6, 5)"];
I2 -> C4;
C6 [pos="(6, 2)"];
I5 -> C6;
C7 [pos="(6, 0)"];
I5 -> C7;
}
If you have graphviz installed, you can run the command in terminal:
dot -Tpng 66666.dot -o output.png
and check what will be drawn in the file output.png
:
The thing is that in graphviz library there are different layout engines, which are responsible for what positions will be drawn nodes and dot
layout engine does not understand the attribute pos="(4, 1)"
, so we will use the neato
layout engine that understands this attribute (as mentioned in its documentation). And we get the output in the terminal:
Error: node P, position (-2, 3), expected two doubles
Error: node C0, position (0, 3), expected two doubles
Error: node C1, position (2, 3), expected two doubles
Error: node I2, position (4, 5), expected two doubles
Error: node I5, position (4, 1), expected two doubles
Error: node C3, position (6, 7), expected two doubles
Error: node C4, position (6, 5), expected two doubles
Error: node C6, position (6, 2), expected two doubles
Error: node C7, position (6, 0), expected two doubles
and file output.png
:
We see that the coordinates do not affect the drawing positions and an error is written about the incorrect format of the input data. As mentioned there (converting network graph to graphviz), this error is due to an incorrect entry of the coordinate value. The format that graphviz understands is pos="x,y"
, not pos="(x,y)"
.
Note: Full value format looks like this
%f,%f('!')?
(e.g.pos="42,24!"
). The optional!
indicates the node position should not change.
Let's change the format of positions in the graph object before writing it to the file:
# Listing 2
# See the previous part of the code in Listing 1
for node in xx:
xx.nodes[node]['pos'] = "{},{}!".format(
xx.nodes[node]['pos'][0], xx.nodes[node]['pos'][1])
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
Result in the file 66666.dot
now:
strict digraph "" {
P [pos="-2,3!"];
C0 [pos="0,3!"];
P -> C0;
C1 [pos="2,3!"];
C0 -> C1;
I2 [pos="4,5!"];
C1 -> I2;
I5 [pos="4,1!"];
C1 -> I5;
C3 [pos="6,7!"];
I2 -> C3;
C4 [pos="6,5!"];
I2 -> C4;
C6 [pos="6,2!"];
I5 -> C6;
C7 [pos="6,0!"];
I5 -> C7;
}
Result in output.png
:
Looks good. To make it look like the result from matplotlib, you need to add more attributes label
, shape
to nodes and arrowhead
to edges:
# Listing 3
# See the previous part of the code in Listing 1
for node in xx:
xx.nodes[node]['pos'] = "{},{}!".format(
xx.nodes[node]['pos'][0], xx.nodes[node]['pos'][1])
xx.nodes[node]['label'] = ""
xx.nodes[node]['shape'] = "circle"
for edge in xx.edges:
xx.edges[edge]['arrowhead'] = 'box'
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
Result in output.png
:
Note (optional): To show the result of the matplotlib, add at the beginning of the file
import matplotlib.pyplot as plt
and at the endplt.show()
.
.dot
file directly in Pythonfilename.dot
directly in Python and get a graph object, you can use function networkx.drawing.nx_agraph.read_dot
.Upvotes: 1