#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# This file is part of pyunicorn.
# Copyright (C) 2008--2019 Jonathan F. Donges and pyunicorn authors
# URL: <http://www.pik-potsdam.de/members/donges/software>
# License: BSD (3-clause)
"""
Provides classes for the analysis of dynamical systems and time series based
on recurrence plots, including measures of recurrence quantification
analysis (RQA) and recurrence network analysis.
"""
# array object and fast numerics
import numpy as np
from ..core import Network
from .recurrence_plot import RecurrencePlot
#
# Class definitions
#
[docs]class RecurrenceNetwork(RecurrencePlot, Network):
"""
Class RecurrenceNetwork for generating and quantitatively analyzing
recurrence networks.
More information on the theory and applications of recurrence networks can
be found in [Marwan2009]_, [Donner2010b]_.
Examples:
- Create an instance of RecurrenceNetwork with a fixed recurrence
threshold and without embedding::
RecurrenceNetwork(time_series, threshold=0.1)
- Create an instance of RecurrenceNetwork at a fixed (global) recurrence
rate and using time delay embedding::
RecurrenceNetwork(time_series, dim=3, tau=2,
recurrence_rate=0.05).recurrence_rate()
"""
#
# Internal methods
#
[docs] def __init__(self, time_series, metric="supremum", normalize=False,
missing_values=False, silence_level=0, **kwds):
"""
Initialize an instance of RecurrenceNetwork.
Creates an embedding of the given time series, calculates a recurrence
plot from the embedding and then creates a Network object from the
recurrence plot, interpreting the recurrence matrix as the adjacency
matrix of a complex network.
Either recurrence threshold ``threshold``/``threshold_std``, recurrence
rate ``recurrence_rate`` or local recurrence rate
``local_recurrence_rate`` have to be given as keyword arguments.
Embedding is only supported for scalar time series. If embedding
dimension ``dim`` and delay ``tau`` are **both** given as keyword
arguments, embedding is applied. Multidimensional time series are
processed as is by default.
:type time_series: 2D array (time, dimension)
:arg time_series: The time series to be analyzed, can be scalar or
multi-dimensional.
:arg str metric: The metric for measuring distances in phase space
("manhattan", "euclidean", "supremum").
:arg bool normalize: Decide whether to normalize the time series to
zero mean and unit standard deviation.
:arg bool missing_values: Toggle special treatment of missing values in
:attr:`.RecurrencePlot.time_series`.
:arg number silence_level: Inverse level of verbosity of the object.
:arg number threshold: The recurrence threshold keyword for generating
the recurrence network using a fixed threshold.
:arg number threshold_std: The recurrence threshold keyword for
generating the recurrence plot using a fixed threshold in units of
the time series' STD.
:arg number recurrence_rate: The recurrence rate keyword for generating
the recurrence network using a fixed recurrence rate.
:arg number local_recurrence_rate: The local recurrence rate keyword
for generating the recurrence plot using a fixed local recurrence
rate (same number of recurrences for each state vector).
:arg number adaptive_neighborhood_size: The adaptive neighborhood size
parameter for generating recurrence plots based on the algorithm in
[Xu2008]_.
:arg number dim: The embedding dimension.
:arg number tau: The embedding delay.
:type node_weights: 1D array (time)
:arg node_weights: The sequence of weights associated with each
node for calculating n.s.i. network measures.
"""
# Initialize the underlying RecurrencePlot object
RecurrencePlot.__init__(self, time_series=time_series, metric=metric,
normalize=normalize,
missing_values=missing_values,
silence_level=silence_level, **kwds)
# Set diagonal of R to zero to avoid self-loops in the recurrence
# network
A = self.R.copy()
A.flat[::self.N+1] = 0
# Get keywords
local_recurrence_rate = kwds.get("local_recurrence_rate")
node_weights = kwds.get("node_weights")
# Assign to each embedded state vector the weight of the earliest
# sample contained in it (too simple!)
if node_weights is not None:
node_weights = node_weights[:self.N]
# Remove state vectors containing missing values
if missing_values:
A = np.delete(A, np.where(self.missing_value_indices), axis=0)
A = np.delete(A, np.where(self.missing_value_indices), axis=1)
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix.
if local_recurrence_rate is not None:
# A recurrence network with fixed local recurrence rate (Eckmann
# definition of a recurrence plot) is directed by definition.
Network.__init__(self, A, directed=True, node_weights=node_weights,
silence_level=silence_level)
else:
# Create a Network object interpreting the recurrence matrix as
# the graph adjacency matrix. Recurrence networks are undirected
# by definition.
Network.__init__(self, A, directed=False,
node_weights=node_weights,
silence_level=silence_level)
[docs] def __str__(self):
"""
Returns a string representation.
"""
return 'RecurrenceNetwork:\n%s\n%s' % (
RecurrencePlot.__str__(self), Network.__str__(self))
[docs] def clear_cache(self):
"""
Clean up memory by deleting information that can be recalculated from
basic data.
Extends the clean up methods of the parent classes.
"""
# Call clean up of RecurrencePlot
RecurrencePlot.clear_cache(self)
# Call clean up of Network
Network.clear_cache(self)
#
# Methods to handle recurrence networks
#
[docs] def set_fixed_threshold(self, threshold):
"""
Create a recurrence network at a fixed threshold.
:arg number threshold: The threshold.
"""
# Set fixed threshold on recurrence plot level
RecurrencePlot.set_fixed_threshold(self, threshold)
# Set diagonal of R to zero to avoid self-loops in the recurrence
# network
A = self.R.copy()
A.flat[::self.N+1] = 0
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix. Recurrence networks are undirected by
# definition.
Network.__init__(self, A, directed=False,
silence_level=self.silence_level)
[docs] def set_fixed_threshold_std(self, threshold_std):
"""
Set the recurrence network to a fixed threshold in units of the
standard deviation of the time series.
:arg number threshold_std: The recurrence threshold in units of the
standard deviation of the time series.
"""
# Set fixed threshold in units of STD on recurrence plot level
RecurrencePlot.set_fixed_threshold_std(self, threshold_std)
# Set diagonal of R to zero to avoid self-loops in the recurrence
# network
A = self.R.copy()
A.flat[::self.N+1] = 0
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix. Recurrence networks are undirected by
# definition.
Network.__init__(self, A, directed=False,
silence_level=self.silence_level)
[docs] def set_fixed_recurrence_rate(self, recurrence_rate):
"""
Create a recurrence network at a fixed link density (recurrence rate).
:arg number recurrence_rate: The link density / recurrence rate.
"""
# Set fixed recurrence rate on recurrence plot level
RecurrencePlot.set_fixed_recurrence_rate(self, recurrence_rate)
# Set diagonal of R to zero to avoid self-loops in the recurrence
# network
A = self.R.copy()
A.flat[::self.N+1] = 0
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix. Recurrence networks are undirected by
# definition.
Network.__init__(self, A, directed=False,
silence_level=self.silence_level)
[docs] def set_fixed_local_recurrence_rate(self, local_recurrence_rate):
"""
Create a recurrence network at a fixed local recurrence rate.
This leads to a directed recurrence network with identical out-degree
:math:`int(N * local_recurrence_rate)`, but variable in-degree. The
associated recurrence plot coincides with the original Eckmann
definition.
:arg number local_recurrence_rate: The local recurrence rate.
"""
# Set fixed local recurrence rate on recurrence plot level
RecurrencePlot.\
set_fixed_local_recurrence_rate(self, local_recurrence_rate)
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix. Set diagonal of R to zero to avoid
# self-loops in the recurrence network
A = self.R.copy()
A.flat[::self.N+1] = 0
# A recurrence network with fixed local recurrence rate (Eckmann
# definition of a recurrence plot) is directed by definition.
Network.__init__(self, A, directed=True,
silence_level=self.silence_level)
[docs] def set_adaptive_neighborhood_size(self, adaptive_neighborhood_size,
order=None):
"""
Create a recurrence network using the :index:`adaptive neighborhood
size <single: adaptive neighborhood size; recurrence network>`
algorithm used in [Xu2008]_.
The exact algorithm was deduced from private correspondence with the
authors. It leads to an undirected network with mean degree
:math:`<k> = 2 * m`, where m is the adaptive_neighborhood_size.
The degree :math:`k_v` of single nodes may vary, but :math:`k_v >= m`
holds!
:arg number adaptive_neighborhood_size: The number of adaptive nearest
neighbors (recurrences) assigned to each state vector.
:type order: 1D array (int32)
:arg order: The indices of state vectors in the order desired for
processing by the algorithm.
"""
# Set adaptive neighborhood size on recurrence plot level
RecurrencePlot.set_adaptive_neighborhood_size(
self, adaptive_neighborhood_size, order)
# Create a Network object interpreting the recurrence matrix as the
# graph adjacency matrix. Set diagonal of R to zero to avoid
# self-loops in the recurrence network
A = self.R.copy()
A.flat[::self.N+1] = 0
# A recurrence network with fixed local recurrence rate (Eckmann
# definition of a recurrence plot) is directed by definition.
Network.__init__(self, A, directed=False,
silence_level=self.silence_level)
[docs] def transitivity_dim_single_scale(self):
"""
Return transitivity dimension for a single scale.
The single scale transitivity dimension can be interpreted as a global
measure of the dimensionality of the set of points underlying the
recurrence network ([Donner2011b]_.). The scale is determined by the
chosen recurrence threshold. Note that the maxima and minima of the
single scale transitivity dimension when varying the scale give a
more meaningful measure of dimensionality as is explained in
[Donner2011b]_.
**Attention:** currently only works correctly for supremum norm.
:rtype: float
:return: the single scale transitivity dimension.
"""
return np.log(self.transitivity()) / np.log(3. / 4.)
[docs] def local_clustering_dim_single_scale(self):
"""
Return local clustering dimension for a single scale.
The single scale local clustering dimension can be interpreted as a
local measureof the dimensionality of the set of points underlying the
recurrence network ([Donner2011b]_.). The scale is determined by the
chosen recurrence threshold. Note that the maxima and minima of the
single scale local clustering dimension when varying the scale give a
more meaningful measure of dimensionality as is explained in
[Donner2011b]_.
**Attention:**
currently only works correctly for supremum norm.
:rtype: 1d numpy array [node] of float
:return: the single scale transitivity dimension.
"""
return np.log(self.local_clustering()) / np.log(3. / 4.)