15

Using Networkx in Python, I'm trying to visualise how different movie critics are biased towards certain production companies. To show this in a graph, my idea is to fix the position of each production-company-node to an individual location in a circle, and then use the spring_layout algorithm to position the remaining movie-critic-nodes, such that one can easily see how some critics are drawn more towards certain production companies.

My problem is that I can't seem to fix the initial position of the production-company-nodes. Surely, I can fix their position but then it is just random, and I don't want that - I want them in a circle. I can calculate the position of all nodes and afterwards set the position of the production-company-nodes, but this beats the purpose of using a spring_layout algorithm and I end up with something wacky like:

enter image description here

Any ideas on how to do this right?

Currently my code does this:

def get_coordinates_in_circle(n):
    return_list = []
    for i in range(n):
        theta = float(i)/n*2*3.141592654
        x = np.cos(theta)
        y = np.sin(theta)
        return_list.append((x,y))
    return return_list

G_pc = nx.Graph()
G_pc.add_edges_from(edges_2212)

fixed_nodes = []
for n in G_pc.nodes():
    if n in production_companies:
        fixed_nodes.append(n)

pos = nx.spring_layout(G_pc,fixed=fixed_nodes)

circular_positions = get_coordinates_in_circle(len(dps_2211))
i = 0
for p in pos.keys():
    if p in production_companies:
        pos[p] = circular_positions[i]
        i += 1

colors = get_node_colors(G_pc, "gender")

nx.draw_networkx_nodes(G_pc, pos, cmap=plt.get_cmap('jet'), node_color=colors, node_size=50, alpha=0.5)
nx.draw_networkx_edges(G_pc,pos, alpha=0.01)
plt.show()
Ulf Aslak
  • 7,876
  • 4
  • 34
  • 56

1 Answers1

26

To create a graph and set a few positions:

import networkx as nx
G=nx.Graph()
G.add_edges_from([(1,2),(2,3),(3,1),(1,4)]) #define G
fixed_positions = {1:(0,0),2:(-1,2)}#dict with two of the positions set
fixed_nodes = fixed_positions.keys()
pos = nx.spring_layout(G,pos=fixed_positions, fixed = fixed_nodes)
nx.draw_networkx(G,pos)

enter image description here

Your problem appears to be that you calculate the positions of all the nodes before you set the positions of the fixed nodes.

Move pos = nx.spring_layout(G_pc,fixed=fixed_nodes) to after you set pos[p] for the fixed nodes, and change it to pos = nx.spring_layout(G_pc,pos=pos,fixed=fixed_nodes)

The dict pos stores the coordinates of each node. You should have a quick look at the documentation. In particular,

pos : dict or None optional (default=None). Initial positions for nodes as a dictionary with node as keys and values as a list or tuple. If None, then nuse random initial positions.

fixed : list or None optional (default=None). Nodes to keep fixed at initial position. list or None optional (default=None)

You're telling it to keep those nodes fixed at their initial position, but you haven't told them what that initial position should be. So I would believe it takes a random guess for that initial position, and holds it fixed. However, when I test this, it looks like I run into an error. It appears that if I tell (my version of) networkx to hold nodes in [1,2] as fixed, but I don't tell it what their positions are, I get an error (at bottom of this answer). So I'm surprised your code is running.


For some other improvements to the code using list comprehensions:

def get_coordinates_in_circle(n):
    thetas = [2*np.pi*(float(i)/n) for i in range(n)]
    returnlist = [(np.cos(theta),np.sin(theta)) for theta in thetas]
    return return_list

G_pc = nx.Graph()
G_pc.add_edges_from(edges_2212)
circular_positions = get_coordinates_in_circle(len(dps_2211))
#it's not clear to me why you don't define circular_positions after
#fixed_nodes with len(fixed_nodes) so that they are guaranteed to 
#be evenly spaced.

fixed_nodes = [n for n in G_pc.nodes() if n in production_companies]

pos = {}
for i,p in enumerate(fixed_nodes):
    pos[p] = circular_positions[i]

colors = get_node_colors(G_pc, "gender")
pos = nx.spring_layout(G_pc,pos=pos, fixed=fixed_nodes)
nx.draw_networkx_nodes(G_pc, pos, cmap=plt.get_cmap('jet'), node_color=colors, node_size=50, alpha=0.5)
nx.draw_networkx_edges(G_pc,pos, alpha=0.01)
plt.show()

Here's the error I see:

import networkx as nx
G=nx.Graph()
G.add_edge(1,2)
pos = nx.spring_layout(G, fixed=[1,2])

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-4-e9586af20cc2> in <module>()
----> 1 pos = nx.spring_layout(G, fixed=[1,2])

.../networkx/drawing/layout.pyc in fruchterman_reingold_layout(G, dim, k, pos, fixed, iterations, weight, scale)
    253            # We must adjust k by domain size for layouts that are not near 1x1
    254            nnodes,_ = A.shape
--> 255            k=dom_size/np.sqrt(nnodes)
    256         pos=_fruchterman_reingold(A,dim,k,pos_arr,fixed,iterations)
    257     if fixed is None:

UnboundLocalError: local variable 'dom_size' referenced before assignment
Joel
  • 22,598
  • 6
  • 69
  • 93
  • Thanks for your answer. What error do you get? To me it looks like you are adding positions to pos before you have declared it. And to address your code comment, the positions will be evenly spaces because len(dps_2211) == len(fixed_nodes). I understand why this is confusing. BUT your solution works. Turns out that the important thing was just to create 'pos' and add positions to it and then calculate it afterwards using the 'pos' you just created. Thanks – Ulf Aslak May 04 '15 at 20:44
  • You were correct about adding things to `pos` before declaring (I approved your edit). I'll add the error I get at the bottom of my answer. – Joel May 04 '15 at 20:57