total_nodes = 10 adjacency_matrix = np.random.rand(total_nodes, total_nodes) < 0.25 weight_matrix = np.random.randn(total_frames, total_nodes, total_nodes) # Normalise the weights, such that they are on the interval [0, 1]. # They can then be passed directly to matplotlib colormaps (which expect floats on that interval). vmin, vmax = -2, 2 weight_matrix[weight_matrix < vmin] = vmin weight_matrix[weight_matrix > vmax] = vmax weight_matrix -= vmin weight_matrix /= vmax - vmin cmap = plt.cm.RdGy fig, ax = plt.subplots() g = Graph(adjacency_matrix, edge_cmap=cmap, arrows=True, ax=ax) def update(ii): artists = [] for jj, kk in zip(*np.where(adjacency_matrix)): w = weight_matrix[ii, jj, kk] artist = g.edge_artists[(jj, kk)] artist.set_facecolor(cmap(w)) artist.update_width( 0.03 * np.abs(w - 0.5) ) # np.abs(w-0.5) so that large negative edges are also wide artists.append(artist) return artists
- :code:`edge_color` : edge face colour - :code:`edge_alpha` : edge transparency - :code:`edge_zorder` : node zorder; artists with higher z-order occlude artists with lower z-order - :code:`arrows` : boolean flag that turn the drawing of arrow heads on or off All node and edge artist properties can be specified in three ways: """ ################################################################################ # 1. Using a single scalar or string that will be applied to all artists. import matplotlib.pyplot as plt from netgraph import Graph edges = [(0, 1), (1, 1)] Graph(edges, node_color='red', node_size=4.) plt.show() ################################################################################ # 2. Using a dictionary mapping individual nodes or individual edges to a property: import numpy as np import matplotlib.pyplot as plt from netgraph import Graph Graph( [(0, 1), (1, 2), (2, 0)], edge_color={ (0, 1): 'g', (1, 2): 'lightblue', (2, 0): np.array([1, 0, 0])
#!/usr/bin/env python """ Default Design ============== Default visualisation for an unweighted graph. """ import matplotlib.pyplot as plt from netgraph import Graph cube = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (2, 6), (1, 5), (3, 7)] Graph(cube) plt.show()
=============== """ import matplotlib.pyplot as plt import networkx as nx from netgraph import Graph # create a random geometric graph and plot it g = nx.random_geometric_graph(100, 0.15, seed=42) node_positions = nx.get_node_attributes(g, 'pos') plot_instance = Graph(g, node_layout=node_positions, node_size=1, node_edge_width=0.1, edge_width=0.1) # select a random path in the network and plot it path = nx.shortest_path(g, 33, 66) for node in path: plot_instance.node_artists[node].radius = 1.5 * 1e-2 plot_instance.node_artists[node].set_color('orange') for ii, node_1 in enumerate(path[:-1]): node_2 = path[ii+1] if (node_1, node_2) in plot_instance.edges: edge = (node_1, node_2) else: # the edge is specified in reverse node order
#!/usr/bin/env python """ Curved Edges ============ Curved edges are repelled by nodes and other edges. This reduces node/edge and edge/edge occlusions. """ import matplotlib.pyplot as plt import networkx as nx from netgraph import Graph Graph(nx.wheel_graph(7), edge_layout='curved') plt.show()
#!/usr/bin/env python """ Dot and radial node layouts =========================== Plot a tree or other directed, acyclic graph with the :code:`'dot'` or :code:`'radial'` node layout. Netgraph uses an implementation of the Sugiyama algorithm provided by the grandalf_ library (and thus does not require Graphviz to be installed). .. _grandalf: https://github.com/bdcht/grandalf """ import matplotlib.pyplot as plt import networkx as nx from netgraph import Graph unbalanced_tree = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (2, 6), (3, 7), (3, 8), (4, 9), (4, 10), (4, 11), (5, 12), (5, 13), (5, 14), (5, 15)] balanced_tree = nx.balanced_tree(3, 3) fig, (ax1, ax2) = plt.subplots(1, 2) Graph(unbalanced_tree, node_layout='dot', ax=ax1) Graph(balanced_tree, node_layout='radial', ax=ax2) plt.show()
""" import numpy as np import matplotlib.pyplot as plt from netgraph import Graph weighted_cube = [ (0, 1, -0.1), (1, 2, -0.2), (2, 3, -0.4), (3, 2, 0.0), (3, 0, -0.2), (4, 5, 0.7), (5, 6, 0.9), (6, 5, -0.2), (6, 7, 0.5), (7, 4, 0.1), (0, 4, 0.5), (1, 5, -0.3), (5, 1, -0.4), (2, 6, 0.8), (3, 7, 0.4) ] edge_width = {(u, v) : 3 * np.abs(w) for (u, v, w) in weighted_cube} edge_color = {(u, v) : 'blue' if w <=0 else 'red' for (u, v, w) in weighted_cube} Graph(weighted_cube, edge_width=edge_width, edge_color=edge_color, arrows=True) plt.show()
def plot_reduced_network_topology(ax, network_type, A_reduced, m, color, color_bias, node_size_bias, title_letter): """TODO: Docstring for plot_network_topology. :ax: TODO :A_unit: TODO :returns: TODO """ ax.annotate(title_letter, xy=(-0.2, 1.03), xycoords="axes fraction", size=labelsize * 0.7) cmap = sns.color_palette(color, as_cmap=True) G = nx.from_numpy_array(A_reduced) k = np.sum(A_reduced, 1) node_size = np.log(k + node_size_bias) node_size_dict = {u: v for u, v in zip(np.arange(len(k)), node_size)} node_color_dict = { u: cmap(k_i / k.max() + color_bias) for u, k_i in zip(np.arange(len(k)), k) } edgelists = [(ni, nj) for ni in range(m) for nj in range(m) if A_reduced[ni, nj]] if m == 1: node_layout = {0: (0.7, 0.5)} node_size_dict = {0: 15} radius = 0.2 edge_width = 5 node_edge_width = 3 ax.set_xlim(0, 1) ax.set_ylim(0, 1) else: node_layout = 'spring' node_sizes = np.log(k + 1) / m**0.3 * 2.5 node_size_dict = {u: node_sizes[u] for u in np.arange(len(k))} radius = 0.07 / m**0.25 widths = np.log(A_reduced / A_reduced.max() + 1.3) * 2 widths = np.log(A_reduced / A_reduced.max() + 1) * 80 / m**1.5 edge_width = {(u, v): widths[v, u] * 1 if u == v else widths[v, u] for (u, v) in edgelists} node_edge_width = 1 if m == 3: node_layout = {0: (0.3, 0.2), 1: (0.7, 0.2), 2: (0.5, 0.6)} node_layout = { key: (x * (np.random.random() * 0.1 + 1), y * (np.random.random() * 0.1 + 1)) for key, (x, y) in node_layout.items() } node_sizes = np.log(k + 1) * 2 node_size_dict = {u: node_sizes[u] for u in np.arange(len(k))} radius = 0.08 widths = np.log(A_reduced / A_reduced.max() + 1.5) * 10 edge_width = {(u, v): widths[v, u] * 0.5 if u == v else widths[v, u] for (u, v) in edgelists} elif m == 5: if network_type == 'SF': node_layout = { 0: (0.5, 0.45), 1: (0.2, 0.25), 2: (0.6, 0.2), 3: (0.6, 0.75), 4: (0.3, 0.55) } else: node_layout = { 0: (0.3, 0.4), 1: (0.7, 0.4), 2: (0.5, 0.6), 3: (0.4, 0.2), 4: (0.6, 0.2) } node_layout = { key: (x * (np.random.random() * 0.1 + 1), y * (np.random.random() * 0.1 + 1)) for key, (x, y) in node_layout.items() } #node_layout=nx.spring_layout(G) node_sizes = np.log(k + 3) * 1.8 node_size_dict = {u: node_sizes[u] for u in np.arange(len(k))} radius = 0.06 widths = np.log(A_reduced / A_reduced.max() + 1.3) * 5 edge_width = {(u, v): widths[v, u] * 0.5 if u == v else widths[v, u] for (u, v) in edgelists} g = Graph(edgelists, edge_width=edge_width, arrows=True, node_layout=node_layout, node_size=node_size_dict, edge_layout='straight', node_color=node_color_dict, node_alpha=0.8, edge_color='tab:grey', edge_alp=0.5, ax=ax, edge_layout_kwargs={'selfloop_radius': radius}) xy_position = np.vstack((g.node_positions.values())) x_max, x_min = xy_position[:, 0].max() + radius * 2, xy_position[:, 0].min( ) - radius * 2.0 y_max, y_min = xy_position[:, 1].max() + radius * 2, xy_position[:, 1].min( ) - radius * 2.0
from matplotlib.animation import FuncAnimation from netgraph import Graph # Simulate a dynamic network with # - 5 frames / different node states, # - with 10 nodes at each time point, and # - an expected edge density of 25%. total_frames = 5 total_nodes = 10 adjacency_matrix = np.random.rand(total_nodes, total_nodes) < 0.25 node_values = np.random.rand(total_frames, total_nodes) cmap = plt.cm.viridis fig, ax = plt.subplots() g = Graph(adjacency_matrix, edge_width=1.5, arrows=True, ax=ax) ax.axis([0, 1, 0, 1]) def update(ii): for node, artist in g.node_artists.items(): value = node_values[ii, node] artist.set_facecolor(cmap(value)) artist.set_edgecolor(cmap(value)) # The default node size is 3., which is rescaled internally # to 0.03 to yield layouts comparable to networkx and igraph. # As the expectation of `value` is 0.5, we multiply `value` by 6 * 0.01, # and thus match the default node size on average. artist.radius = 6 * 0.01 * value return g.node_artists.values()
from matplotlib.animation import FuncAnimation from netgraph import Graph # Simulate a dynamic network with # - 5 frames / network states, # - with 10 nodes at each time point, # - an expected edge density of 25% at each time point total_frames = 5 total_nodes = 10 adjacency_matrix = np.random.rand(total_frames, total_nodes, total_nodes) < 0.25 fig, ax = plt.subplots() g = Graph(np.ones((total_nodes, total_nodes)), edge_width=1.5, arrows=True, ax=ax) # initialise with fully connected graph def update(ii): for (jj, kk), artist in g.edge_artists.items(): # turn visibility of edge artists on or off, depending on the adjacency if adjacency_matrix[ii, jj, kk]: artist.set_visible(True) else: artist.set_visible(False) return g.edge_artists.values() animation = FuncAnimation(fig, update,
def plot_network_topology(ax, network_type, N, A_unit, color, color_bias, node_size_bias, title_letter): """TODO: Docstring for plot_network_topology. :ax: TODO :A_unit: TODO :returns: TODO """ simpleaxis(ax) ax.annotate(title_letter, xy=(-0.2, 1.03), xycoords="axes fraction", size=labelsize * 0.7) cmap = sns.color_palette(color, as_cmap=True) N_actual = len(A_unit) k = np.sum(A_unit > 0, 0) if network_type == 'SF': edge_width = 1.0 node_size = k**0.25 * 2.1 elif network_type == 'ER': edge_width = 0.2 node_size = k**0.2 * 2 else: node_size = k**0.2 * 2 edge_width = 2.5 node_size_dict = {u: v for u, v in zip(np.arange(len(k)), node_size)} node_color_dict = { u: cmap(k_i / k.max() + color_bias) for u, k_i in zip(np.arange(len(k)), k) } G = nx.from_numpy_array(A_unit) if network_type == 'SBM_ER': space = 'linear' feature = feature_from_network_topology(A_unit, G, space, tradeoff_para=0.5, method='degree') group_index = group_index_from_feature_Kmeans(feature, len(N)) groups_dict = { node_i: group_i for group_i, nodes in enumerate(group_index) for node_i in nodes } g = Graph(A_unit, edge_width=edge_width, arrows=True, node_size=node_size_dict, edge_layout='straight', node_layout='community', node_layout_kwargs={'node_to_community': groups_dict}, node_color=node_color_dict, node_alpha=0.8, edge_color='tab:grey', edge_alp=0.8, ax=ax, edge_zorder=1) else: g = Graph(A_unit, edge_width=edge_width, arrows=True, node_size=node_size_dict, edge_layout='straight', node_layout=nx.spring_layout(G), node_color=node_color_dict, node_alpha=0.8, edge_color='tab:grey', edge_alp=0.8, ax=ax, edge_zorder=1)
import matplotlib.pyplot as plt from netgraph import Graph partitions = [ list(range(3)), list(range(3, 9)), list(range(9, 21)) ] edges = list(zip(np.repeat(partitions[0], 2), partitions[1])) \ + list(zip(np.repeat(partitions[0], 2), partitions[1][1:])) \ + list(zip(np.repeat(partitions[1], 2), partitions[2])) \ + list(zip(np.repeat(partitions[1], 2), partitions[2][1:])) Graph(edges, node_layout='multipartite', node_layout_kwargs=dict(layers=partitions, reduce_edge_crossings=True), node_labels=True) plt.show() ################################################################################ # To change the layout from the left-right orientation to a bottom-up orientation, # call the layout function directly and swap x and y coordinates of the node positions. import numpy as np import matplotlib.pyplot as plt from netgraph import Graph, get_multipartite_layout partitions = [ list(range(3)), list(range(3, 9)),
=================== Default visualisation for a weighted graph. """ import matplotlib.pyplot as plt from netgraph import Graph weighted_cube = [(0, 1, -0.1), (1, 2, -0.2), (2, 3, -0.4), (3, 2, 0.0), (3, 0, -0.2), (4, 5, 0.7), (5, 6, 0.9), (6, 5, -0.2), (6, 7, 0.5), (7, 4, 0.1), (0, 4, 0.5), (1, 5, -0.3), (5, 1, -0.4), (2, 6, 0.8), (3, 7, 0.4)] cmap = 'RdGy' Graph(weighted_cube, edge_cmap=cmap, edge_width=2., arrows=True) plt.show() ################################################################################ # By default, different edge weights are represented by different colors. # The default colormap is :code:`'RdGy'` but any diverging matplotlib colormap can be used: # # - :code:`'PiYG'` # - :code:`'PRGn'` # - :code:`'BrBG'` # - :code:`'PuOr'` # - :code:`'RdGy'` (default) # - :code:`'RdBu'` # - :code:`'RdYlBu'` # - :code:`'RdYlGn'` # - :code:`'Spectral'`
#!/usr/bin/env python """ Custom Node Positions and Edge Paths ==================================== Node positions can be set explicitly by using a dictionary that maps node IDs to (x, y) coordinates as the :code:`node_layout` keyword argument. Analogously, edge paths can be set explicitly by using a dictionary that maps edge IDs to ndarray of (x, y) coordinates as the :code:`edge_layout` keyword argument. """ import numpy as np import matplotlib.pyplot as plt from netgraph import Graph edge_list = [(0, 1)] node_positions = {0: (0.2, 0.4), 1: (0.8, 0.6)} edge_paths = { (0, 1): np.array([(0.2, 0.4), (0.2, 0.8), (0.5, 0.8), (0.5, 0.2), (0.8, 0.2), (0.8, 0.6)]) } Graph(edge_list, node_layout=node_positions, edge_layout=edge_paths) plt.show()
Directed Graphs =============== Default visualisation for a directed graph. """ import matplotlib.pyplot as plt from netgraph import Graph cube = [ (0, 1), (1, 2), (2, 3), # <- bidirectional edges (3, 2), # <- (3, 0), (4, 5), (5, 6), # <- (6, 5), # <- (6, 7), (0, 4), (7, 4), (5, 1), # <- (1, 5), # <- (2, 6), (3, 7) ] Graph(cube, edge_width=2., arrows=True) plt.show()
#!/usr/bin/env python """ Circular node layout ==================== The circular node layout routine in netgraph uses the Baur-Brandes algorithm to reduce edge crossings. """ import matplotlib.pyplot as plt from netgraph import Graph unbalanced_tree = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (2, 6), (3, 7), (3, 8), (4, 9), (4, 10), (4, 11), (5, 12), (5, 13), (5, 14), (5, 15)] Graph(unbalanced_tree, node_layout='circular') plt.show()
.. __ : https://github.com/Penlect/rectangle-packer """ import matplotlib.pyplot as plt from itertools import combinations from netgraph import Graph edge_list = [] # add 15 2-node components edge_list.extend([(ii, ii + 1) for ii in range(30, 60, 2)]) # add 10 3-node components for ii in range(60, 90, 3): edge_list.extend([(ii, ii + 1), (ii, ii + 2), (ii + 1, ii + 2)]) # add a couple of larger components n = 90 for ii in [10, 20, 30]: edge_list.extend(list(combinations(range(n, n + ii), 2))) n += ii nodes = list(range(n)) Graph(edge_list, nodes=nodes, node_size=1, edge_width=0.3, node_layout='circular') plt.show()
community_to_color = { 0: 'tab:blue', 1: 'tab:orange', 2: 'tab:green', 3: 'tab:red', } node_color = { node: community_to_color[community_id] for node, community_id in node_to_community.items() } Graph( g, node_color=node_color, node_edge_width=0, edge_alpha=0.1, node_layout='community', node_layout_kwargs=dict(node_to_community=node_to_community), edge_layout='bundled', edge_layout_kwargs=dict(k=2000), ) plt.show() ################################################################################ # Alternatively, the best partition into communities can be inferred, for example # using the Louvain algorithm (:code:`pip install python-louvain`): from community import community_louvain node_to_community = community_louvain.best_partition(g)