2

I've been studying the other networkx plotting posts, but I've been having a hard time adapting them to my problem.

1) How do I create subplots with network graphs, without a PREDEFINED number of objects to plot? The function grabs this dynamically.

2) Is there an easy way to filter the network graph by limiting only those edges with over a weight of 2, for instance? or do I have to edit the network object itself to do so?

UPDATE #2: I figured out a way to filter by degree (see below). I'm wondering more generally if there are better ways to make my network data more understandable?

nol comes in the format [ [Year, networkobject], [Year, networkobject]]

def standardgraph_multiyear(nol, minimumdegree):
"""
Plots multiple graphs based on year
nol = takes in a LIST of [year, network object]
minimum = takes in a digit to filter nodes by degree
"""

#Each iteration prints a new subplot 
numrows = len(nol)
fig = plt.figure(figsize=(10,60))    
for i, val in enumerate(nol):
    gloc = numrows,1,i+1
    plt.subplot(numrows, 1, i+1)

    if minimumdegree > 0:
        outdeg = val[1].degree()
        to_keep = [n for n in outdeg if outdeg[n] > minimumdegree]
        mingraph = val[1].subgraph(to_keep)
        pos = nx.spring_layout(mingraph, iterations=200)
        nx.draw(mingraph, pos, font_size=8, with_labels=True)
        nx.draw_networkx_edges(mingraph, pos, alpha=.2)
        nx.draw_networkx_nodes(mingraph, pos, node_size=60, font_size =8, labels=True)
        nx.draw_networkx_labels(mingraph, pos, font_color='k', font_size=8)
        plt.title("Year {0}".format(val[0]), loc = 'center', fontsize=20)

    if minimumdegree == 0:
        outdeg = val[1].degree()
        to_keep = [n for n in outdeg if outdeg[n] > minimumdegree]
        mingraph = val[1].subgraph(to_keep)
        pos = nx.spring_layout(mingraph, iterations=200)
        nx.draw(mingraph, pos, font_size=8, with_labels=True)
        nx.draw_networkx_edges(mingraph, pos, alpha=.2)
        nx.draw_networkx_nodes(mingraph, pos, node_size=60, font_size =8, labels=True)
        nx.draw_networkx_labels(mingraph, pos, font_color='k', font_size=8)
        plt.title("Year {0}".format(val[0]), loc = 'center', fontsize=20)
return 

fig.savefig('out.png', dpi=100)

user3314418
  • 2,903
  • 9
  • 33
  • 55

1 Answers1

9

Your out of range error likely comes from the call to plt.subplot(221+i), since you don't seem to limit i to be <4; thus matplotlib will not know what subplot you intend to refer to?

(You also seem to have some conflicting code assembling the plots: a call to plt.subplots(1,1) and a later which requests a 2x2 grid).

In a different question I used the more basic plt.subplot(xxx) syntax to generate multiple subplots (following the four grids example from networkx). But you can also do it as shown below, setting the ax= keyword argument to an already existing set of axes. Note the call to sca() before rendering to each axis, which I needed to get this to work.

I've also shown one way to filter the edges that are shown below, and it does not require modifying the underlying graph: instead, you construct the edge lineweights you want based on the data from your graph, and use that as argument to draw_networkx_edges.

Edit (re updated question): the example code now includes a more explicit illustration of how to handle an unknown number of networks.

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np

n = 15; m = 40                        # graph size
L = np.random.choice(xrange(n), 2*m)  # select some edge destinations
weights = 0.5 + 5 * np.random.rand(m) # give each edge a weight


G = nx.Graph()                           # create a graph object
G.add_nodes_from(xrange(n))              # add n nodes to it
for i, (fr, to) in enumerate(zip(L[1::2], L[::2])): 
  G.add_edge(fr, to, weight=weights[i])  # add each edge

# use one of the edge properties to control line thickness
edgewidth = [ d['weight'] for (u,v,d) in G.edges(data=True)]

# and create a filtered version (still need an entry for each edge)
w_threshold = 2
edgewidth_filtered = [ d['weight'] if d['weight'] > w_threshold else 0
                      for (u,v,d) in G.edges(data=True)]

# alt. filtering - all edges that meet some criterion are displayed uniformly 
binary_filtered_edges = [ 1 if d['weight'] > w_threshold else 0
                      for (u,v,d) in G.edges(data=True)]

titles = [ 'edges drawn with lineweight=1', 'edge width from edge weight',
  'edge width from edge weight, only strong edges',
  'strong edges shown as lineweight=1', ]

edge_display_params = [ np.ones(len(edgewidth),), edgewidth,  
  edgewidth_filtered, binary_filtered_edges,]

# to illustrate drawing an unknown number of graphs, add some repeats repeats
n_extra = np.random.randint(0, 5)
indices = range(len(edge_display_params)) * 3
indices = indices[len(edge_display_params) + n_extra:]

# layout
pos = nx.spring_layout(G, iterations=50)
pos = nx.circular_layout(G)
#pos = nx.random_layout(G)

# rendering
fig = plt.figure(1); plt.clf()
# compute a grid size that will fit all graphs on it (couple blanks likely)
nr = int(np.ceil(np.sqrt(len(indices))))
fig, ax = plt.subplots(nr, nr, num=1)

for i, j in enumerate(indices):
    # dereference index into valid data (needed here since some repeated rather
    # than creating more, to illustrate handling unknown amount of data)
    k = indices[j]
    widths = edge_display_params[k]
    # compute index for the subplot, and set this subplot as current
    ix = np.unravel_index(i, ax.shape)
    plt.sca(ax[ix])
    # draw all nodes homogeneously, and edge weights as filtered
    nx.draw_networkx_nodes(G, pos, ax=ax[ix])
    nx.draw_networkx_edges(G, pos, width=widths, ax=ax[ix],)

    ax[ix].set_title(titles[k], fontsize=10)
    ax[ix].set_axis_off()

plt.show()

edge line thickness based on data

This example uses the same input graph four times over but obviously you could apply a single filter to different graphs (by filtering within the plotting loop) instead of applying different filters.


Below shows one run that created an extra 4 graphs, and so we have one unused pane:

unknown number of graphs

Community
  • 1
  • 1
Bonlenfum
  • 19,101
  • 2
  • 53
  • 56
  • Are you referring to the values in the `plt.subplots(2,2, num=1)` call? [subplots](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.subplots) is a convenience function to create several axis handles. The first two arguments are #rows, #cols, and the `num=1` is just to make it use figure number 1 (useful in interactive session so you don't create lots of figures). What about using `nr=int(np.ceil(np.sqrt(len(graph_list))); fig, ax = plt.subplots(nr, nr, num=1) ` to create a square grid that will be big enough to plot each graph in `graph_list`? – Bonlenfum Feb 20 '14 at 16:09
  • Hm, when I do that, I get a huge grid, but the plots all just go into one corner. I got some traction with this: plt.subplot(numrows, 1, i+1) where i is simply the index of the network object, numrows is the number of network objects for each year. However, each subplot comes out really squished. I tried adding a figsize keyword argument, but it gives me a syntax error. Appreciate your help! – user3314418 Feb 20 '14 at 16:38
  • The `figsize` argument is for `plt.figure()`, which can be passed as a keyword arg via `plt.subplots`, but not `plt.subplot`. My suggestion above uses the shape of `ax` to step through the indices appropriately, but note that the index inside the loop is `ix` rather than `i`. If this doesn't solve it could you edit your original question with some more detail/updates as the comment boxes aren't really the place for solving big stuff.. – Bonlenfum Feb 20 '14 at 17:16
  • Hi, I've got the figsize issue figured out, but now I'm trying to implement your edge filtering technique. I'm simply trying to implement option 3 (edgewidth_filtered), but I don't think I'm doing it correctly. Thanks in advance! – user3314418 Feb 20 '14 at 19:04
  • I figured out both my original issues, I think, but I realize my real question is how to best c – user3314418 Feb 20 '14 at 23:05
  • I figured out both issues, but I realize my 'real' question is how to clean my network graph to make it more readable. I've seen stackoverflow posts about 'pruning' but I'm wondering if there are similar others – user3314418 Feb 20 '14 at 23:05
  • I've updated the answer to include my above suggestion re: multiple figures. For your second update -- on how to make the meaning of a graph stand out more -- this is a distinct issue and you might be better off asking a new question. It is hard to do for the general case, but if you know *some* domain specifics then you could make some progress (e.g. if bridges are crucial to your network then highlighting edges with high betweenness centrality might help, or perhaps the nodes could be sized and or colored by property/class). Does that make sense? – Bonlenfum Feb 21 '14 at 17:55