FluctuationAnalysisTools 1.6.1a10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fluctuationanalysistools-1.6.1a10/FluctuationAnalysisTools.egg-info/PKG-INFO +36 -0
- fluctuationanalysistools-1.6.1a10/FluctuationAnalysisTools.egg-info/SOURCES.txt +27 -0
- fluctuationanalysistools-1.6.1a10/FluctuationAnalysisTools.egg-info/dependency_links.txt +1 -0
- fluctuationanalysistools-1.6.1a10/FluctuationAnalysisTools.egg-info/top_level.txt +2 -0
- fluctuationanalysistools-1.6.1a10/LICENSE.txt +7 -0
- fluctuationanalysistools-1.6.1a10/MANIFEST.in +0 -0
- fluctuationanalysistools-1.6.1a10/PKG-INFO +36 -0
- fluctuationanalysistools-1.6.1a10/README.md +27 -0
- fluctuationanalysistools-1.6.1a10/StatTools/Gamma.py +145 -0
- fluctuationanalysistools-1.6.1a10/StatTools/__init__.py +0 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/__init__.py +2 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/dfa.py +235 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/dpcca.py +250 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/dpcca_as_class.py +128 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/fa.py +87 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/movmean.py +25 -0
- fluctuationanalysistools-1.6.1a10/StatTools/analysis/qss.py +204 -0
- fluctuationanalysistools-1.6.1a10/StatTools/auxiliary.py +340 -0
- fluctuationanalysistools-1.6.1a10/StatTools/generators/__init__.py +2 -0
- fluctuationanalysistools-1.6.1a10/StatTools/generators/base_filter.py +248 -0
- fluctuationanalysistools-1.6.1a10/StatTools/generators/cholesky_transform.py +118 -0
- fluctuationanalysistools-1.6.1a10/StatTools/generators/fbm.py +117 -0
- fluctuationanalysistools-1.6.1a10/StatTools/generators/field_generator.py +402 -0
- fluctuationanalysistools-1.6.1a10/StatTools_C_API.cpp +374 -0
- fluctuationanalysistools-1.6.1a10/pyproject.toml +21 -0
- fluctuationanalysistools-1.6.1a10/setup.cfg +4 -0
- fluctuationanalysistools-1.6.1a10/setup.py +19 -0
- fluctuationanalysistools-1.6.1a10/tests/test_dpcca.py +126 -0
- fluctuationanalysistools-1.6.1a10/tests/test_fa.py +87 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: FluctuationAnalysisTools
|
|
3
|
+
Version: 1.6.1a10
|
|
4
|
+
Summary: This library allows to create and process long-term dependent datasets.
|
|
5
|
+
Author-email: Aleksandr Sinitca <amsinitca@etu.ru>, Alexandr Kuzmenko <alexander.k.spb@gmail.com>, Asya Lyanova <ailianova@etu.ru>
|
|
6
|
+
Maintainer-email: Aleksandr Sinitca <amsinitca@etu.ru>
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE.txt
|
|
9
|
+
|
|
10
|
+
# StatTools
|
|
11
|
+
This library allows to create and process long-term dependent datasets.
|
|
12
|
+
|
|
13
|
+
## Installation:
|
|
14
|
+
|
|
15
|
+
python setup.py install
|
|
16
|
+
|
|
17
|
+
## Basis usage
|
|
18
|
+
|
|
19
|
+
1. To create a simple dataset with given Hurst parameter:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from StatTools.filters import FilteredArray
|
|
23
|
+
|
|
24
|
+
h = 0.8 # choose Hurst parameter
|
|
25
|
+
total_vectors = 1000 # total number of vectors in output
|
|
26
|
+
vectors_length = 1440 # each vector's length
|
|
27
|
+
t = 8 # threads in use during computation
|
|
28
|
+
|
|
29
|
+
correlated_vectors = Filter(h, vectors_length).generate(n_vectors=total_vectors,
|
|
30
|
+
threads=t, progress_bar=True)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Contributors
|
|
34
|
+
|
|
35
|
+
* [Alexandr Kuzmenko](https://github.com/alexandr-1k)
|
|
36
|
+
* [Aleksandr Sinitca](https://github.com/Sinitca-Aleksandr)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
StatTools_C_API.cpp
|
|
5
|
+
pyproject.toml
|
|
6
|
+
setup.py
|
|
7
|
+
FluctuationAnalysisTools.egg-info/PKG-INFO
|
|
8
|
+
FluctuationAnalysisTools.egg-info/SOURCES.txt
|
|
9
|
+
FluctuationAnalysisTools.egg-info/dependency_links.txt
|
|
10
|
+
FluctuationAnalysisTools.egg-info/top_level.txt
|
|
11
|
+
StatTools/Gamma.py
|
|
12
|
+
StatTools/__init__.py
|
|
13
|
+
StatTools/auxiliary.py
|
|
14
|
+
StatTools/analysis/__init__.py
|
|
15
|
+
StatTools/analysis/dfa.py
|
|
16
|
+
StatTools/analysis/dpcca.py
|
|
17
|
+
StatTools/analysis/dpcca_as_class.py
|
|
18
|
+
StatTools/analysis/fa.py
|
|
19
|
+
StatTools/analysis/movmean.py
|
|
20
|
+
StatTools/analysis/qss.py
|
|
21
|
+
StatTools/generators/__init__.py
|
|
22
|
+
StatTools/generators/base_filter.py
|
|
23
|
+
StatTools/generators/cholesky_transform.py
|
|
24
|
+
StatTools/generators/fbm.py
|
|
25
|
+
StatTools/generators/field_generator.py
|
|
26
|
+
tests/test_dpcca.py
|
|
27
|
+
tests/test_fa.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2021 Alexandr Kuzmenko
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: FluctuationAnalysisTools
|
|
3
|
+
Version: 1.6.1a10
|
|
4
|
+
Summary: This library allows to create and process long-term dependent datasets.
|
|
5
|
+
Author-email: Aleksandr Sinitca <amsinitca@etu.ru>, Alexandr Kuzmenko <alexander.k.spb@gmail.com>, Asya Lyanova <ailianova@etu.ru>
|
|
6
|
+
Maintainer-email: Aleksandr Sinitca <amsinitca@etu.ru>
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE.txt
|
|
9
|
+
|
|
10
|
+
# StatTools
|
|
11
|
+
This library allows to create and process long-term dependent datasets.
|
|
12
|
+
|
|
13
|
+
## Installation:
|
|
14
|
+
|
|
15
|
+
python setup.py install
|
|
16
|
+
|
|
17
|
+
## Basis usage
|
|
18
|
+
|
|
19
|
+
1. To create a simple dataset with given Hurst parameter:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from StatTools.filters import FilteredArray
|
|
23
|
+
|
|
24
|
+
h = 0.8 # choose Hurst parameter
|
|
25
|
+
total_vectors = 1000 # total number of vectors in output
|
|
26
|
+
vectors_length = 1440 # each vector's length
|
|
27
|
+
t = 8 # threads in use during computation
|
|
28
|
+
|
|
29
|
+
correlated_vectors = Filter(h, vectors_length).generate(n_vectors=total_vectors,
|
|
30
|
+
threads=t, progress_bar=True)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Contributors
|
|
34
|
+
|
|
35
|
+
* [Alexandr Kuzmenko](https://github.com/alexandr-1k)
|
|
36
|
+
* [Aleksandr Sinitca](https://github.com/Sinitca-Aleksandr)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# StatTools
|
|
2
|
+
This library allows to create and process long-term dependent datasets.
|
|
3
|
+
|
|
4
|
+
## Installation:
|
|
5
|
+
|
|
6
|
+
python setup.py install
|
|
7
|
+
|
|
8
|
+
## Basis usage
|
|
9
|
+
|
|
10
|
+
1. To create a simple dataset with given Hurst parameter:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from StatTools.filters import FilteredArray
|
|
14
|
+
|
|
15
|
+
h = 0.8 # choose Hurst parameter
|
|
16
|
+
total_vectors = 1000 # total number of vectors in output
|
|
17
|
+
vectors_length = 1440 # each vector's length
|
|
18
|
+
t = 8 # threads in use during computation
|
|
19
|
+
|
|
20
|
+
correlated_vectors = Filter(h, vectors_length).generate(n_vectors=total_vectors,
|
|
21
|
+
threads=t, progress_bar=True)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Contributors
|
|
25
|
+
|
|
26
|
+
* [Alexandr Kuzmenko](https://github.com/alexandr-1k)
|
|
27
|
+
* [Aleksandr Sinitca](https://github.com/Sinitca-Aleksandr)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
from tqdm import tqdm
|
|
3
|
+
from multiprocessing import Pool, Value, Array, Lock
|
|
4
|
+
from ctypes import c_double
|
|
5
|
+
from contextlib import closing
|
|
6
|
+
from threading import Thread
|
|
7
|
+
from StatTools.analysis.dfa import bar_manager
|
|
8
|
+
from functools import partial
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GammaReplacement():
|
|
12
|
+
|
|
13
|
+
def __init__(self, dataset, set_mean=20, set_std=6):
|
|
14
|
+
|
|
15
|
+
if set_mean < 0:
|
|
16
|
+
error_str = "\tGammaReplacement Error: Mean value is not supposed to be negative!"
|
|
17
|
+
raise NameError(error_str)
|
|
18
|
+
|
|
19
|
+
if set_std ==0 or set_mean == 0:
|
|
20
|
+
error_str = "\tGammaReplacement Error: Zero mean or std!"
|
|
21
|
+
raise NameError(error_str)
|
|
22
|
+
|
|
23
|
+
if not isinstance(dataset, type(numpy.array([]))):
|
|
24
|
+
self.dataset = numpy.array(dataset)
|
|
25
|
+
|
|
26
|
+
else:
|
|
27
|
+
self.dataset = dataset
|
|
28
|
+
|
|
29
|
+
self.set_std = set_std
|
|
30
|
+
self.set_mean = set_mean
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def rank_replacement_with_gamma(initial_vector):
|
|
34
|
+
sorted_x1 = numpy.sort(initial_vector)
|
|
35
|
+
|
|
36
|
+
var_normal = numpy.var(initial_vector, ddof=1)
|
|
37
|
+
mean_normal = numpy.mean(initial_vector)
|
|
38
|
+
|
|
39
|
+
theta = var_normal / mean_normal
|
|
40
|
+
k = mean_normal / (var_normal / mean_normal)
|
|
41
|
+
validation_vector = numpy.random.mtrand.gamma(k, theta, len(initial_vector))
|
|
42
|
+
|
|
43
|
+
sorted_x2 = numpy.sort(validation_vector)
|
|
44
|
+
|
|
45
|
+
# @njit(cache=True)
|
|
46
|
+
def rank_replacement_core(x1, sorted_x1, sorted_x2):
|
|
47
|
+
for i in range(len(x1)):
|
|
48
|
+
get_index_in_sorted_x1 = numpy.where(sorted_x1 == x1[i])[0][0]
|
|
49
|
+
x1[i] = sorted_x2[get_index_in_sorted_x1]
|
|
50
|
+
return x1
|
|
51
|
+
|
|
52
|
+
x1 = rank_replacement_core(initial_vector, sorted_x1, sorted_x2)
|
|
53
|
+
|
|
54
|
+
return x1
|
|
55
|
+
|
|
56
|
+
def perform(self, threads=1, progress_bar=False):
|
|
57
|
+
|
|
58
|
+
if threads == 1:
|
|
59
|
+
|
|
60
|
+
if self.dataset.size > pow(10, 6):
|
|
61
|
+
print("\tGamaReplacement Warning: Given the size of input dataset it'd be faster to use more threads . . .")
|
|
62
|
+
|
|
63
|
+
if progress_bar:
|
|
64
|
+
bar = tqdm(desc="GammaReplacment", total=len(self.dataset), leave=False, position=0)
|
|
65
|
+
|
|
66
|
+
one_dim = False
|
|
67
|
+
|
|
68
|
+
if self.dataset.ndim == 1:
|
|
69
|
+
|
|
70
|
+
self.dataset = [self.dataset]
|
|
71
|
+
one_dim = True
|
|
72
|
+
|
|
73
|
+
for v, vector in enumerate(self.dataset):
|
|
74
|
+
self.dataset[v] = self.dataset[v] * (self.set_std / numpy.std(self.dataset[v], ddof=1))
|
|
75
|
+
self.dataset[v] = self.dataset[v] + (-numpy.mean(self.dataset[v]) + self.set_mean)
|
|
76
|
+
self.dataset[v] = self.rank_replacement_with_gamma(self.dataset[v])
|
|
77
|
+
self.dataset[v] = self.dataset[v] * (self.set_std / numpy.std(self.dataset[v], ddof=1))
|
|
78
|
+
self.dataset[v] = self.dataset[v] + (-numpy.mean(self.dataset[v]) + self.set_mean)
|
|
79
|
+
a = numpy.std(self.dataset[v], ddof=1)
|
|
80
|
+
if progress_bar:
|
|
81
|
+
bar.update(1)
|
|
82
|
+
|
|
83
|
+
if one_dim:
|
|
84
|
+
return self.dataset[0]
|
|
85
|
+
|
|
86
|
+
return self.dataset
|
|
87
|
+
|
|
88
|
+
else:
|
|
89
|
+
|
|
90
|
+
vectors_indices = numpy.linspace(0, len(self.dataset) - 1, len(self.dataset), dtype=int)
|
|
91
|
+
vectors_by_threads = numpy.array_split(vectors_indices, threads)
|
|
92
|
+
|
|
93
|
+
shared_initial_dataset = Array(c_double, self.dataset.size)
|
|
94
|
+
vectors_num = len(self.dataset)
|
|
95
|
+
vectors_length = len(self.dataset[0])
|
|
96
|
+
|
|
97
|
+
numpy.copyto(numpy.frombuffer(shared_initial_dataset.get_obj()).reshape((vectors_num, vectors_length)),
|
|
98
|
+
self.dataset)
|
|
99
|
+
|
|
100
|
+
bar_value = Value('i', 0)
|
|
101
|
+
stop_bit = Value('i', 0)
|
|
102
|
+
lock = Lock()
|
|
103
|
+
|
|
104
|
+
if progress_bar:
|
|
105
|
+
bar = Thread(target=bar_manager, args=('GammaReplacement', vectors_num, bar_value, lock, 'total', stop_bit))
|
|
106
|
+
bar.start()
|
|
107
|
+
|
|
108
|
+
with closing(Pool(processes=threads, initializer=self.global_initializer, initargs=(shared_initial_dataset,
|
|
109
|
+
bar_value, lock))) as pool:
|
|
110
|
+
pool.map(partial(self.processing_vectors, set_mean=self.set_mean, set_std=self.set_std, vectors_num=vectors_num,
|
|
111
|
+
vectors_length=vectors_length, function=self.rank_replacement_with_gamma), vectors_by_threads)
|
|
112
|
+
|
|
113
|
+
stop_bit.value += 1
|
|
114
|
+
|
|
115
|
+
output_array = numpy.frombuffer(shared_initial_dataset.get_obj()).reshape((vectors_num, vectors_length))
|
|
116
|
+
|
|
117
|
+
return output_array
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def global_initializer(shared_init_data, b_val, b_lk):
|
|
121
|
+
global SHARED_VECTORS
|
|
122
|
+
global BAR_VALUE
|
|
123
|
+
global BAR_LOCK
|
|
124
|
+
SHARED_VECTORS = shared_init_data
|
|
125
|
+
BAR_VALUE = b_val
|
|
126
|
+
BAR_LOCK = b_lk
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def processing_vectors(indices, set_mean, set_std, vectors_num, vectors_length, function):
|
|
131
|
+
|
|
132
|
+
def get_pointer():
|
|
133
|
+
return numpy.frombuffer(SHARED_VECTORS.get_obj(), dtype=c_double).reshape((vectors_num, vectors_length))
|
|
134
|
+
|
|
135
|
+
for v in indices:
|
|
136
|
+
vector = get_pointer()[v]
|
|
137
|
+
vector = vector * (set_std / numpy.std(vector, ddof=1))
|
|
138
|
+
vector = vector + numpy.abs(numpy.mean(vector) - set_mean)
|
|
139
|
+
vector = function(vector)
|
|
140
|
+
vector = vector * (set_std / numpy.std(vector, ddof=1))
|
|
141
|
+
vector = vector + numpy.abs(numpy.mean(vector) - set_mean)
|
|
142
|
+
get_pointer()[v] = vector
|
|
143
|
+
|
|
144
|
+
with BAR_LOCK:
|
|
145
|
+
BAR_VALUE.value += 1
|
|
File without changes
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
from math import floor, exp, ceil
|
|
3
|
+
from multiprocessing import Pool, Array, Value, Lock, cpu_count
|
|
4
|
+
from ctypes import c_double
|
|
5
|
+
from contextlib import closing
|
|
6
|
+
from threading import Thread
|
|
7
|
+
from functools import partial
|
|
8
|
+
from tqdm import tqdm, TqdmWarning
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def bar_manager(description, total, counter, lock, mode="total", stop_bit=None):
|
|
13
|
+
max_val = total
|
|
14
|
+
if mode == "percent":
|
|
15
|
+
max_val = 100
|
|
16
|
+
with closing(tqdm(desc=description, total=max_val, leave=False, position=0)) as bar:
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
last_val = counter.value
|
|
20
|
+
while True:
|
|
21
|
+
if stop_bit is not None:
|
|
22
|
+
if stop_bit.value > 0:
|
|
23
|
+
break
|
|
24
|
+
|
|
25
|
+
time.sleep(0.25)
|
|
26
|
+
with lock:
|
|
27
|
+
if counter.value > last_val:
|
|
28
|
+
if mode == "percent":
|
|
29
|
+
bar.update(round(((counter.value - last_val) * 100 / total), 2))
|
|
30
|
+
else:
|
|
31
|
+
bar.update(counter.value - last_val)
|
|
32
|
+
last_val = counter.value
|
|
33
|
+
|
|
34
|
+
if counter.value == total:
|
|
35
|
+
bar.close()
|
|
36
|
+
break
|
|
37
|
+
except TqdmWarning:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
class DFA:
|
|
41
|
+
|
|
42
|
+
def __init__(self, dataset, degree=2, root=False, ignore_input_control=False):
|
|
43
|
+
if ignore_input_control:
|
|
44
|
+
s_return_1d, F_s_return_1d = self.dfa_core_cycle(dataset, degree, root)
|
|
45
|
+
self.s = s_return_1d
|
|
46
|
+
self.F_s = F_s_return_1d
|
|
47
|
+
else:
|
|
48
|
+
if isinstance(dataset, type("string")):
|
|
49
|
+
try:
|
|
50
|
+
dataset = numpy.loadtxt(dataset)
|
|
51
|
+
except OSError:
|
|
52
|
+
error_str = "\n The file either doesn't exit or you use wrong path!"
|
|
53
|
+
raise NameError(error_str)
|
|
54
|
+
|
|
55
|
+
if numpy.size(dataset) == 0:
|
|
56
|
+
error_str = "\n Input file is empty!"
|
|
57
|
+
raise NameError(error_str)
|
|
58
|
+
|
|
59
|
+
if not isinstance(dataset, type(numpy.array([]))):
|
|
60
|
+
try: # in case of list
|
|
61
|
+
dataset = numpy.array(dataset, dtype=float)
|
|
62
|
+
except ValueError:
|
|
63
|
+
error_str = "\n Input dataset is supposed to be numpy array, list or directory!"
|
|
64
|
+
raise NameError(error_str)
|
|
65
|
+
|
|
66
|
+
dataset = numpy.array(dataset)
|
|
67
|
+
|
|
68
|
+
if dataset.ndim > 2 or dataset.ndim == 0:
|
|
69
|
+
error_str = "\n You can not use such input array! Only 1- or 2-dimensional arrays are allowed!"
|
|
70
|
+
raise NameError(error_str)
|
|
71
|
+
|
|
72
|
+
self.dataset = dataset
|
|
73
|
+
self.degree = degree
|
|
74
|
+
self.root = root
|
|
75
|
+
|
|
76
|
+
if self.dataset.ndim == 1:
|
|
77
|
+
s_max = int(len(dataset) / 4)
|
|
78
|
+
try:
|
|
79
|
+
log_s_max = numpy.arange(1.6, numpy.log(s_max), 0.5)
|
|
80
|
+
except ValueError:
|
|
81
|
+
error_str = "\n Wrong input array ! (It's probably too short)"
|
|
82
|
+
raise NameError(error_str)
|
|
83
|
+
if numpy.size(log_s_max) < 1:
|
|
84
|
+
error_str = "\n Input array is too small! (It usually requires 20 or more samples!)"
|
|
85
|
+
raise NameError(error_str)
|
|
86
|
+
|
|
87
|
+
if self.dataset.ndim == 2:
|
|
88
|
+
|
|
89
|
+
s_max = int(len(dataset[0]) / 4)
|
|
90
|
+
try:
|
|
91
|
+
log_s_max = numpy.arange(1.6, numpy.log(s_max), 0.5)
|
|
92
|
+
except ValueError:
|
|
93
|
+
error_str = "\n Wrong input vectors in input matrix! (They are probably too short)"
|
|
94
|
+
raise NameError(error_str)
|
|
95
|
+
if numpy.size(log_s_max) < 1:
|
|
96
|
+
error_str = "\n Vectors in your input array are too short! Use longer vectors " \
|
|
97
|
+
"(it usually requires 20 or more samples) or transpose!"
|
|
98
|
+
raise NameError(error_str)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def initializer_for_parallel_mod(shared_array, h_est, shared_c, shared_l):
|
|
102
|
+
global datasets_array
|
|
103
|
+
global estimations
|
|
104
|
+
global shared_counter
|
|
105
|
+
global shared_lock
|
|
106
|
+
datasets_array = shared_array
|
|
107
|
+
estimations = h_est
|
|
108
|
+
shared_counter = shared_c
|
|
109
|
+
shared_lock = shared_l
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def dfa_core_cycle(dataset, degree, root):
|
|
113
|
+
data_mean = numpy.mean(dataset)
|
|
114
|
+
data = dataset - data_mean
|
|
115
|
+
Y_cumsum = numpy.cumsum(data)
|
|
116
|
+
|
|
117
|
+
s_max = int(len(data) / 4)
|
|
118
|
+
|
|
119
|
+
log_s_max = numpy.arange(1.6, numpy.log(s_max), 0.5)
|
|
120
|
+
|
|
121
|
+
x_Axis = []
|
|
122
|
+
y_Axis = []
|
|
123
|
+
|
|
124
|
+
for step in log_s_max:
|
|
125
|
+
s = numpy.linspace(1, floor(exp(step)), floor(exp(step)), dtype=int)
|
|
126
|
+
cycles_amount = floor(len(data) / len(s))
|
|
127
|
+
|
|
128
|
+
F_q_s_sum = 0
|
|
129
|
+
for i in range(1, cycles_amount):
|
|
130
|
+
indices = numpy.array((s - (i + 0.5) * len(s)), dtype=int)
|
|
131
|
+
Y_cumsum_s = numpy.take(Y_cumsum, s)
|
|
132
|
+
|
|
133
|
+
coef = numpy.polyfit(indices, Y_cumsum_s, deg=degree)
|
|
134
|
+
current_trend = numpy.polyval(coef, indices)
|
|
135
|
+
F_2 = sum(pow((Y_cumsum_s - current_trend), 2)) / len(s)
|
|
136
|
+
F_q_s_sum += pow(F_2, (degree / 2))
|
|
137
|
+
s += floor(exp(step))
|
|
138
|
+
|
|
139
|
+
F1 = pow(((1 / cycles_amount) * F_q_s_sum), 1 / degree)
|
|
140
|
+
x_Axis.append(numpy.log(floor(exp(step))))
|
|
141
|
+
if root:
|
|
142
|
+
y_Axis.append(numpy.log(F1 / numpy.sqrt(len(s))))
|
|
143
|
+
else:
|
|
144
|
+
y_Axis.append(numpy.log(F1))
|
|
145
|
+
|
|
146
|
+
return numpy.array(x_Axis), numpy.array(y_Axis)
|
|
147
|
+
|
|
148
|
+
def find_h(self, simple_mode=True):
|
|
149
|
+
if self.dataset.ndim == 1:
|
|
150
|
+
self.s, self.F_s = self.dfa_core_cycle(self.dataset, self.degree, self.root)
|
|
151
|
+
else:
|
|
152
|
+
self.s = numpy.array([])
|
|
153
|
+
self.F_s = numpy.array([])
|
|
154
|
+
for vector in self.dataset:
|
|
155
|
+
s, F_s = self.dfa_core_cycle(vector, self.degree, self.root)
|
|
156
|
+
if numpy.size(self.s) < 1:
|
|
157
|
+
self.s = s
|
|
158
|
+
self.F_s = F_s
|
|
159
|
+
else:
|
|
160
|
+
self.s = numpy.vstack((self.s, s))
|
|
161
|
+
self.F_s = numpy.vstack((self.F_s, F_s))
|
|
162
|
+
|
|
163
|
+
if simple_mode:
|
|
164
|
+
|
|
165
|
+
if self.s.ndim == 1:
|
|
166
|
+
return numpy.polyfit(self.s, self.F_s, deg=1)[0]
|
|
167
|
+
else:
|
|
168
|
+
h_estimation = []
|
|
169
|
+
for s, F_s in zip(self.s, self.F_s):
|
|
170
|
+
h_estimation.append(numpy.polyfit(s, F_s, deg=1)[0])
|
|
171
|
+
return numpy.array(h_estimation)
|
|
172
|
+
else:
|
|
173
|
+
error_str = "\n Non-linear approximation is non supported yet!"
|
|
174
|
+
raise NameError(error_str)
|
|
175
|
+
|
|
176
|
+
def parallel_2d(self, threads=cpu_count(), progress_bar=False, h_control=False, h_target=float(), h_limit=float()):
|
|
177
|
+
if threads == 1 or self.dataset.ndim == 1:
|
|
178
|
+
return self.find_h()
|
|
179
|
+
|
|
180
|
+
if len(self.dataset) / threads < 1:
|
|
181
|
+
error_str = "\n DFA Warning: Input array is too small for using it in parallel mode!" \
|
|
182
|
+
f"\n You better use either less threads ({len(self.dataset)}) or don't use " \
|
|
183
|
+
f"parallel mode at all!"
|
|
184
|
+
print(error_str)
|
|
185
|
+
h_est = self.find_h()
|
|
186
|
+
return h_est
|
|
187
|
+
|
|
188
|
+
if len(self.dataset) / threads < 10:
|
|
189
|
+
error_str = "\n DFA Warning: It may be not so effective when using parallel mode with such small array!" \
|
|
190
|
+
"\n Spawning processes creates its own overhead!"
|
|
191
|
+
print(error_str)
|
|
192
|
+
|
|
193
|
+
vectors_indices_by_threads = numpy.array_split(numpy.linspace(0, len(self.dataset) - 1, len(self.dataset),
|
|
194
|
+
dtype=int), threads)
|
|
195
|
+
|
|
196
|
+
dataset_to_memory = Array(c_double, len(self.dataset) * len(self.dataset[0]))
|
|
197
|
+
h_estimation_in_memory = Array(c_double, len(self.dataset))
|
|
198
|
+
numpy.copyto(numpy.frombuffer(dataset_to_memory.get_obj()).reshape((len(self.dataset), len(self.dataset[0]))),
|
|
199
|
+
self.dataset)
|
|
200
|
+
|
|
201
|
+
shared_counter = Value('i', 0)
|
|
202
|
+
shared_lock = Lock()
|
|
203
|
+
|
|
204
|
+
if progress_bar:
|
|
205
|
+
bar_thread = Thread(target=bar_manager, args=(f"DFA", len(self.dataset), shared_counter, shared_lock))
|
|
206
|
+
bar_thread.start()
|
|
207
|
+
|
|
208
|
+
with closing(Pool(processes=threads, initializer=self.initializer_for_parallel_mod, initargs=
|
|
209
|
+
(dataset_to_memory, h_estimation_in_memory, shared_counter, shared_lock))) as pool:
|
|
210
|
+
invalid_i = pool.map(partial(self.parallel_core, quantity=len(self.dataset), length=len(self.dataset[0]),
|
|
211
|
+
h_control=h_control, h_target=h_target, h_limit=h_limit),
|
|
212
|
+
vectors_indices_by_threads)
|
|
213
|
+
|
|
214
|
+
if h_control:
|
|
215
|
+
invalid_i = numpy.concatenate(invalid_i)
|
|
216
|
+
return numpy.frombuffer(h_estimation_in_memory.get_obj()), invalid_i
|
|
217
|
+
else:
|
|
218
|
+
return numpy.frombuffer(h_estimation_in_memory.get_obj())
|
|
219
|
+
|
|
220
|
+
def parallel_core(self, indices, quantity, length, h_control, h_target, h_limit):
|
|
221
|
+
|
|
222
|
+
invalid_i = []
|
|
223
|
+
for i in indices:
|
|
224
|
+
vector = numpy.frombuffer(datasets_array.get_obj()).reshape((quantity, length))[i]
|
|
225
|
+
x_ax, y_ax = self.dfa_core_cycle(vector, self.degree, self.root)
|
|
226
|
+
lin_reg = numpy.polyfit(x_ax, y_ax, deg=1)[0]
|
|
227
|
+
numpy.frombuffer(estimations.get_obj())[i] = lin_reg
|
|
228
|
+
with shared_lock:
|
|
229
|
+
shared_counter.value += 1
|
|
230
|
+
|
|
231
|
+
if h_control:
|
|
232
|
+
if abs(lin_reg - h_target) > h_limit:
|
|
233
|
+
invalid_i.append(i)
|
|
234
|
+
|
|
235
|
+
return numpy.array(invalid_i)
|