Source code for epipack.networks

"""
Some network models and layout functions.
"""

import numpy as np

def _edge(u,v,w):
    u, v = sorted([u,v])
    return u, v, w

def _dist(i,j,N):
    return min(np.abs(i-j), N-np.abs(i-j))



[docs]def get_grid_layout(N_nodes,edge_weight_tuples=[],windowwidth=400,linkwidth=1): """ Returns a stylized network dictionary that puts nodes in a grid layout. Parameters ========== N_nodes : int The number of nodes in the network edge_weight_tuples : list, default = [] A list of tuples. Each tuple describes an edge with the first entry being the source node index, the second entry being the target node indes and the third entry being the weight, e.g. .. code:: python [ (0, 1, 1.0) ] windowwidth : float, default = 400 The width of the network visualization linkwidth : float, default = 1.0 All links get the same width. Returns ======= network : dict A stylized network dictionary in netwulf-format. """ w = h = windowwidth N = N_nodes N_side = int(np.ceil(np.sqrt(N))) dx = w / N_side radius = dx/2 network = {} stylized_network = { 'xlim': [0, w], 'ylim': [0, h], 'linkAlpha': 0.5, 'nodeStrokeWidth': 0.0001, } nodes = [ { 'id': i*N_side + j, 'x_canvas': (i+0.5)*dx, 'y_canvas': (j+0.5)*dx, 'radius': radius } for i in range(N_side) for j in range(N_side) ] nodes = nodes[:N_nodes] links = [ {'source': u, 'target': v, 'width': linkwidth} for u, v, w in edge_weight_tuples ] stylized_network['nodes'] = nodes stylized_network['links'] = links return stylized_network
[docs]def get_random_layout(N_nodes, edge_weight_tuples=[], windowwidth=400, linkwidth=1, node_scale_by_degree=0.5, circular=True, ): """ Returns a stylized network dictionary that puts nodes in a random layout. Parameters ========== N_nodes : int The number of nodes in the network edge_weight_tuples : list, default = [] A list of tuples. Each tuple describes an edge with the first entry being the source node index, the second entry being the target node index and the third entry being the weight, e.g. .. code:: python [ (0, 1, 1.0) ] windowwidth : float, default = 400 The width of the network visualization linkwidth : float, default = 1.0 All links get the same width. node_scale_by_degree : float, default = 0.5 Scale the node radius by ``degree**node_scale_by_degree``. Per default, the node disk area will be proportional to the degree. If you want all nodes to be equally sized, set ``node_scale_by_degree = 0``. circular : bool, default = True Use a circular or square layout Returns ======= network : dict A stylized network dictionary in netwulf-format. """ w = h = windowwidth N = N_nodes N_side = int(np.ceil(np.sqrt(N))) dx = w / N_side radius = dx/3 network = {} stylized_network = { 'xlim': [0, w], 'ylim': [0, h], 'linkAlpha': 0.5, 'nodeStrokeWidth': 0.0001, } degree = np.zeros(N,) for u, v, _w in edge_weight_tuples: degree[u] += 1 degree[v] += 1 median_degree = np.median(degree) if median_degree == 0: median_degree = 1 radius_scale = (degree/median_degree)**node_scale_by_degree radius_scale[radius_scale==0] = 1.0 radius = radius_scale * radius if not circular: pos = (np.random.rand(N, 2) * w).tolist() else: R = w/2 r = R*np.sqrt(np.random.random(N,)) t = 2*np.pi*np.random.random(N,) pos = np.zeros((N, 2)) pos[:,0] = r*np.cos(t) + w/2 pos[:,1] = r*np.sin(t) + w/2 nodes = [ { 'id': i, 'x_canvas': pos[0], 'y_canvas': pos[1], 'radius': radius[i], } for i, pos in enumerate(pos)] nodes = nodes[:N_nodes] links = [ {'source': u, 'target': v, 'width': linkwidth} for u, v, w in edge_weight_tuples ] stylized_network['nodes'] = nodes stylized_network['links'] = links return stylized_network
[docs]def get_small_world_layout( N_nodes, edge_weight_tuples=[], windowwidth=400, linkwidth=1, node_scale_by_degree=2, nbounce=20, Rbounce=0.1, R=0.8, ): """ Returns a stylized network dictionary that puts nodes in a small-world inspired circular layout. The ring of nodes will be drawn bouncy to better display connections between nearby regions. Nodes that connect far-away regions of a network will be displayed more centrally. Distance is defined as lattice distance by integer node id. Hence, nodes must be integers in [0,N). Parameters ========== N_nodes : int The number of nodes in the network edge_weight_tuples : list, default = [] A list of tuples. Each tuple describes an edge with the first entry being the source node index, the second entry being the target node index and the third entry being the weight, e.g. .. code:: python [ (0, 1, 1.0) ] windowwidth : float, default = 400 The width of the network visualization linkwidth : float, default = 1.0 All links get the same width. node_scale_by_degree : float, default = 2 Scale the node radius by ``degree**node_scale_by_degree``. If you want all nodes to be equally sized, set ``node_scale_by_degree = 0``. nbounce : int, default = 20 How wobbly the outer shell should be. Rbounce : int, default = 0.1 How thick the outer shell should be (in units of half window width) R : float, default = 0.8 How large the radius of the whole layout should be (in units of half window width) Returns ======= network : dict A stylized network dictionary in netwulf-format. """ w = h = windowwidth N = N_nodes N_side = int(np.ceil(np.sqrt(N))) dx = w / N_side radius = dx/3 Rbounce /= R R = R * w/2 Rbounce *= R network = {} stylized_network = { 'xlim': [0, w], 'ylim': [0, h], 'linkAlpha': 0.5, 'nodeStrokeWidth': 0.0001, } degree = np.zeros(N,) node_distances = [[] for n in range(N)] for u, v, _w in edge_weight_tuples: node_distances[u].append(_dist(u,v,N)) node_distances[v].append(_dist(u,v,N)) degree[u] += 1 degree[v] += 1 median_degree = np.median(degree) if median_degree == 0: median_degree = 1 radius_scale = (degree/median_degree)**node_scale_by_degree radius_scale[radius_scale==0] = 1.0 radius = radius_scale * radius node_distance_maxs = [ np.max(dists+[0]) for dists in node_distances ] distance_measure = node_distance_maxs dphi = 2*np.pi/N maxmean = 3*N/4 pos = np.zeros((N, 2)) for n in range(N): phi = n*dphi rbase = R+Rbounce*np.sin(nbounce*phi) r = rbase*(1-distance_measure[n]/maxmean) x = r*np.cos(phi) + w/2 y = r*np.sin(phi) + w/2 pos[n,:] = x, y nodes = [ { 'id': i, 'x_canvas': pos[0], 'y_canvas': pos[1], 'radius': radius[i], } for i, pos in enumerate(pos)] nodes = nodes[:N_nodes] links = [ {'source': u, 'target': v, 'width': linkwidth} for u, v, w in edge_weight_tuples ] stylized_network['nodes'] = nodes stylized_network['links'] = links return stylized_network