Source code for peccary.utils

"""
Utility functions for PECCARY
"""

import numpy as np
from math import factorial
from scipy.signal import argrelmax, argrelmin

from .timeseries import Timeseries

__all__ = ["ell2tpat", "tpat2ell", "HmaxPer", "HminPer", "calcHCplane", "getMaxC"]

[docs] def ell2tpat(ell,dt,n=5): """ Convert sampling interval to pattern timescale Parameters ---------- ell : float Sampling interval dt : float Timestep or timeseries resolution n : int, optional Sampling size, by default 5 Returns ------- float Equivalent pattern timescale """ return ell*dt*(n-1.)
[docs] def tpat2ell(tpat,dt,n=5,returnInt=True): """ Convert pattern timescale to sampling interval Parameters ---------- tpat : float or ndarray Pattern timescale dt : float Timestep or timeseries resolution n : int, optional Sampling size, by default 5 returnInt : boolean, optional Return integer or Returns ------- int, list of int, or ndarray Equivalent sampling interval(s); returns int if tpat is a single value, list of ints if tpat is array/list, and ndarray if tpat is array/list and returnInt is set to False """ if isinstance(tpat,(int, float)): return int(np.round(tpat/(dt*(n-1.)))) elif returnInt: return [int(np.round(tp/(dt*(n-1.)))) for tp in tpat] else: return np.array(tpat)/(dt*(n-1.))
[docs] def HmaxPer(n=5): """ Calculate maximum Permutation Entropy value (H) for a periodic function given the specified sampling size n. Parameters ---------- n : int Sampling size, by default 5 Returns ------- float Maximum Permuation Entropy for periodic function """ Nper = 2.*(2.*(n-2.)+1.) return np.log(Nper)/np.log(factorial(n))
[docs] def HminPer(n=5): """ Calculate minimum Permutation Entropy value (H) for a periodic function given the specified sampling size n. Parameters ---------- n : int Sampling size, by default 5 Returns ------- float Minimum Permuation Entropy for periodic function """ return np.log(2)/np.log(factorial(n))
[docs] def calcHCplane(n=5, nsteps=1000): """ Get maximum and minimum C(H) curves based on pattern length for plotting on the HC plane Parameters ---------- n : int, optional Sampling size, by default 5 nsteps : int, optional Number of steps to use for generating HC bounding curves, by default 1000 Returns ------- 4-tuple 4-tuple of ndarrays consisting (respectively) of: - H values for minimum HC curve - C values for minimum HC curve - H values for maximum HC curve - C values for maximum HC curve """ nsteps = nsteps n = n N = factorial(n) invN = 1./N log2_N = np.log2(N) log2_Np1 = np.log2(N+1.) # Set up blank arrays for x- and y-values of max/min curves Cmaxx = np.zeros((N-1)*nsteps) Cmaxy = np.zeros((N-1)*nsteps) Cminx = np.zeros(nsteps) Cminy = np.zeros(nsteps) # Calculate H and C values for minimum HC curve for i in np.arange(nsteps): pk = invN + i*(1.-(invN))/nsteps pj = (1. - pk)/(N - 1.) S = -pk * np.log2(pk) - (N - 1.) * pj * np.log2(pj) qk = pk/2. + 1./(2.*N) qj = pj/2. + 1./(2.*N) Scom = -qk * np.log2(qk) - (N - 1.) * qj * np.log2(qj) Cminx[i] = S / log2_N Cminy[i] = -2. * (S/log2_N) * (Scom - 0.5*S - 0.5*log2_N)\ /((1 + invN)*log2_Np1 - 2*np.log2(2.*N) + log2_N) # Calculate H and C values for maximum HC curve for i in np.arange(1,N): for l in np.arange(nsteps): pk = l*(1./(N-i+1.))/nsteps pj = (1. - pk)/(N - i) if pk ==0.: S = -(N - i) * pj * np.log2(pj) else: S = -pk * np.log2(pk) - (N - i) * pj * np.log2(pj) qk = pk/2. + 1./(2.*N) qj = pj/2. + 1./(2.*N) Scom = -qk * np.log2(qk) - (N - i) * qj * np.log2(qj) - \ (i-1)*(1./(2.*N))*np.log2(1./(2.*N)) Cmaxx[(i-1)*nsteps+l] = S / log2_N Cmaxy[(i-1)*nsteps+l] = -2.*(S/log2_N)*(Scom - 0.5*S - 0.5*log2_N) \ /((1. + invN)*log2_Np1 - 2.*np.log2(2.*N) + log2_N) return Cminx, Cminy, Cmaxx, Cmaxy
[docs] def getMaxC(n=5, nsteps=1000): """ Get maximum possible Statistical Complexity value from HC bounding curves for sampling size n Parameters ---------- n : int, optional Sampling, by default 5 nsteps : int, optional Number of steps to use for generating HC bounding curves, by default 1000 Returns ------- float Maximum possible C value for given n """ Cminx, Cminy, Cmaxx, Cmaxy = calcHCplane(n=n, nsteps=nsteps) return np.max(Cmaxy)
def tNatApprox(t, data, n=5, method='ncross', attr=None, dt=None, ptcl=None, crossVal=None): """ Approximate the natural timescale for an inputted timeseries. Parameters ---------- t : 1-D array Timesteps data data : 1-D array Timeseries data n : int, optional Sampling size, by default 5, by default 5 method : str, optional Method to use for natural timescale approximation, choose from 'minavg', 'maxavg', 'ncross', by default 'ncross' attr : str, optional Timeseries attribute; needed if extracting a coordinate or data attribute from a Timeseries object, by default None dt : float, optional Timestep resolution; needed if using built-in, by default None ptcl: int, optional Particle index, by default None crossVal: float, optional Central value used for 'ncross' method, by default None """ # Check if data is Timeseries object if isinstance(data, Timeseries): tser = getattr(data, attr) # data timeseries else: # Setting up data tser=np.array(data) # data timeseries if ptcl is not None: if type(ptcl) is int: tser = tser[ptcl] else: raise TypeError('Particle index (ptcl) must be integer') if len(tser.shape)>1: raise TypeError('Data must be a 1-D array') if method == 'minavg': return np.mean(np.diff(t[argrelmin(tser)])) elif method == 'maxavg': return np.mean(np.diff(t[argrelmax(tser)])) elif method == 'ncross': if crossVal is None: crossVal = np.mean(tser) else: pass crossLocs = np.where(np.sign(tser[1:]-crossVal) != np.sign(tser[:-1]-crossVal))[0] return t[-1]/(len(crossLocs)/2.) else: raise KeyError("Method {} does not exist. Please use either 'ncross', 'minavg', or 'maxavg'.".format(method))