Source code for superblockify.metrics.plot

"""Plotting functions for the network measures."""

from itertools import product

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.colors import LogNorm

from .measures import rel_increase
from ..plot import plot_by_attribute


[docs] def plot_distance_distributions( dist_matrix, dist_title, coords, coord_title, labels, distance_unit="m" ): """Plot the distributions of the Euclidean distances and coordinates. Parameters ---------- dist_matrix : ndarray The distance matrix for the partitioning. dist_matrix[i, j] is the Euclidean distance between node i and node j. dist_title : str The title of the histogram of the Euclidean distances. coords : tuple The coordinates of the nodes. coords[0] is the x-coordinates, coords[1] is the y-coordinates. Can be either angular or Euclidean coordinates. coord_title : str The title of the scatter plot of the coordinates. labels : tuple The labels of the coordinates. labels[0] is the label of the x-coordinate, labels[1] is the label of the y-coordinate. distance_unit : str, optional The unit of the distance. Defaults to "m". """ _, axe = plt.subplots(1, 2, figsize=(10, 5)) # Plot distribution of distances axe[0].hist(dist_matrix.flatten(), bins=100) axe[0].set_title(dist_title) axe[0].set_xlabel( "Travel time (s)" if distance_unit == "s" else f"Distance ({distance_unit})" ) axe[0].set_ylabel("Count") # Plot scatter plot of lat/lon, aspect ratio should be 1:1 axe[1].set_aspect("equal") axe[1].scatter(coords[0], coords[1], alpha=0.5, s=1) axe[1].ticklabel_format(axis="both", style="sci", scilimits=(0, 0)) axe[1].set_title(coord_title) axe[1].set_xlabel(labels[0]) axe[1].set_ylabel(labels[1]) plt.tight_layout()
[docs] def plot_distance_matrices(metric, name=None): """Show the distance matrices for the network measures. Plots all available distance matrices in a single figure. Parameters ---------- metric : metrics.Metric The metric to plot the distance matrices for. name : str The name to put into the title of the plot. Returns ------- fig, axes : matplotlib.figure.Figure, matplotlib.axes.Axes The figure and axes of the plot. Raises ------ ValueError If no distance matrices are available. """ if metric.distance_matrix is None: raise ValueError("No distance matrices available.") # Make figure with the fitting amount of subplots fig, axes = plt.subplots( 1, len(metric.distance_matrix), figsize=(len(metric.distance_matrix) * 5, 5) ) # Find maximal, non-inf value for the colorbar max_val = max( np.max(value[value != np.inf]) for value in metric.distance_matrix.values() ) dist_im = None # Subplots with shared colorbar, title, and y-axis label for axe, (key, value) in zip(axes, metric.distance_matrix.items()): dist_im = axe.imshow(value, vmin=0, vmax=max_val) axe.set_title(f"$d_{key}(i, j)$", fontsize=14) axe.set_xlabel("Node $j$") axe.set_aspect("equal") # Share y-axis axes[0].set_ylabel("Node $i$") for axe in axes[1:]: axe.get_shared_y_axes().joined(axes[0], axe) # Plot colorbar on the right side of the figure fig.colorbar(dist_im, ax=axes, fraction=0.046, pad=0.04) # Label colorbar # dist_im.set_label(f"Distance [{unit}]") not working # Label at the right of the plot, next to colorbar fig.text( 0.925, 0.5, f"Distance [{metric.unit_symbol()}]", va="center", rotation="vertical", fontsize=14, ) # Title above all subplots fig.suptitle( f"Distance matrices for the network measures ({metric.unit_symbol()})\n" f"{'(' + name + ')' if name else ''}", fontsize=16, ) return fig, axes
[docs] def plot_distance_matrices_pairwise_relative_difference(metric, name=None): """Show the pairwise relative difference between the distance matrices. Plots the pairwise relative difference between the distance matrices in a single figure. Only plots the lower triangle of the distance matrices. On the diagonal the distance matrices are plotted as in `plot_distance_matrices`. Parameters ---------- metric : metrics.Metric The metric to plot the distance matrices for. name : str The name to put into the title of the plot. Returns ------- fig, axes : matplotlib.figure.Figure, matplotlib.axes.Axes The figure and axes of the plot. Raises ------ ValueError If no distance matrices are available. """ # pylint: disable=too-many-locals if metric.distance_matrix is None: raise ValueError("No distance matrices available.") # Make figure with the fitting amount of subplots # We need len(metric.distance_matrix)^2 subplots, but we only plot the lower # triangle, the rest will be empty. On the diagonal we plot the distance # matrices. fig, axes = plt.subplots( len(metric.distance_matrix), len(metric.distance_matrix), figsize=(len(metric.distance_matrix) * 5, len(metric.distance_matrix) * 5), ) # Find maximal, non-inf value for the colorbar for the diagonal max_val = max( np.max(value[value != np.inf]) for value in metric.distance_matrix.values() ) # Calculate the pairwise relative difference between the distance matrices # save the relative difference and the minimal value for the colorbar regarding # the absolute value rel_diff = {} max_val_rel = 0 # For the lower triangle for i, (key_i, value_i) in enumerate(metric.distance_matrix.items()): for _, (key_j, value_j) in enumerate(metric.distance_matrix.items()): if f"{key_i}{key_j}" not in metric.directness.keys(): continue # Calculate the pairwise relative difference # Use np.inf if either value is np.inf or if the denominator is 0 # pylint: disable=arguments-out-of-order rel_diff[key_j, key_i] = rel_increase(value_j, value_i) # Find the maximal value for the colorbar regarding the absolute value # (ignoring np.inf) max_val_rel = max( max_val_rel, np.max(rel_diff[key_j, key_i][rel_diff[key_j, key_i] != np.inf]), ) # Plot distance matrices on diagonal axes and relative difference on the # lower triangle axes # Iterate over all combinations of keys, for the upper triangle make the axes # invisible # Only write labels on the left and bottom axes dist_im = None diff_im = None for i, (key_i, key_j) in enumerate(product(metric.distance_matrix, repeat=2)): axe = axes[i // len(metric.distance_matrix), i % len(metric.distance_matrix)] # Make the upper triangle axes invisible if i // len(metric.distance_matrix) < i % len(metric.distance_matrix): axe.set_visible(False) # On the diagonal plot the distance matrices elif i // len(metric.distance_matrix) == i % len(metric.distance_matrix): # Use colormap viridis for the distance matrices dist_im = axe.imshow( metric.distance_matrix[key_i], vmin=0, vmax=max_val, cmap="viridis" ) axe.set_title(f"$d_{key_i}(i, j)$") axe.set_aspect("equal") # On the lower triangle plot the pairwise relative difference else: # The relative differences are all negative, the colormap will go from # min_val to 0, a fitting colormap is RdYlGn diff_im = axe.imshow( rel_diff[key_i, key_j], # vmin=1, # vmax=max_val_rel, # logaritmic scale norm=LogNorm(vmin=1, vmax=max_val_rel), cmap="RdYlGn_r", ) axe.set_title(f"$\\frac{{d_{{{key_i}}}(i, j)}}{{d_{{{key_j}}}(i, j)}}$") axe.set_xlabel("Node $j$") axe.set_ylabel("Node $i$") axe.set_aspect("equal") # Only write labels on the left and bottom axes if i // len(metric.distance_matrix) != len(metric.distance_matrix) - 1: axe.set_xticklabels([]) if i % len(metric.distance_matrix) != 0: axe.set_yticklabels([]) # Set the labels for all x and y axes for axe in axes[-1, :]: axe.set_xlabel("Node $j$") for axe in axes[:, 0]: axe.set_ylabel("Node $i$") # Plot the two colorbars on the right side of the figure # Colorbar for the diagonal fig.colorbar(dist_im, ax=axes, fraction=0.046, pad=0.04) # Colorbar for the lower triangle fig.colorbar(diff_im, ax=axes, fraction=0.046, pad=0.04) # Label colorbar dist_im.set_label(f"Distance [{metric.unit_symbol()}]") # Title above all subplots fig.suptitle( f"Pairwise relative difference between the distance matrices " f"({metric.unit_symbol()}) {'(' + name + ')' if name else ''}" ) return fig, axes
[docs] def plot_component_wise_travel_increase( partitioner, distance_matrix, node_list, measure1, measure2, unit, **pg_kwargs ): """Calculate and plot the component-wise travel increase. For every component of the partitioner, calculate the relative travel increase. This is the mean of the relative travel increase for all pairs of nodes in the component. Relative travel increase is the percentual increase of the travel between measure 1 and measure 2. Mean of d_1(i, j) / d_1(i, j) if i in the concerning component. Parameters ---------- partitioner : Partitioner The partitioner to calculate the component-wise travel increase for. distance_matrix : dict The distance matrix to calculate the component-wise travel increase for. node_list : list The list of nodes the distance matrices are sorted by. measure1 : str The name of the first measure. Key for the distance matrix. measure2 : str The name of the second measure. Key for the distance matrix. unit : str The unit of the distance matrices. pg_kwargs : dict Keyword arguments for the plot_graph function. Returns ------- fig, axes : matplotlib.figure.Figure, matplotlib.axes.Axes The figure and axes of the plot. """ # pylint: disable=too-many-locals partition_nodes = partitioner.get_partition_nodes() rel_incr = rel_increase(distance_matrix[measure1], distance_matrix[measure2]) for component in partition_nodes: # Calculate mean of the relative travel increase: Take all increases from inside # the component to all other nodes and reverse. All rows and columns with # Indices in the component nodes_idx = [ node_list.index(node) for node in component["subgraph"].nodes if node not in partitioner.sparsified.nodes ] # column from nodes to all other nodes col = rel_incr[:, nodes_idx] # row from all other nodes to nodes row = rel_incr[nodes_idx, :] # Take the mean of the union where the values are not np.inf rel_increase_component = np.mean( np.union1d( col[~np.isinf(col)], row[~np.isinf(row)], ) ) component["rel_increase"] = rel_increase_component # Write this to the edges of the subgraph for edge in component["subgraph"].edges: partitioner.graph.edges[edge]["rel_increase_comp"] = rel_increase_component _, axes = plt.subplots(1, 1, figsize=(10, 10)) # Add colorbar sc_mapbl = plt.cm.ScalarMappable( cmap="RdYlGn_r", norm=plt.Normalize( vmin=1, vmax=np.max([component["rel_increase"] for component in partition_nodes]), ), ) sc_mapbl._A = [] # pylint: disable=protected-access # add colorbar to the figure cbar = plt.colorbar( sc_mapbl, ax=axes, fraction=0.046, pad=0.04, orientation="vertical" ) # Label the colorbar, vertical alignment, latex math mode cbar.ax.set_ylabel( rf"Travel distance increase in superblock ({unit}) " rf"$d_{{{measure1}}} (i, j)$ / $d_{{{measure2}}} (i, j)$", rotation=270, labelpad=20, fontsize=17, ) # Plot by attribute return plot_by_attribute( partitioner.graph, edge_attr="rel_increase_comp", edge_attr_types="numerical", edge_cmap="RdYlGn_r", edge_minmax_val=( 1, np.max([component["rel_increase"] for component in partition_nodes]), ), ax=axes, **pg_kwargs, )
[docs] def plot_relative_difference(metric, key_i, key_j, title=None): """Plot the relative difference between two distance matrices. Parameters ---------- metric : Metric The metric to plot the relative difference for. key_i : str The key of the first distance matrix. This is the baseline. key_j : str The key of the second distance matrix. This is the comparison. title : str, optional The title of the plot. Returns ------- fig, axe : matplotlib.figure.Figure, matplotlib.axes.Axes The figure and axe of the plot. """ # Calculate the relative difference rel_diff = rel_increase( metric.distance_matrix[key_i], metric.distance_matrix[key_j] ) # Plot the relative difference fig, axe = plt.subplots(1, 1, figsize=(8, 8)) # The relative differences are all negative, the colormap will go from min_val to 0, # a fitting colormap is RdYlGn diff_im = plt.imshow( rel_diff, # vmin=1, # vmax=np.max(rel_diff), cmap="RdYlGn_r", # log-scale norm=LogNorm( vmin=1, # max_val is the maximum value of the relative difference, but not np.inf vmax=np.max(rel_diff[~np.isinf(rel_diff)]), ), ) axe.set_title( f"{title} ({metric.unit_symbol()}) | " f"$\\frac{{d_{{{key_i}}}(i, j)}}{{d_{{{key_j}}}(i, j)}}$" ) axe.set_xlabel("Node $j$") axe.set_ylabel("Node $i$") axe.set_aspect("equal") # Plot the two colorbar fig.colorbar(diff_im, ax=axe, fraction=0.046, pad=0.04) return fig, axe
[docs] def plot_relative_increase_on_graph(graph, unit_symbol, **pg_kwargs): """Plot the relative increase edge wise from attribute `rel_increase`. Use `:func:metric.measures.write_relative_increase_to_edges` to write the relative increase to the edges. Parameters ---------- graph : networkx.Graph The graph to plot the relative increase on, must have the attribute `rel_increase` on the edges. unit_symbol : str The unit symbol of the metric. pg_kwargs : dict Keyword arguments for the plot_graph function. Returns ------- fig, axes : matplotlib.figure.Figure, matplotlib.axes.Axes The figure and axes of the plot. """ _, axes = plt.subplots(1, 1, figsize=(10, 10)) # Add colorbar sc_mapbl = plt.cm.ScalarMappable( cmap="RdYlGn_r", norm=plt.Normalize( vmin=1, vmax=np.max([edge["rel_increase"] for edge in graph.edges.values()]) ), ) sc_mapbl._A = [] # pylint: disable=protected-access # add colorbar to the figure cbar = plt.colorbar( sc_mapbl, ax=axes, fraction=0.046, pad=0.04, orientation="vertical" ) # Label the colorbar, vertical alignment, latex math mode cbar.ax.set_ylabel( f"Travel distance increase ({unit_symbol}) " "(all to all demand) $d_{N}(i, j)$ / $d_{S}(i, j)$", rotation=270, labelpad=20, fontsize=15, ) return plot_by_attribute( graph, edge_attr="rel_increase", edge_attr_types="numerical", edge_cmap="RdYlGn_r", edge_minmax_val=( 1, np.max([edge["rel_increase"] for edge in graph.edges.values()]), ), ax=axes, **pg_kwargs, )