12

I'm trying to make a graph in networkx. I'm having trouble assigning different node sizes to the nodes.

Here is my code I've been playing with:

import sys
from collections import defaultdict
import networkx as nx
import matplotlib.pyplot as plt

inp = sys.argv[1]
cluster = sys.argv[1] + ".cluster"
counts = sys.argv[1] + ".counts"

with open(cluster, "r") as f1:
        edges = [line.strip().split('\t') for line in f1]

with open(counts, "r") as f2:
        countsdic = defaultdict(list)
        for line in f2:
                k,v = line.strip().split()
                countsdic[k].append(v)

tmp = []

for el in sum(edges, []):
        tmp.append(el)

nodes = []

for t in tmp:
        if t not in nodes:
                nodes.append(t)

node_sizes = {}
for n in nodes:
        node_sizes[n] = ' '.join(countsdic[n])
print node_sizes

nodes2 = []
sizes = []
for k in node_sizes.keys():
        nodes2.append(k)
for v in node_sizes.values():
        sizes.append(v)
print nodes2
print len(nodes2)
print sizes
print len(sizes)
g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)

nx.draw_random(g, node_list = nodes2, node_size = sizes)

# I've also tried assigning node_list and node_size with node_sizes.keys() and node_sizes.values()

plt.savefig(inp + "." + gtype + ".png")
plt.show()

If I do not attempt to change the node sizes, I get a pretty decent graph. The dictionary values are between 1 and 10, with a few high values like 156 which I need to be the largest, so I would need to do something like: node_sizes = [n*100 for n in sizes] for the smaller values to at least appear on the graph and the larger values to appear relevant, but that didn't work either.

The error I get is:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in __call__
    return self.func(*args)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 276, in resize
    self.show()
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 348, in draw
    FigureCanvasAgg.draw(self)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_agg.py", line 451, in draw
    self.figure.draw(self.renderer)
  File "/usr/lib/pymodules/python2.7/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/figure.py", line 1034, in draw
    func(*args)
  File "/usr/lib/pymodules/python2.7/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/axes.py", line 2086, in draw
    a.draw(renderer)
  File "/usr/lib/pymodules/python2.7/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/collections.py", line 717, in draw
    for x in self._sizes]
TypeError: Not implemented for this type

After a couple hours of google searching, I am unable to resolve the issue. Here is a generated without changing the node sizes:

An example output of the graph where all nodes are the same size. (NEED ALL NODES RELATIVE TO VALUE SIZE

All comments and help are appreciated.

st.ph.n
  • 549
  • 2
  • 5
  • 19

2 Answers2

19

2014/07/08 12:29PM: Updated to reflect comments from @user3358205

The problem is that the drawing functions in NetworkX require node_sizes to be input as a list of ints, while you are passing a list of strings. You can read the parameters to the drawing functions here.

Because I don't have the input files to your program, I can't reproduce your output. However, here's an example where you vary the size of the nodes by passing a list of node_sizes. Note that in the output, I am labeling each node by their size.

import sys, networkx as nx, matplotlib.pyplot as plt

# Create a list of 10 nodes numbered [0, 9]
nodes = range(10)
node_sizes = []
labels = {}
for n in nodes:
        node_sizes.append( 100 * n )
        labels[n] = 100 * n

# Node sizes: [0, 100, 200, 300, 400, 500, 600, 700, 800, 900]

# Connect each node to its successor
edges = [ (i, i+1) for i in range(len(nodes)-1) ]

# Create the graph and draw it with the node labels
g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)

nx.draw_random(g, node_size = node_sizes, labels=labels, with_labels=True)    
plt.show()

example graph

mdml
  • 22,442
  • 8
  • 58
  • 66
  • Thanks for your answer mdml. So, where I appended the values from the dictionary to a new list called sizes, it did not work, however it was a list. Can you possibly explain why it did not? – st.ph.n Jul 08 '14 at 16:05
  • @user3358205: was it the same error message? If so, can you perhaps [simplify the code in your question](http://stackoverflow.com/help/mcve) (including the input)? – mdml Jul 08 '14 at 16:07
  • I think I figured out why my code wasn't working. I was appending strings from the dictionary node_sizes.values() to the list sizes, not integers. – st.ph.n Jul 08 '14 at 16:11
  • Now using random, those nodes which correspond to large sizes, cover up some of the other smaller nodes. Do you know off hand he best way, or drawing method, to position the nodes? – st.ph.n Jul 08 '14 at 16:13
  • @user3358205: makes sense, you should post an answer so folks know you've solved it. And I'd try a few different types of layouts (e.g. `draw_spring`) and see which one works best. – mdml Jul 08 '14 at 16:15
4

With mdml's answer I was able to answer solve the problem. As it turns out I was passing a list to networkx for node sizes, although it did not appreciate the list. I was appending strings to the list, not integers. Changing v to and int() solves this problem, then I multiplied by 100, since some of the values were small, to make the node sizes relevant:

with open(cluster, "r") as f1:
     edges = [line.strip().split('\t') for line in f1]

with open(counts, "r") as f2:
     countsdic = defaultdict(list)
     for line in f2:
         k,v = line.strip().split()
         countsdic[k].append(v)

tmp = []

for el in sum(edges, []):
    tmp.append(el)

nodes = []

for t in tmp:
    if t not in nodes:
        nodes.append(t)

node_sizes = {}
for n in nodes:
    node_sizes[n] = ' '.join(countsdic[n])

sizes = []
for v in node_sizes.values():
    x = int(v) * 100
    sizes.append(x)

g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)

nx.draw_random(g, node_size = sizes)

plt.savefig(inp + "." + gtype + ".png")
plt.show()

The graph output I was looking for:

enter image description here

sophiadw
  • 557
  • 3
  • 10
st.ph.n
  • 549
  • 2
  • 5
  • 19