7

I'm using this bit of code from a deap symbolic regression example problem and the graph displays fine but I want the nodes to expand as rounded rectangles to fit the text automatically. (I don't want to just specify the node size through trial and error). How would I do that?

# show tree
import matplotlib.pyplot as plt
import networkx

nodes, edges, labels = gp.graph(bests[0])
graph = networkx.Graph()
graph.add_nodes_from(nodes)
graph.add_edges_from(edges)
pos = networkx.graphviz_layout(graph, prog="dot")

plt.figure(figsize=(7,7))
networkx.draw_networkx_nodes(graph, pos, node_size=900, node_color="w")
networkx.draw_networkx_edges(graph, pos)
networkx.draw_networkx_labels(graph, pos, labels)
plt.axis("off")
plt.show()
user2514676
  • 473
  • 2
  • 6
  • 18

4 Answers4

5

The argument node_size accepts both scalar and vector values. While scalar makes all nodes of equal sizes, vector helps you to specify individual values in a list to be used for each node. If your node ids are strings then the following strategy works quite well.

Just change the size argument to a list in networkx.draw_networkx_nodes based on the length of each node id. Choose the_base_size appropriately.

networkx.draw_networkx_nodes(graph, pos, 
    node_size=[len(v) * the_base_size for v in graph.nodes()], 
    node_color="w")

You can adapt this to the case where you can handles labels too.

***However, I am not sure if one-to-one correspondence will be preserved while it chooses the node sizes from the list based on label sizes. Do share your results. I have personally used it for string node ids and it works well.

mathfux
  • 5,759
  • 1
  • 14
  • 34
theOne
  • 411
  • 4
  • 4
1

There isn't a simple way to do that with matplotlib and networkx (of course it is possible with enough code). Graphviz does a really excellent job with labels and it is easy to write dot format files from networkx to process with Graphviz.
Also take a look at https://github.com/chebee7i/nxpd which might do exactly what you need.

Aric
  • 24,511
  • 5
  • 78
  • 77
  • I have GraphViz and pygraphviz installed and working. Do you know of, or can you provide, any specific example code for doing this specific type of binary tree with pygraphviz? – user2514676 Jan 10 '15 at 17:53
  • I found the answer here (there's a pygraphviz example in there): http://deap.gel.ulaval.ca/doc/dev/tutorials/advanced/gp.html – user2514676 Jan 10 '15 at 17:59
  • Sounds like a separate question. But yes, use the "dot" layout with graphviz. – Aric Jan 10 '15 at 18:00
  • 1
    For people finding this answer in the future, it seems that `nxpd` is only compatible with networkx 1.x versions. From trying to run it with the latest version 2.4, it seems to access 1.x properties, like `G.number_of_selfloops()`, as described here: https://networkx.github.io/documentation/stable/release/migration_guide_from_1.x_to_2.0.html – starwarswii Apr 18 '20 at 22:59
1

I stumbled upon this problem recently. Even after 5 years, there is no direct way of doing it. However, after many trials and errors, finally I found sensible work-around. You can remove the nodes and just use labels with background color.

pos = nx.spring_layout(graph) # Get positions of your nodes
nx.draw_networkx_nodes(graph, pos=pos, label=labels, bbox=dict(fc="r"))

Here labels is dictionary with node as key and node name as value.

Another fine-tuned way of achieving this especially when you want to adjust property of each label (like color, shape, background etc), best way it to add labels by yourself using plt.text().

pos = nx.spring_layout(graph) # Get positions of your nodes
for key in pos:
    x, y = pos[key]
    plt.text(x,y,key,fc="r", ha="center", va="center")
Dexter
  • 1,421
  • 3
  • 22
  • 43
1

I liked @mathfux's solution because it positions the arrows correctly in directed graphs. But it needs some tweaks to solve that correspondence problem mentioned (lists of positions and colours are in layout order, not node order); also to handle zero-based indexing for lists. I also found the sizes work by a square law, rather than linearly. Here's an improved version with colours, using Kamada Kwai rather than Spring so the layout doesn't change each time.

import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx

G = nx.DiGraph()
G.add_edges_from([(0,1), (1, 1), (1, 7), (2, 1), (2, 2), (2, 3), 
                  (2, 6), (3, 5), (4, 3), (5, 4), (5, 8),
                  (5, 9), (6, 4), (7, 2), (7, 6), (8, 7)])
labelList="zero one twotwotwo three four five six seven eighteighteight nine".split(' ')

positions=nx.kamada_kawai_layout(G)
plt.figure(figsize =(9, 9))
nx.draw_networkx(G, 
                 node_color =['C{}'.format(i) for i in positions], 
                 pos=positions, 
                labels={idx: val for idx, val in enumerate(labelList)},
                node_size=[len(labelList[i])**2 * 60 for i in positions]
                )

Resulting network diagram

CharlesW
  • 955
  • 8
  • 18