Source code for phasik.drawing.drawing_networks

"""
Functions to visualize 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

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 Parameters ---------- color : color Color to use for nodes and edges Returns ------- dict """ 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 Parameters ---------- color : color Color to use for nodes Returns ------- dict """ 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 Parameters ---------- color : color Color to use for edges Returns ------- dict """ return { "edge_color": color, }
[docs]def standard_label_params(color): """ Returns a dictionary containing standard values of label plotting parameters Parameters ---------- color : color Color to use for labels Returns ------- dict """ 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, pos=None, color="mediumseagreen", edge_widths=None, edge_colors=None, edge_cmap=None, edge_vmin=None, edge_vmax=None, label_nodes=True, colorbar=True, ): """Basic graph drawing function Parameters ---------- graph : networkx.Graph Graph to visualise ax : matplotlib.Axes, optional Axes on which to draw the graph pos : dict Dictionary of node positions of the form {node_id : (x, y)} color : str, optional Color to use for the graph nodes and edges (default 'mediumseagreen') edge_widths: float or array of floats Line width of edges edge_colors: color or array of colors Edge color. Can be a single color or a sequence of colors with the same length as edgelist. Color can be string or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. edge_cmap: Matplotlib colormap, optional Colormap for mapping intensities of edges edge_vmin,edge_vmax: floats, optional Minimum and maximum for edge colormap scaling label_nodes : bool, optional Whether to label the nodes or just leave them as small circles (default True) colorbar : bool, optional Wether to draw a colorbar Returns ------- matplotlib.axes.Axes The axis object to draw on Examples -------- >>> import networkx as nx >>> import phasik as pk >>> G = nx.fast_gnp_random_graph(10, 0.5) >>> pk.draw_graph(G) """ if ax is None: ax = plt.gca() if pos is None: pos = nx.spring_layout(graph) params_nodes = standard_node_params(color) params_edges = standard_edge_params(color) params_labels = standard_label_params(color) if edge_widths is not None: params_edges["width"] = edge_widths if edge_colors is not None: params_edges["edge_color"] = edge_colors nx.draw_networkx_nodes(graph, ax=ax, pos=pos, **params_nodes) im = nx.draw_networkx_edges( graph, ax=ax, pos=pos, edge_cmap=edge_cmap, edge_vmin=edge_vmin, edge_vmax=edge_vmin, **params_edges, ) if label_nodes: nx.draw_networkx_labels(graph, ax=ax, pos=pos, **params_labels) if colorbar: if edge_colors is not None: if len(edge_colors) == len(graph.edges): plt.colorbar(im, ax=ax, label="edge color scale") return ax
[docs]def highlight_subgraphs(graphs, colors, ax=None, pos=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 pos : dict Dictionary of node positions label_nodes : bool, optional Whether or not to label the graph nodes or leave them as circles Returns ------- None Examples -------- >>> import networkx as nx >>> import phasik as pk >>> graphs = [nx.fast_gnp_random_graph(10, 0.5) for i in range(3)] >>> pk.highlight_subgraphs(graphs, ["red", "blue", "green"]) """ if ax is None: ax = plt.gca() if pos is None: pos = nx.spring_layout(graphs[0]) for graph, color in zip(graphs, colors): draw_graph( graph=graph, pos=pos, ax=ax, label_nodes=label_nodes, color=color, edge_colors=color, ) return
[docs]def animate_temporal_network( temporal_network, color_temporal="red", color_constant="silver", width_scale=1.5, with_labels=True, pos=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 with_labels : bool, optional Wether to draw node labels pos : 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 Examples -------- >>> import phasik as pk >>> pk.animate_temporal_network(temporal_network) """ if frames is None: frames = temporal_network.T() aggregated_network = temporal_network.aggregated_network(output="normalised") if pos is None: pos = nx.spring_layout(aggregated_network) if ax is None: fig, ax = plt.subplots(figsize=(15, 12)) else: fig = plt.gcf() if isinstance(temporal_network, pk.PartiallyTemporalNetwork): edges_temporal = temporal_network.temporal_edges edges_all = list(aggregated_network.edges()) edges_constant = list(set(edges_all).difference(edges_temporal)) elif isinstance(temporal_network, pk.TemporalNetwork): edges_temporal = list(aggregated_network.edges()) 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