Source code for tick.plot.plot_hawkes

# License: BSD 3 clause

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable

from tick.plot.plot_utilities import share_x, share_y


[docs]def plot_hawkes_kernel_norms(kernel_object, show=True, pcolor_kwargs=None, node_names=None, rotate_x_labels=0.): """Generic function to plot Hawkes kernel norms. Parameters ---------- kernel_object : `Object` An object that must have the following API : * `kernel_object.n_nodes` : a field that stores the number of nodes of the associated Hawkes process (thus the number of kernels is this number squared) * `kernel_object.get_kernel_norms()` : must return a 2d numpy array with the norm of each kernel show : `bool`, default=`True` if `True`, show the plot. Otherwise an explicit call to the show function is necessary. Useful when superposing several plots. pcolor_kwargs : `dict`, default=`None` Extra pcolor kwargs such as cmap, vmin, vmax node_names : `list` of `str`, shape=(n_nodes, ), default=`None` node names that will be displayed on axis. If `None`, node index will be used. rotate_x_labels : `float`, default=`0.` Number of degrees to rotate the x-labels clockwise, to prevent overlapping. Notes ----- Kernels are displayed such that it shows norm of column influence's on row. """ n_nodes = kernel_object.n_nodes if node_names is None: node_names = range(n_nodes) elif len(node_names) != n_nodes: ValueError('node_names must be a list of length {} but has length {}' .format(n_nodes, len(node_names))) row_labels = ['${} \\rightarrow$'.format(i) for i in node_names] column_labels = ['$\\rightarrow {}$'.format(i) for i in node_names] norms = kernel_object.get_kernel_norms() fig, ax = plt.subplots() if rotate_x_labels != 0.: # we want clockwise rotation because x-axis is on top rotate_x_labels = -rotate_x_labels x_label_alignment = 'right' else: x_label_alignment = 'center' if pcolor_kwargs is None: pcolor_kwargs = {} if norms.min() >= 0: pcolor_kwargs.setdefault("cmap", plt.cm.Blues) else: # In this case we want a diverging colormap centered on 0 pcolor_kwargs.setdefault("cmap", plt.cm.RdBu) max_abs_norm = np.max(np.abs(norms)) pcolor_kwargs.setdefault("vmin", -max_abs_norm) pcolor_kwargs.setdefault("vmax", max_abs_norm) heatmap = ax.pcolor(norms, **pcolor_kwargs) # put the major ticks at the middle of each cell ax.set_xticks(np.arange(norms.shape[0]) + 0.5, minor=False) ax.set_yticks(np.arange(norms.shape[1]) + 0.5, minor=False) # want a more natural, table-like display ax.invert_yaxis() ax.xaxis.tick_top() ax.set_xticklabels(row_labels, minor=False, fontsize=17, rotation=rotate_x_labels, ha=x_label_alignment) ax.set_yticklabels(column_labels, minor=False, fontsize=17) fig.subplots_adjust(right=0.8) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.5) fig.colorbar(heatmap, cax=cax) if show: plt.show() return fig
[docs]def plot_hawkes_kernels(kernel_object, support=None, hawkes=None, n_points=300, show=True, log_scale=False, min_support=1e-4, ax=None): """Generic function to plot Hawkes kernels. Parameters ---------- kernel_object : `Object` An object that must have the following API : * `kernel_object.n_nodes` : a field that stores the number of nodes of the associated Hawkes process (thus the number of kernels is this number squared) * `kernel_object.get_kernel_supports()` : must return a 2d numpy array with the size of the support of each kernel * `kernel_object.get_kernel_values(self, i, j, abscissa_array)` : must return as a numpy 1d array the sampled `(i,j)` kernel values corresponding to the abscissa `abscissa_array` support : `float`, default=None the size of the support that will be used to plot all the kernels. If None or non positive then the maximum kernel supports is used hawkes : `SimuHawkes`, default=None If a `SimuHawkes` object is given then the kernels plots are superposed with those of this object (considered as the `True` kernels). This is used to plot on the same plots the estimated kernels along with the true kernels. n_points : `int`, default=300 Number of points that will be used in abscissa. More points will lead to a more precise graph. show : `bool`, default=`True` if `True`, show the plot. Otherwise an explicit call to the show function is necessary. Useful when superposing several plots. log_scale : `bool`, default=`False` If `True`, then x-axis and y-axis are on a log-scale. This is useful to plot power-law kernels. min_support : `float`, default=1e-4 Start value of the plot. Only used if log_scale is `True`. ax : `np.ndarray` of `matplotlib.axes`, default=None If not None, the figure will be plot on these axes and show will be set to False. """ if support is None or support <= 0: plot_supports = kernel_object.get_kernel_supports() support = plot_supports.max() * 1.2 n_nodes = kernel_object.n_nodes if log_scale: x_values = np.logspace( np.log10(min_support), np.log10(support), n_points) else: x_values = np.linspace(0, support, n_points) if ax is None: fig, ax_list_list = plt.subplots(n_nodes, n_nodes, sharex=True, sharey=True) else: if ax.shape != (n_nodes, n_nodes): raise ValueError('Given ax has shape {} but should have shape {}' .format(ax.shape, (n_nodes, n_nodes))) ax_list_list = ax show = False if n_nodes == 1: ax_list_list = np.array([[ax_list_list]]) for i, ax_list in enumerate(ax_list_list): for j, ax in enumerate(ax_list): y_values = kernel_object.get_kernel_values(i, j, x_values) ax.plot(x_values, y_values, label="Kernel (%d, %d)" % (i, j)) if hawkes: y_true_values = hawkes.kernels[i, j].get_values(x_values) ax.plot(x_values, y_true_values, label="True Kernel (%d, %d)" % (i, j)) # set x_label for last line if i == n_nodes - 1: ax.set_xlabel(r"$t$", fontsize=18) ax.set_ylabel(r"$\phi^{%g,%g}(t)$" % (i, j), fontsize=18) if log_scale: ax.set_xscale('log') ax.set_yscale('log') legend = ax.legend() for label in legend.get_texts(): label.set_fontsize(12) if show: plt.show() return ax_list_list.ravel()[0].figure
def plot_hawkes_baseline_and_kernels( hawkes_object, kernel_support=None, hawkes=None, n_points=300, show=True, log_scale=False, min_support=1e-4, ax=None): """Generic function to plot Hawkes baseline and kernels. Parameters ---------- hawkes_object : `Object` An object that must have the following API : * `kernel_object.n_nodes` : a field that stores the number of nodes of the associated Hawkes process (thus the number of kernels is this number squared) * `kernel_object.get_baseline_values(self, i, abscissa_array)` : must return as a numpy 1d array the sampled `i` baseline values corresponding to the abscissa `abscissa_array` * `kernel_object.period_length` : a field that stores the size of the baseline period * `kernel_object.get_kernel_supports()` : must return a 2d numpy array with the size of the support of each kernel * `kernel_object.get_kernel_values(self, i, j, abscissa_array)` : must return as a numpy 1d array the sampled `(i,j)` kernel values corresponding to the abscissa `abscissa_array` kernel_support : `float`, default=None the size of the support that will be used to plot all the kernels. If None or non positive then the maximum kernel supports is used hawkes : `SimuHawkes`, default=None If a `SimuHawkes` object is given then the baseline and kernels plots are superposed with those of this object (considered as the `True` baseline and kernels). This is used to plot on the same plots the estimated value along with the true values. n_points : `int`, default=300 Number of points that will be used in abscissa. More points will lead to a more precise graph. show : `bool`, default=`True` if `True`, show the plot. Otherwise an explicit call to the show function is necessary. Useful when superposing several plots. log_scale : `bool`, default=`False` If `True`, then x-axis and y-axis of kernels are on a log-scale. This is useful to plot power-law kernels. min_support : `float`, default=1e-4 Start value of the kernels plot. Only used if log_scale is `True`. ax : `np.ndarray` of `matplotlib.axes`, default=None If not None, the figure will be plot on these axes and show will be set to False. """ n_nodes = hawkes_object.n_nodes if ax is None: fig, ax_list_list = plt.subplots(n_nodes, n_nodes + 1, figsize=(10, 6)) else: ax_list_list = ax show = False # invoke plot_hawkes_kernels ax_kernels = ax_list_list[:, 1:] plot_hawkes_kernels(hawkes_object, support=kernel_support, hawkes=hawkes, n_points=n_points, show=False, log_scale=log_scale, min_support=min_support, ax=ax_kernels) share_x(ax_kernels) share_y(ax_kernels) # plot hawkes baselines ax_baselines = ax_list_list[:, 0] t_values = np.linspace(0, hawkes_object.period_length, n_points) for i in range(n_nodes): ax = ax_baselines[i] ax.plot(t_values, hawkes_object.get_baseline_values(i, t_values), label='baseline ({})'.format(i)) ax.plot(t_values, hawkes.get_baseline_values(i, t_values), label='true baseline ({})'.format(i)) ax.set_ylabel("$\mu_{}(t)$".format(i), fontsize=18) # set x_label for last line if i == n_nodes - 1: ax.set_xlabel(r"$t$", fontsize=18) legend = ax.legend() for label in legend.get_texts(): label.set_fontsize(12) share_x(ax_baselines.reshape(2, 1)) share_y(ax_baselines.reshape(2, 1)) if show: plt.show() return ax_list_list.ravel()[0].figure def _normalize_functions(y_values_list, t_values): """Normalize list of functions by their integral value Parameters ---------- y_values_list : `list` of np.ndarray y values of the list of function we want to normalize t_values : `np.ndarray` t values shared by all functions given with y_values_list Returns ------- normalized_y_values_list : `list` of np.ndarray Normalized y values of the given list of function normalizations : `np.ndarray` Normalization factors that have been used """ y_values_list = np.array(y_values_list) normalizations = [ 1. / np.trapz(y_values, t_values) for y_values in y_values_list ] normalized_y_values_list = (y_values_list.T * normalizations).T return normalized_y_values_list, normalizations def _find_best_match(diff_matrix): """Find best best possible match by induction Parameters ---------- diff_matrix : `np.ndarray`, shape=(n_basis, n_basis) Matrix containing differences for all pairs of values Returns ------- matches : `list`, shape=(n_nodes,) List of all found matches """ diff_matrix = diff_matrix.astype(float) matches = [] n_basis = diff_matrix.shape[0] for _ in range(n_basis): row, col = np.unravel_index(np.argmin(diff_matrix), (n_basis, n_basis)) diff_matrix[row, :] = np.inf diff_matrix[:, col] = np.inf matches += [(row, col)] return matches
[docs]def plot_basis_kernels(learner, support=None, basis_kernels=None, n_points=300, show=True): """Function used to plot basis of kernels It is used jointly with `tick.hawkes.inference.HawkesBasisKernels` learner class. Parameters ---------- learner : `HawkesBasisKernels` The given learner which basis kernels are plotted support : `float`, default=None the size of the support that will be used to plot all the kernels. If None or non positive then the maximum kernel supports is used basis_kernels : `list` of `func`, default=None True basis kernels. If not `None`, it will find the closest estimated basis kernel and will plot it together. This basis kernels will be normalized to fit better with their estimations. n_points : `int`, default=300 Number of points that will be used in abscissa. More points will lead to a more precise graph. show : `bool`, default=`True` if `True`, show the plot. Otherwise an explicit call to the show function is necessary. Useful when superposing several plots. """ if support is None or support <= 0: support = learner.kernel_support fig, ax_list = plt.subplots(1, learner.n_basis, figsize=(8, 4), sharey=True) if basis_kernels is not None: if len(basis_kernels) != learner.n_basis: raise ValueError('Learner has {} basis kernels, cannot ' 'compare to {} basis kernels'.format( learner.n_basis, len(basis_kernels))) t_values = learner.kernel_discretization[:-1] basis_values_list = [ basis_kernel(t_values) for basis_kernel in basis_kernels ] normalized_basis_kernels, basis_normalizations = \ _normalize_functions(basis_values_list, t_values) normalized_estimates, estimated_normalizations = \ _normalize_functions(learner.basis_kernels, t_values) kernel_diff = np.array([[ np.trapz(np.abs(nbf - ne), t_values) for nbf in normalized_basis_kernels ] for ne in normalized_estimates]) matches = _find_best_match(kernel_diff) t_values = np.linspace(0, support, n_points) for estimated_index, basis_index in matches: basis_kernel = basis_kernels[basis_index] estimated = learner.basis_kernels[estimated_index] piecewise_y = np.repeat(estimated, 2) piecewise_t = np.hstack( (learner.kernel_discretization[0], np.repeat(learner.kernel_discretization[1:-1], 2), learner.kernel_discretization[-1])) ax_list[basis_index].step(piecewise_t, piecewise_y, label="estimated %i" % estimated_index) rescaled_basis = basis_kernel(t_values) * \ basis_normalizations[basis_index] / \ estimated_normalizations[estimated_index] ax_list[basis_index].plot(t_values, rescaled_basis, label="true basis %i" % basis_index) legend = ax_list[basis_index].legend() for label in legend.get_texts(): label.set_fontsize(12) else: for estimated_index in range(learner.n_basis): estimated = learner.basis_kernels[estimated_index] piecewise_y = np.repeat(estimated, 2) piecewise_t = np.hstack( (learner.kernel_discretization[0], np.repeat(learner.kernel_discretization[1:-1], 2), learner.kernel_discretization[-1])) ax_list[estimated_index].plot( piecewise_t, piecewise_y, label="estimated %i" % estimated_index) legend = ax_list[estimated_index].legend() for label in legend.get_texts(): label.set_fontsize(12) if show: plt.show() return fig