Source code for teneto.networkmeasures.bursty_coeff

"""Bursty Coeff"""

import numpy as np
from .intercontacttimes import intercontacttimes
from ..utils import binarize
import itertools


[docs] def bursty_coeff(data, calc='edge', nodes='all', communities=None, threshold_type=None, threshold_level=None, threshold_params=None): u""" Calculates the bursty coefficient.[1][2] Parameters ---------- data : array, dict This is either (1) temporal network input with nettype: 'bu', 'bd'. (2) dictionary of ICTs (output of *intercontacttimes*). (3) temporal network input with nettype: 'wu', 'wd'. If weighted, you must also specify threshold_type and threshold_value which will make it binary. calc : str Caclulate the bursty coeff over what. Options include 'edge': calculate B on all ICTs between node i and j. (Default); 'node': caclulate B on all ICTs connected to node i.; 'communities': calculate B for each communities (argument communities then required); 'meanEdgePerNode': first calculate ICTs between i and j, then take the mean over all j. nodes: list or str Options: 'all': do for all nodes (default) or list of node indexes to calculate. communities : array, optional None (default) or Nx1 vector of communities assignment. This returns a "centrality" per communities instead of per node. threshold_type : str, optional If input is weighted. Specify binarizing threshold type. See teneto.utils.binarize threshold_level : str, optional If input is weighted. Specify binarizing threshold level. See teneto.utils.binarize threhsold_params : dict If input is weighted. Dictionawy with kwargs for teneto.utils.binarize Returns ------- B : array Bursty coefficienct per (edge or node measure). Notes ------ The burstiness coefficent, B, is defined in refs [1,2] as: .. math:: B = {{\sigma_{ICT} - \mu_{ICT}} \over {\sigma_{ICT} + \mu_{ICT}}} Where :math:`\sigma_{ICT}` and :math:`\mu_{ICT}` are the standard deviation and mean of the inter-contact times respectively (see teneto.networkmeasures.intercontacttimes) When B > 0, indicates bursty intercontact times. When B < 0, indicates periodic/tonic intercontact times. When B = 0, indicates random. Examples --------- First import all necessary packages >>> import teneto >>> import numpy as np Now create 2 temporal network of 2 nodes and 60 time points. The first has periodict edges, repeating every other time-point: >>> G_periodic = np.zeros([2, 2, 60]) >>> ts_periodic = np.arange(0, 60, 2) >>> G_periodic[:,:,ts_periodic] = 1 The second has a more bursty pattern of edges: >>> ts_bursty = [1, 8, 9, 32, 33, 34, 39, 40, 50, 51, 52, 55] >>> G_bursty = np.zeros([2, 2, 60]) >>> G_bursty[:,:,ts_bursty] = 1 The two networks look like this: .. plot:: import numpy as np import teneto import matplotlib.pyplot as plt ts_bursty = [1, 8, 9, 32, 33, 34, 39, 40, 50, 51, 52, 55] G_bursty = np.zeros([2, 2, 60]) G_bursty[:,:,ts_bursty] = 1 G_periodic = np.zeros([2, 2, 60]) ts_periodic = np.arange(0, 60, 2) G_periodic[:,:,ts_periodic] = 1 fig,ax = plt.subplots(2, 1, figsize=(10,3)) teneto.plot.slice_plot(G_bursty, ax[0], cmap='Pastel2', nodesize=20, nLabs=['0', '1']) teneto.plot.slice_plot(G_periodic, ax[1], cmap='Pastel2', nodesize=20, nLabs=['0', '1']) ax[0].set_title('G_bursty') ax[1].set_title('G_periodic') ax[0].set_ylim([-0.25,1.25]) ax[1].set_ylim([-0.25,1.25]) ax[0].set_xticklabels([]) ax[1].set_xticklabels([]) plt.tight_layout() fig.show() Now we call bursty_coeff. >>> B_periodic = teneto.networkmeasures.bursty_coeff(G_periodic) >>> B_periodic array([[nan, -1.], [-1., nan]]) Above we can see that between node 0 and 1, B=-1 (the diagonal is nan). Doing the same for the second example: >>> B_bursty = teneto.networkmeasures.bursty_coeff(G_bursty) >>> B_bursty array([[ nan, 0.13311003], [0.13311003, nan]]) gives a positive value, indicating the inter-contact times between node 0 and 1 is bursty. References ---------- .. [1] Goh, KI & Barabasi, AL (2008) Burstiness and Memory in Complex Systems. EPL (Europhysics Letters), 81: 4 [`Link <https://arxiv.org/pdf/physics/0610233.pdf>`_] .. [2] Holme, P & Saramäki J (2012) Temporal networks. Physics Reports. 519: 3. [`Link <https://arxiv.org/pdf/1108.1780.pdf>`_] (Discrete formulation used here) """ if threshold_type is not None: if threshold_params is None: threshold_params = {} data = binarize(data, threshold_type, threshold_level, **threshold_params) if calc == 'communities' and communities is None: raise ValueError( "Specified calc='communities' but no communities\ argument provided (list of clusters/modules)") ict = 0 # are ict present if isinstance(data, dict): # This could be done better if [k for k in list(data.keys()) if k == 'intercontacttimes'] == ['intercontacttimes']: ict = 1 # if shortest paths are not calculated, calculate them if ict == 0: data = intercontacttimes(data) ict_shape = data['intercontacttimes'].shape if len(ict_shape) == 2: node_len = ict_shape[0] * ict_shape[1] elif len(ict_shape) == 1: node_len = 1 else: raise ValueError('more than two dimensions of intercontacttimes') if isinstance(nodes, list) and len(ict_shape) > 1: node_combinations = [[list(set(nodes))[t], list(set(nodes))[tt]] for t in range( 0, len(nodes)) for tt in range(0, len(nodes)) if t != tt] do_nodes = [np.ravel_multi_index(n, ict_shape) for n in node_combinations] else: do_nodes = np.arange(0, node_len) # Reshae ICTs if calc == 'node': ict = np.concatenate(data['intercontacttimes'] [do_nodes, do_nodes], axis=1) elif calc == 'communities': unique_communities = np.unique(communities) ict_shape = (len(unique_communities), len(unique_communities)) ict = np.array([[None] * ict_shape[0]] * ict_shape[1]) for i, s1 in enumerate(unique_communities): for j, s2 in enumerate(unique_communities): if i == j: ind = list( zip(*itertools.combinations(np.where(communities == s1)[0], 2))) ict[i, j] = np.concatenate( data['intercontacttimes'][ind[0], ind[1]]) else: ict[i, j] = np.concatenate(np.concatenate( data['intercontacttimes'][communities == s1, :][:, communities == s2])) # Quick fix, but could be better data['intercontacttimes'] = ict do_nodes = np.arange(0, ict_shape[0]*ict_shape[1]) if len(ict_shape) > 1: ict = data['intercontacttimes'].reshape(ict_shape[0] * ict_shape[1]) b_coeff = np.zeros(len(ict)) * np.nan else: b_coeff = np.zeros(1) * np.nan ict = [data['intercontacttimes']] for i in do_nodes: if ict[i] is not None: mu_ict = np.mean(ict[i]) sigma_ict = np.std(ict[i]) b_coeff[i] = (sigma_ict - mu_ict) / (sigma_ict + mu_ict) else: b_coeff[i] = np.nan if len(ict_shape) > 1: b_coeff = b_coeff.reshape(ict_shape) return b_coeff