-
Notifications
You must be signed in to change notification settings - Fork 18
/
hfd.py
138 lines (110 loc) · 3.77 KB
/
hfd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/python3
"""
Higuchi Fractal Dimension according to:
T. Higuchi, Approach to an Irregular Time Series on the
Basis of the Fractal Theory, Physica D, 1988; 31: 277-283.
"""
import os
import ctypes
import numpy as np
from numpy.ctypeslib import ndpointer
def curve_length(X,opt=True,num_k=50,k_max=None):
"""
Calculate curve length <Lk> for Higuchi Fractal Dimension (HFD)
Input:
X - input (time) series (must be 1D, to be converted into a NumPy array)
opt (=True) - optimized? (if libhfd.so was compiled uses the faster code).
num_k - number of k values to generate.
k_max - the maximum k (the k array is generated uniformly in log space
from 2 to k_max)
Output:
k - interval "times", window sizes
Lk - curve length
"""
### Make sure X is a NumPy array with the correct dimension
X = np.array(X)
if X.ndim != 1:
raise ValueError("Input array must be 1D (time series).")
N = X.size
### Get interval "time"
k_arr = interval_t(N,num_val=num_k,kmax=k_max)
### The average length
Lk = np.zeros(k_arr.size)
### C library
if opt:
X = np.require(X, float, ('C', 'A'))
k_arr = np.require(k_arr, ctypes.c_size_t, ('C', 'A'))
Lk = np.require(Lk, float, ('C', 'A'))
## Load library here
libhfd = init_lib()
## Run the C code here
libhfd.curve_length(k_arr,k_arr.size,X,N,Lk)
else:
### Native Python run
for i in range(k_arr.size):# over array of k's
Lmk = 0.0
for j in range(k_arr[i]):# over m's
## Construct X_k^m, i.e. X_(k_arr[i])^j, as X[j::k_arr[i]]
## Calculate L_m(k)
Lmk += (
np.sum(
np.abs(
np.diff( X[j::k_arr[i]] )
)
)
* (N - 1) /
(
( (N-j-1)//k_arr[i] )
*
k_arr[i]
)
) / k_arr[i]
### Calculate the average Lmk
Lk[i] = Lmk / k_arr[i]
return (k_arr, Lk);
def lin_fit_hfd(k,L,log=True):
"""
Calculate Higuchi Fractal Dimension (HFD) by fitting a line to already computed
interval times k and curve lengths L
Input:
k - interval "times", window sizes
L - curve length
log (=True) - k and L values will be transformed to np.log2(k) and np.log2(L),
respectively
Output:
HFD
"""
if log:
return (-np.polyfit(np.log2(k),np.log2(L),deg=1)[0]);
else:
return (-np.polyfit(k,L,deg=1)[0]);
def hfd(X,**kwargs):
"""
Calculate Higuchi Fractal Dimension (HFD) for 1D data/series
Input:
X - input (time) series (must be 1D, to be converted into a NumPy array)
Output:
HFD
"""
k, L = curve_length(X,**kwargs)
return lin_fit_hfd(k, L);
def interval_t(size,num_val=50,kmax=None):
### Generate sequence of interval times, k
if kmax is None:
k_stop = size//2
else:
k_stop = kmax
if k_stop > size//2:## prohibit going larger than N/2
k_stop = size//2
print("Warning: k cannot be longer than N/2")
k = np.logspace(start=np.log2(2),stop=np.log2(k_stop),base=2,num=num_val,dtype=int)
return np.unique(k);
def init_lib():
libdir = os.path.dirname(__file__)
libfile = os.path.join(libdir, "libhfd.so")
lib = ctypes.CDLL(libfile)
rwptr = ndpointer(float, flags=('C','A','W'))
rwptr_sizet = ndpointer(ctypes.c_size_t, flags=('C','A','W'))
lib.curve_length.restype = ctypes.c_int
lib.curve_length.argtypes = [rwptr_sizet, ctypes.c_size_t, rwptr, ctypes.c_size_t, rwptr]
return lib;