Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

networkx - change node size based on list or dictionary value

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.

like image 823
st.ph.n Avatar asked Jul 08 '14 15:07

st.ph.n


2 Answers

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

like image 96
mdml Avatar answered Oct 25 '22 03:10

mdml


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

like image 41
st.ph.n Avatar answered Oct 25 '22 03:10

st.ph.n