Source code for EoN.auxiliary

import networkx as nx
import EoN
import numpy as np
import random

[docs]def subsample(report_times, times, status1, status2=None, status3 = None): r''' Given a list/array of times to report at, returns the number of nodes of each status at those times. returns them subsampled at specific report_times. :Arguments: **report_times** iterable (ordered) times at which we want to know state of system **times** iterable (ordered) times at which we have the system state (assumed no change between these times) **status1** iterable generally S, I, or R number of nodes in given status at corresponding time in times. **status2** iterable (optional, default None) generally S, I, or R number of nodes in given status at corresponding time in times. **status3** iterable (optional, default None) generally S, I, or R number of nodes in given status at corresponding time in times. :Returns: If only status1 is defined **report_status1** numpy array gives ``status1`` subsampled just at ``report_times``. If more are defined then it returns a list, either **[report_status1, report_status2]** or **[report_status1, report_status2, report_status3]** In each case, these are subsampled just at report_times. :SAMPLE USE: :: import networkx as nx import EoN import numpy as np import matplotlib.pyplot as plt """ in this example we will run 100 stochastic simulations. Each simulation will produce output at a different set of times. In order to calculate an average we will use subsample to find the epidemic sizes at a specific set of times given by report_times. """ G = nx.fast_gnp_random_graph(10000,0.001) tau = 1. gamma = 1. report_times = np.linspace(0,5,101) Ssum = np.zeros(len(report_times)) Isum = np.zeros(len(report_times)) Rsum = np.zeros(len(report_times)) iterations = 100 for counter in range(iterations): t, S, I, R = EoN.fast_SIR(G, tau, gamma, initial_infecteds = range(10)) #t, S, I, and R have an entry for every single event. newS, newI, newR = EoN.subsample(report_times, t, S, I, R) #could also do: newI = EoN.subsample(report_times, t, I) plt.plot(report_times, newS, linewidth=1, alpha = 0.4) plt.plot(report_times, newI, linewidth=1, alpha = 0.4) plt.plot(report_times, newR, linewidth=1, alpha = 0.4) Ssum += newS Isum += newI Rsum += newR Save = Ssum / float(iterations) Iave = Isum / float(iterations) Rave = Rsum / float(iterations) plt.plot(report_times, Save, "--", linewidth = 5, label = "average") plt.plot(report_times, Iave, "--", linewidth = 5) plt.plot(report_times, Rave, "--", linewidth = 5) plt.legend(loc = "upper right") plt.savefig("tmp.pdf") If only one of the sample times is given then returns just that. If report_times goes longer than times, then this simply assumes the system freezes in the final state. This uses a recursive approach if multiple arguments are defined. ''' if report_times[0] < times[0]: raise EoN.EoNError("report_times[0]<times[0]") report_status1 = [] next_report_index = 0 next_observation_index = 0 while next_report_index < len(report_times): while next_observation_index < len(times) and \ times[next_observation_index]<= report_times[next_report_index]: candidate = status1[next_observation_index] next_observation_index += 1 report_status1.append(candidate) next_report_index +=1 report_status1= np.array(report_status1) if status2 is not None: if status3 is not None: report_status2, report_status3 = subsample(report_times, times, status2, status3) return report_status1, report_status2, report_status3 else: report_status2 = subsample(report_times, times, status2) return report_status1, report_status2 else: return report_status1
[docs]def get_time_shift(times, L, threshold): r''' Identifies the first time at which list/array L crosses a threshold. Useful for shifting times. :Arguments: **times** list or numpy array (ordered) the times we have observations **L** a list or numpy array order of L corresponds to times **threshold** number the threshold value :Returns: **t** number the first time at which L reaches or exceeds threshold. :SAMPLE USE: :: import networkx as nx import EoN import numpy as np import matplotlib.pyplot as plt """ in this example we will run 20 stochastic simulations. We plot the unshifted curves (grey) and the curves shifted so that t=0 when 1% have been infected (I+R = 0.01N) (red) """ plt.clf() # just clearing any previous plotting. N=100000 kave = 10. G = nx.fast_gnp_random_graph(N,kave/(N-1.)) tau = 0.2 gamma = 1. report_times = np.linspace(0,5,101) Ssum = np.zeros(len(report_times)) Isum = np.zeros(len(report_times)) Rsum = np.zeros(len(report_times)) iterations = 20 for counter in range(iterations): R=[0] while R[-1]<1000: #if an epidemic doesn't happen, repeat t, S, I, R = EoN.fast_SIR(G, tau, gamma) print R[-1] plt.plot(t, I, linewidth = 1, color = 'gray', alpha=0.4) tshift = EoN.get_time_shift(t, I+R, 0.01*N) plt.plot(t-tshift, I, color = 'red', linewidth = 1, alpha = 0.4) plt.savefig("timeshift_demonstration.pdf") ''' for index, t in enumerate(times): if L[index]>= threshold: break return t
[docs]def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, leaf_vs_root_factor = 0.5): ''' If the graph is a tree this will return the positions to plot this in a hierarchical layout. Based on Joel's answer at https://stackoverflow.com/a/29597209/2966723, but with some modifications. We include this because it may be useful for plotting transmission trees, and there is currently no networkx equivalent (though it may be coming soon). There are two basic approaches we think of to allocate the horizontal location of a node. - Top down: we allocate horizontal space to a node. Then its ``k`` descendants split up that horizontal space equally. This tends to result in overlapping nodes when some have many descendants. - Bottom up: we allocate horizontal space to each leaf node. A node at a higher level gets the entire space allocated to its descendant leaves. Based on this, leaf nodes at higher levels get the same space as leaf nodes very deep in the tree. We use use both of these approaches simultaneously with ``leaf_vs_root_factor`` determining how much of the horizontal space is based on the bottom up or top down approaches. ``0`` gives pure bottom up, while 1 gives pure top down. :Arguments: **G** the graph (must be a tree) **root** the root node of the tree - if the tree is directed and this is not given, the root will be found and used - if the tree is directed and this is given, then the positions will be just for the descendants of this node. - if the tree is undirected and not given, then a random choice will be used. **width** horizontal space allocated for this branch - avoids overlap with other branches **vert_gap** gap between levels of hierarchy **vert_loc** vertical location of root **leaf_vs_root_factor** xcenter: horizontal location of root ''' if not nx.is_tree(G): raise TypeError('cannot use hierarchy_pos on a graph that is not a tree') if root is None: if isinstance(G, nx.DiGraph): root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11 else: root = random.choice(list(G.nodes)) def _hierarchy_pos(G, root, leftmost, width, leafdx = 0.2, vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, rootpos = None, leafpos = None, parent = None): ''' see hierarchy_pos docstring for most arguments pos: a dict saying where all nodes go if they have been assigned parent: parent of this branch. - only affects it if non-directed ''' if rootpos is None: rootpos = {root:(xcenter,vert_loc)} else: rootpos[root] = (xcenter, vert_loc) if leafpos is None: leafpos = {} children = list(G.neighbors(root)) leaf_count = 0 if not isinstance(G, nx.DiGraph) and parent is not None: children.remove(parent) if len(children)!=0: rootdx = width/len(children) nextx = xcenter - width/2 - rootdx/2 for child in children: nextx += rootdx rootpos, leafpos, newleaves = _hierarchy_pos(G,child, leftmost+leaf_count*leafdx, width=rootdx, leafdx=leafdx, vert_gap = vert_gap, vert_loc = vert_loc-vert_gap, xcenter=nextx, rootpos=rootpos, leafpos=leafpos, parent = root) leaf_count += newleaves leftmostchild = min((x for x,y in [leafpos[child] for child in children])) rightmostchild = max((x for x,y in [leafpos[child] for child in children])) leafpos[root] = ((leftmostchild+rightmostchild)/2, vert_loc) else: leaf_count = 1 leafpos[root] = (leftmost, vert_loc) # pos[root] = (leftmost + (leaf_count-1)*dx/2., vert_loc) # print(leaf_count) return rootpos, leafpos, leaf_count xcenter = width/2. if isinstance(G, nx.DiGraph): leafcount = len([node for node in nx.descendants(G, root) if G.out_degree(node)==0]) elif isinstance(G, nx.Graph): leafcount = len([node for node in nx.node_connected_component(G, root) if G.degree(node)==1 and node != root]) rootpos, leafpos, leaf_count = _hierarchy_pos(G, root, 0, width, leafdx=width*1./leafcount, vert_gap=vert_gap, vert_loc = vert_loc, xcenter = xcenter) pos = {} for node in rootpos: pos[node] = (leaf_vs_root_factor*leafpos[node][0] + (1-leaf_vs_root_factor)*rootpos[node][0], leafpos[node][1]) # pos = {node:(leaf_vs_root_factor*x1+(1-leaf_vs_root_factor)*x2, y1) for ((x1,y1), (x2,y2)) in (leafpos[node], rootpos[node]) for node in rootpos} xmax = max(x for x,y in pos.values()) for node in pos: pos[node]= (pos[node][0]*width/xmax, pos[node][1]) return pos