Source code for phasik.drawing.drawing_networks

"""
Functions to visualise networks and temporal networks
"""

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import seaborn as sb
from matplotlib import animation
from matplotlib.lines import Line2D
from networkx.drawing.nx_agraph import graphviz_layout

import phasik as pk

__all__ = [
    'standard_node_params',
    'standard_edge_params',
    'standard_label_params',
    'standard_params',    
    'draw_graph',
    'highlight_subgraphs',
    'animate_temporal_network'
]    

[docs]def standard_params(color): """Returns a dictionary containing standard values of plotting parameters""" return { 'node_color': color, 'edge_color': color, 'font_color': 'k', 'font_size' : 'medium', 'edgecolors': 'k', 'node_size': 100, 'bbox': dict(facecolor=color, edgecolor='black', boxstyle='round, pad=0.2', alpha=1) }
[docs]def standard_node_params(color): """Returns a dictionary containing standard values of node plotting parameters""" return { 'node_color': color, 'edgecolors': 'k', 'node_size': 100 }
[docs]def standard_edge_params(color): """Returns a dictionary containing standard values of edge plotting parameters""" return { 'edge_color': color, }
[docs]def standard_label_params(color): """Returns a dictionary containing standard values of label plotting parameters""" return { 'font_color': 'k', 'font_size' : 'medium', 'bbox': dict(facecolor=color, edgecolor='black', boxstyle='round, pad=0.2', alpha=1) }
[docs]def draw_graph(graph, ax=None, label_nodes=True, color='mediumseagreen', layout=None): """Basic graph drawing function Parameters ---------- graph : networkx.Graph Graph to visualise ax : matplotlib.Axes, optional Axes on which to draw the graph label_nodes : bool, optional Whether to label the nodes or just leave them as small circles (default True) color : str, optional Color to use for the graph nodes and edges (default 'mediumseagreen') Returns ------- None """ if ax is None: ax = plt.gca() if layout is None : layout = graphviz_layout(graph, prog='neato') _draw_graph(graph, layout, ax, label_nodes, color)
def _draw_graph(graph, layout, ax, label_nodes, color): """Plots a networkx.Graph with a predefined style Parameters ---------- graph : network.Graph Graph to visualise layout : dict Dictionary of positions used by plotting function in networkx ax : maplotlib.axis Axes on which to plot label_nodes : bool If True, plot node labels color : str Color used for nodes and edges Returns ------- """ params_nodes = standard_node_params(color) params_edges = standard_edge_params(color) params_labels = standard_label_params(color) nx.draw_networkx_nodes(graph, ax=ax, pos=layout, **params_nodes) nx.draw_networkx_edges(graph, ax=ax, pos=layout, **params_edges) if label_nodes: nx.draw_networkx_labels(graph, ax=ax, pos=layout, **params_labels)
[docs]def highlight_subgraphs(graphs, colors, ax=None, layout=None, label_nodes=True): """Draw multiple nested subgraphs on the same axes Parameters ---------- graphs : list of networkx.Graph colors : list of str List of colors, one for each of the graphs in 'graphs' ax : matplotlib.Axes, optional Axes to plot on label_nodes : bool, optional Whether or not to label the graph nodes or leave them as circles Returns ------- None """ if ax is None: ax = plt.gca() if layout is None : layout = graphviz_layout(graphs[0], prog='neato') for graph, color in zip(graphs, colors): _draw_graph(graph, layout, ax, label_nodes, color)
[docs]def animate_temporal_network(temporal_network, color_temporal='red', color_constant='silver', width_scale=1.5, with_labels=True, layout=None, ax=None, interval=20, frames=None) : """Return animation of the temporal network evolving over time Parameters ---------- temporal_network : phasik.TemporalNetwork Temporal network to visualise color_temporal : str Color of the time-varying edges, defaults to 'red' color_constant : str Color of the constant edges (defaults to 'silver'), i.e. for which we have no temporal information width_scale : float Scale factor for width of the temporal edges compared to the constant ones layout : dict Dictionary of node positions ax : matplotlib.axis Axes to plot the animation on interval : int Interval of time between frames, in ms. frames : int Number of frames of the animation (should be at most the number of timepoints (default)) Returns ------- matplotlib.animation """ if frames is None: frames = temporal_network.T() aggregated_network = temporal_network.aggregated_network(output='normalised') if layout is None : pos = graphviz_layout(aggregated_network, prog='neato') else : pos = layout if ax is None : fig, ax = plt.subplots(figsize=(15, 12)) else : fig = plt.gcf() if isinstance(temporal_network, pk.PartiallyTemporalNetwork) : nodes_temporal = temporal_network.temporal_nodes edges_temporal = temporal_network.temporal_edges nodes_all = list(aggregated_network.nodes()) edges_all = list(aggregated_network.edges()) edges_constant = list(set(edges_all).difference(edges_temporal)) elif isinstance(temporal_network, pk.TemporalNetwork) : nodes_temporal = list(aggregated_network.nodes()) edges_temporal = list(aggregated_network.edges()) nodes_all = nodes_temporal edges_all = edges_temporal edges_constant = list(set(edges_all).difference(edges_temporal)) else : raise TypeError("'temporal_network' must be of type TemporalNetowkr or PartiallyTemporalNetwork") params_nodes = pk.standard_node_params(color_constant) del params_nodes['node_color'] # will be set below param_labels = pk.standard_label_params(color_constant) def update_network(i) : ax.clear() snapshot_network = temporal_network.network_at_time(time_index=i, output='weighted') # constant network nx.draw_networkx_nodes(aggregated_network, node_color=color_constant, pos=pos, ax=ax, **params_nodes) nx.draw_networkx_edges(snapshot_network, edgelist=edges_constant, edge_color=color_constant, pos=pos, ax=ax) if with_labels : nx.draw_networkx_labels(aggregated_network, pos=pos, ax=ax, **param_labels) # changing edges weights = np.array([w for u,v,w in snapshot_network.edges.data("weight") if (u,v) in edges_temporal]) width_min = 0.7 # set minimum width for visualisation widths = np.maximum(weights * width_scale, width_min) nx.draw_networkx_edges(snapshot_network, edgelist=edges_temporal, edge_color=color_temporal, width=widths, pos=pos, ax=ax) ax.set_title(f"Time: {i} min") sb.despine(left=True, bottom=True) custom_lines = [Line2D([0], [0], color=color_constant, lw=width_scale), Line2D([0], [0], color=color_temporal, lw=width_scale)] ax.legend(custom_lines, ['Constant edges', 'Temporal edges']) ani = animation.FuncAnimation(fig, update_network, frames=frames, interval=interval, blit=False) return ani