Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: networkx: How to make node size auto-expand to fit the label

Tags:

networkx

deap

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()
like image 399
user2514676 Avatar asked Jan 10 '15 17:01

user2514676


People also ask

How do I change the size of a node in Networkx?

Node Size. Altering node size globally is, again, quite simple via a keyword argument in the . draw() method — just specify node_size!

How many nodes can Networkx handle?

For NetworkX, a graph with more than 100K nodes may be too large. I'll demonstrate that it can handle a network with 187K nodes in this post, but the centrality calculations were prolonged. Luckily, there are some other packages available to help us with even larger graphs.

How do you customize a Networkx graph?

The draw() function of networkx library is used to draw the graph G with matplotlib. You can make customization to the nodes by passing these parameters to the function: node_size, node_color, node_shape, alpha, linewidths.


3 Answers

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.

like image 140
theOne Avatar answered Oct 23 '22 04:10

theOne


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.

like image 44
Aric Avatar answered Oct 23 '22 06:10

Aric


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

like image 1
CharlesW Avatar answered Oct 23 '22 06:10

CharlesW