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
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()

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:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With