4

I wrote a code to convert my MultiDiGraph() to a pydot graph to display self loops and arrows but after conversion, the pydot graph A has lost all the attributes of G. How can change the node size of graph A and set them equal to corresponding value in node_sizes[] list like I did for G?

code:

def draw_graph(graph, size):

    # extract nodes from graph
    nodes = set([n1 for n1, n2 in graph] + [n2 for n1, n2 in graph])
    print("Nodes ",nodes,"\n")

    node_sizes = []

    for n in nodes:
        #Scaling up the node importance by a factor of 2000 to make it visible
        node_sizes.append( size[n] * 2000)

    print("Node size ",node_sizes,"\n")

    # create networkx graph
    G=nx.MultiDiGraph()

    G.add_edges_from(edges)
    G.add_nodes_from(nodes)

    edge_colours = ['black' for edge in G.edges()]

    pos = nx.spring_layout(G)
    nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'), node_size = node_sizes)
    nx.draw_networkx_labels(G, pos)
    nx.draw_networkx_edges(G, pos, edge_color='black', arrows=True)

    plt.show()

    # render pydot by calling dot, no file saved to disk
    A=nx.to_pydot(G, strict=True)

    png_str = A.create_png(prog='dot')

    # treat the dot output string as an image file
    sio = BytesIO()
    sio.write(png_str)
    sio.seek(0)
    img = mpimg.imread(sio)

    # plot the image
    imgplot = plt.imshow(img, aspect='equal')
    plt.show(block=False)
0 _
  • 10,524
  • 11
  • 77
  • 109
Silvi Mantri
  • 41
  • 1
  • 3

1 Answers1

2

networkx sets the DOT attributes of nodes and edges during conversion using the attributes that the networkx.MultiDiGraph nodes and edges are labeled with. For example, g.add_node(1, label='first node') sets the label attribute:

import networkx as nx

graph = nx.MultiDiGraph()
graph.add_node(1, label='first node')
pd = nx.drawing.nx_pydot.to_pydot(graph)
print(pd.to_string())
# output:
# 'digraph  {\n1 [label="first node"];\n}\n'

(Also: call the function nx.drawing.nx_pydot.to_pydot instead of nx.to_pydot, which doesn't exist as of networkx == 2.0.)

Using this principle, setting the height attribute of a node (or width, see below for more details), we can change the sizes of nodes when drawn:

import networkx as nx

# create a `networkx` graph
graph = nx.MultiDiGraph()
graph.add_edge(1, 2)
graph.add_node(3, height=3)
graph.add_node(4, height=6)
# convert to a `pydot` graph
pd = nx.drawing.nx_pydot.to_pydot(graph)
pd.write_png('foo.png', prog='dot')

In case you want to write your own converter (instead of calling networkx.drawing.nx_pydot.to_pydot), working directly with pydot graphs:

import pydot

pd = pydot.Dot()
# create one node
nd = pydot.Node("a")
nd.set_height('"4"')
pd.add_node(nd)
# create another node
nd = pydot.Node("b")
nd.set_height('"2"')
pd.add_node(nd)
print(pd.to_string())
pd.write_png('bar.png')

The attributes relevant for setting the size of a node are height, width, and fixedsize. From GraphViz's documentation:

height

Height of node, in inches. This is taken as the initial, minimum height of the node. If fixedsize is true, this will be the final height of the node. Otherwise, if the node label requires more height to fit, the node's height will be increased to contain the label. Note also that, if the output format is dot, the value given to height will be the final value.

If the node shape is regular, the width and height are made identical. In this case, if either the width or the height is set explicitly, that value is used. In this case, if both the width or the height are set explicitly, the maximum of the two values is used. If neither is set explicitly, the minimum of the two default values is used.

The above resulted using networkx == 2.0 and pydot == 1.2.3.

0 _
  • 10,524
  • 11
  • 77
  • 109