complexeva 0.1.6__tar.gz → 0.1.8__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.
- {complexeva-0.1.6 → complexeva-0.1.8}/PKG-INFO +1 -1
- complexeva-0.1.8/complexeva/measures/dimension_1/__init__.py +0 -0
- complexeva-0.1.8/complexeva/measures/dimension_1/higuchi.py +91 -0
- complexeva-0.1.8/complexeva/measures/dimension_1/lempel_ziv.py +201 -0
- complexeva-0.1.8/complexeva/measures/dimension_2/__init__.py +0 -0
- complexeva-0.1.8/complexeva/measures/dimension_2/cbb_matrix.py +134 -0
- complexeva-0.1.8/complexeva/measures/network_space/__init__.py +0 -0
- complexeva-0.1.8/complexeva/measures/network_space/cbb_networkx.py +288 -0
- complexeva-0.1.8/complexeva/signal/__init__.py +0 -0
- complexeva-0.1.8/complexeva/signal/co_activation.py +100 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva.egg-info/PKG-INFO +1 -1
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva.egg-info/SOURCES.txt +9 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/setup.py +1 -1
- {complexeva-0.1.6 → complexeva-0.1.8}/README.md +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/__init__.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/dummies/__init__.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/dummies/matrix.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/dummies/networks.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/measures/__init__.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/measures/validate.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/utils/__init__.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/utils/convert_image_matrix.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/utils/convert_matrix_network.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva/utils/device.py +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva.egg-info/dependency_links.txt +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/complexeva.egg-info/top_level.txt +0 -0
- {complexeva-0.1.6 → complexeva-0.1.8}/setup.cfg +0 -0
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import numpy
|
|
5
|
+
from scipy.fft import fft, ifft
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
from donware import inspect_package
|
|
8
|
+
from numba import njit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def hfd_matlab_equivalent(data, k_max=16):
|
|
12
|
+
"""
|
|
13
|
+
[translated from MATLAB code]
|
|
14
|
+
|
|
15
|
+
[output]
|
|
16
|
+
- produce values between 1 and 2
|
|
17
|
+
"""
|
|
18
|
+
N = len(data)
|
|
19
|
+
L = numpy.zeros(k_max)
|
|
20
|
+
x = numpy.zeros(k_max)
|
|
21
|
+
y = numpy.zeros(k_max)
|
|
22
|
+
|
|
23
|
+
for k in range(1, k_max + 1):
|
|
24
|
+
Lk = numpy.zeros(k)
|
|
25
|
+
for m in range(1, k + 1):
|
|
26
|
+
norm_factor = (N - 1) / (numpy.round((N - m) / k) * k)
|
|
27
|
+
X = numpy.sum(numpy.abs(numpy.diff(data[m - 1 :: k])))
|
|
28
|
+
Lk[m - 1] = X * norm_factor / k
|
|
29
|
+
|
|
30
|
+
y[k - 1] = numpy.log(numpy.sum(Lk) / k)
|
|
31
|
+
x[k - 1] = numpy.log(1 / k)
|
|
32
|
+
|
|
33
|
+
D = numpy.polyfit(x, y, 1)
|
|
34
|
+
HFD = D[0]
|
|
35
|
+
|
|
36
|
+
return HFD
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@njit
|
|
40
|
+
def compute_L_x(X, k_max=16):
|
|
41
|
+
N = len(X)
|
|
42
|
+
L = numpy.zeros(k_max)
|
|
43
|
+
x = numpy.zeros(k_max)
|
|
44
|
+
for k in range(1, k_max + 1):
|
|
45
|
+
Lk = numpy.zeros(k)
|
|
46
|
+
for m in range(k):
|
|
47
|
+
Lmk = 0.0
|
|
48
|
+
n_max = (N - m) // k
|
|
49
|
+
for i in range(1, n_max):
|
|
50
|
+
Lmk += abs(X[m + i * k] - X[m + (i - 1) * k])
|
|
51
|
+
Lmk *= (N - 1) / (n_max * k)
|
|
52
|
+
Lk[m] = Lmk
|
|
53
|
+
L[k - 1] = numpy.log(Lk.mean())
|
|
54
|
+
x[k - 1] = numpy.log(1.0 / k)
|
|
55
|
+
return x, L
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def hfd_pyeeg(X, k_max=16):
|
|
59
|
+
x, L = compute_L_x(X, k_max)
|
|
60
|
+
A = numpy.column_stack((x, numpy.ones_like(x)))
|
|
61
|
+
beta, _, _, _ = numpy.linalg.lstsq(A, L, rcond=None)
|
|
62
|
+
return beta[0]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _hfd_pyeeg(X, k_max=16):
|
|
66
|
+
"""
|
|
67
|
+
[from PyEEG package]
|
|
68
|
+
Higuchi Fractal Dimension of a time series X. kmax is an HFD parameter
|
|
69
|
+
|
|
70
|
+
[output]
|
|
71
|
+
- for some reasons produce values between 0 and 1
|
|
72
|
+
|
|
73
|
+
[modifications]
|
|
74
|
+
- kmax should be inclusive, Kmax + 1
|
|
75
|
+
"""
|
|
76
|
+
L = []
|
|
77
|
+
x = []
|
|
78
|
+
N = len(X)
|
|
79
|
+
for k in range(1, k_max + 1):
|
|
80
|
+
Lk = []
|
|
81
|
+
for m in range(0, k):
|
|
82
|
+
Lmk = 0
|
|
83
|
+
for i in range(1, int(numpy.floor((N - m) / k))):
|
|
84
|
+
Lmk += abs(X[m + i * k] - X[m + i * k - k])
|
|
85
|
+
Lmk = Lmk * (N - 1) / numpy.floor((N - m) / float(k)) / k
|
|
86
|
+
Lk.append(Lmk)
|
|
87
|
+
L.append(numpy.log(numpy.mean(Lk)))
|
|
88
|
+
x.append([numpy.log(float(1) / k), 1])
|
|
89
|
+
|
|
90
|
+
(p, _, _, _) = numpy.linalg.lstsq(x, L, rcond=None)
|
|
91
|
+
return p[0]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
+
|
|
3
|
+
import numpy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def lempel_ziv(time_series, method="median"):
|
|
7
|
+
"""
|
|
8
|
+
- compared to other implementations, this implementation is makes the most sense
|
|
9
|
+
|
|
10
|
+
Lempel-Ziv complexity as described in Kaspar and Schuster, Phys. Rev. A.
|
|
11
|
+
Counts the number of distinct patterns that need to be copied to reproduce the sequence.
|
|
12
|
+
Returns: int: The number of distinct patterns (complexity measure)
|
|
13
|
+
"""
|
|
14
|
+
sequence = time_series_to_binary(time_series, method=method)
|
|
15
|
+
|
|
16
|
+
history_pos = 0 # Position in history we're comparing against
|
|
17
|
+
current_len = 1 # Length of current pattern we're checking
|
|
18
|
+
max_pattern_len = 1 # Length of longest matching pattern found
|
|
19
|
+
seq_pos = 1 # Current position in sequence
|
|
20
|
+
complexity = 1 # Start with 1 as first character is always a new pattern
|
|
21
|
+
seq_len = len(sequence)
|
|
22
|
+
|
|
23
|
+
while seq_pos + current_len <= seq_len:
|
|
24
|
+
# Check if current pattern matches what's in history
|
|
25
|
+
if sequence[history_pos + current_len - 1] == sequence[seq_pos + current_len - 1]:
|
|
26
|
+
current_len += 1
|
|
27
|
+
else:
|
|
28
|
+
# Update longest matching pattern length if needed
|
|
29
|
+
if current_len > max_pattern_len:
|
|
30
|
+
max_pattern_len = current_len
|
|
31
|
+
|
|
32
|
+
history_pos += 1
|
|
33
|
+
|
|
34
|
+
# If we've checked all history, we found a new pattern
|
|
35
|
+
if history_pos == seq_pos:
|
|
36
|
+
complexity += 1
|
|
37
|
+
seq_pos += max_pattern_len
|
|
38
|
+
|
|
39
|
+
# Reset for next pattern search
|
|
40
|
+
history_pos = 0
|
|
41
|
+
current_len = 1
|
|
42
|
+
max_pattern_len = 1
|
|
43
|
+
else:
|
|
44
|
+
# Try next position in history
|
|
45
|
+
current_len = 1
|
|
46
|
+
|
|
47
|
+
return complexity
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def time_series_to_binary(vector, method="median"):
|
|
51
|
+
"""
|
|
52
|
+
- convert a time series to a binary sequence based on a threshold.
|
|
53
|
+
- returns: str: A binary sequence as a string of '0's and '1's.
|
|
54
|
+
- https://doi.org/10.1101/2021.09.23.461002 this paper used median, making median the default method.
|
|
55
|
+
- diff also makes somewhat sense
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
vector = numpy.array(vector)
|
|
59
|
+
|
|
60
|
+
if method == "mean":
|
|
61
|
+
threshold = numpy.mean(vector)
|
|
62
|
+
binary_seq = "".join(["1" if x > threshold else "0" for x in vector])
|
|
63
|
+
elif method == "median":
|
|
64
|
+
threshold = numpy.median(vector)
|
|
65
|
+
binary_seq = "".join(["1" if x > threshold else "0" for x in vector])
|
|
66
|
+
elif method == "diff":
|
|
67
|
+
diffs = numpy.diff(
|
|
68
|
+
vector
|
|
69
|
+
) # this measures whether the value is increasing or decreasing as compared to the previous value
|
|
70
|
+
binary_seq = "".join(["1" if x > 0 else "0" for x in diffs])
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError("Method must be 'mean', 'median', or 'diff'.")
|
|
73
|
+
|
|
74
|
+
return binary_seq
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def lempel_ziv_v1(time_series: numpy.ndarray) -> int:
|
|
78
|
+
r"""Manual implementation of the Lempel-Ziv complexity.
|
|
79
|
+
|
|
80
|
+
It is defined as the number of different substrings encountered as the stream is viewed from begining to the end.
|
|
81
|
+
As an example:
|
|
82
|
+
|
|
83
|
+
>>> s = '1001111011000010'
|
|
84
|
+
>>> lempel_ziv_complexity(s) # 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010
|
|
85
|
+
8
|
|
86
|
+
|
|
87
|
+
Marking in the different substrings the sequence complexity :math:`\mathrm{Lempel-Ziv}(s) = 8`: :math:`s = 1 / 0 / 01 / 11 / 10 / 110 / 00 / 010`.
|
|
88
|
+
|
|
89
|
+
- See the page https://en.wikipedia.org/wiki/Lempel-Ziv_complexity for more details.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
Other examples:
|
|
93
|
+
|
|
94
|
+
>>> lempel_ziv_complexity('1010101010101010') # 1, 0, 10, 101, 01, 010, 1010
|
|
95
|
+
7
|
|
96
|
+
>>> lempel_ziv_complexity('1001111011000010000010') # 1, 0, 01, 11, 10, 110, 00, 010, 000
|
|
97
|
+
9
|
|
98
|
+
>>> lempel_ziv_complexity('100111101100001000001010') # 1, 0, 01, 11, 10, 110, 00, 010, 000, 0101
|
|
99
|
+
10
|
|
100
|
+
|
|
101
|
+
- Note: it is faster to give the sequence as a string of characters, like `'10001001'`, instead of a list or a numpy array.
|
|
102
|
+
- Note: see this notebook for more details, comparison, benchmarks and experiments: https://Nbviewer.Jupyter.org/github/Naereen/Lempel-Ziv_Complexity/Short_study_of_the_Lempel-Ziv_complexity.ipynb
|
|
103
|
+
- Note: there is also a Cython-powered version, for speedup, see :download:`lempel_ziv_complexity_cython.pyx`.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
sequence = time_series_to_binary(time_series, method="median")
|
|
107
|
+
|
|
108
|
+
sub_strings = set()
|
|
109
|
+
|
|
110
|
+
ind = 0
|
|
111
|
+
inc = 1
|
|
112
|
+
while True:
|
|
113
|
+
if ind + inc > len(sequence):
|
|
114
|
+
break
|
|
115
|
+
sub_str = sequence[ind : ind + inc]
|
|
116
|
+
if sub_str in sub_strings:
|
|
117
|
+
inc += 1
|
|
118
|
+
else:
|
|
119
|
+
sub_strings.add(sub_str)
|
|
120
|
+
ind += inc
|
|
121
|
+
inc = 1
|
|
122
|
+
return len(sub_strings)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def lempel_ziv_v2(time_series):
|
|
126
|
+
"""
|
|
127
|
+
Compute the Lempel-Ziv complexity of a binary sequence.
|
|
128
|
+
|
|
129
|
+
Parameters:
|
|
130
|
+
sequence (str): The binary sequence as a string of '0' and '1'.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
int: The Lempel-Ziv complexity.
|
|
134
|
+
"""
|
|
135
|
+
sequence = time_series_to_binary(time_series, method="median")
|
|
136
|
+
|
|
137
|
+
sub_strings = set()
|
|
138
|
+
n = len(sequence)
|
|
139
|
+
ind = 0
|
|
140
|
+
count = 0
|
|
141
|
+
|
|
142
|
+
while ind < n:
|
|
143
|
+
inc = 1
|
|
144
|
+
# Find the shortest substring not already in sub_strings
|
|
145
|
+
while sequence[ind : ind + inc] in sub_strings and ind + inc <= n:
|
|
146
|
+
inc += 1
|
|
147
|
+
|
|
148
|
+
# Add the new substring to the set
|
|
149
|
+
sub_strings.add(sequence[ind : ind + inc])
|
|
150
|
+
count += 1
|
|
151
|
+
ind += inc
|
|
152
|
+
|
|
153
|
+
return count
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def lempel_ziv_v4(time_series):
|
|
157
|
+
"""
|
|
158
|
+
Computes the Lempel-Ziv complexity of the input sequence s.
|
|
159
|
+
|
|
160
|
+
Parameters:
|
|
161
|
+
s (str): The input sequence (e.g., a binary string).
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
int: The Lempel-Ziv complexity of the sequence.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
sequence = time_series_to_binary(time_series, method="median")
|
|
168
|
+
n = len(sequence)
|
|
169
|
+
i = 0
|
|
170
|
+
c = 1 # Complexity starts at 1 since the first character is always new
|
|
171
|
+
k = 1
|
|
172
|
+
l = 1
|
|
173
|
+
|
|
174
|
+
while l + k <= n:
|
|
175
|
+
# Check if the substring s[l:l+k] has occurred in s[0:l]
|
|
176
|
+
if sequence[l : l + k] in sequence[0:l]:
|
|
177
|
+
k += 1
|
|
178
|
+
if l + k - 1 == n:
|
|
179
|
+
c += 1
|
|
180
|
+
break
|
|
181
|
+
else:
|
|
182
|
+
c += 1
|
|
183
|
+
l = l + k
|
|
184
|
+
k = 1
|
|
185
|
+
|
|
186
|
+
return c
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if __name__ == "__main__":
|
|
190
|
+
sequence = "1001111011000010"
|
|
191
|
+
print(lempel_ziv_v1(sequence))
|
|
192
|
+
print(lempel_ziv_v2(sequence))
|
|
193
|
+
print(lempel_ziv(sequence))
|
|
194
|
+
print(lempel_ziv_v4(sequence))
|
|
195
|
+
|
|
196
|
+
signal = numpy.random.rand(1000)
|
|
197
|
+
binary_sequence = time_series_to_binary(signal, method="median")
|
|
198
|
+
print(lempel_ziv_v1(binary_sequence))
|
|
199
|
+
print(lempel_ziv_v2(binary_sequence))
|
|
200
|
+
print(lempel_ziv(binary_sequence))
|
|
201
|
+
print(lempel_ziv_v4(binary_sequence))
|
|
File without changes
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from matplotlib import animation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FractalHandlerMatrix:
|
|
7
|
+
"""
|
|
8
|
+
Handles the fractal dimension calculation of a binary matrix using an improved Compact Box-Burning algorithm.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, matrix: np.ndarray):
|
|
12
|
+
self.matrix = matrix
|
|
13
|
+
|
|
14
|
+
def compute_fractal_dimension(self, save_plot_as=None, save_animated_as=None):
|
|
15
|
+
"""
|
|
16
|
+
Calculate the fractal dimension of the matrix using the improved Compact Box-Burning algorithm.
|
|
17
|
+
|
|
18
|
+
Parameters:
|
|
19
|
+
save_plot_as (str, optional): Path to save the static plot showing fractal dimension calculation
|
|
20
|
+
save_animated_as (str, optional): Path to save the animated GIF showing box counting process
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
float: The fractal dimension of the matrix.
|
|
24
|
+
"""
|
|
25
|
+
assert np.all((self.matrix == 0) | (self.matrix == 1)), "> warning: Matrix should be binary (0s and 1s only)"
|
|
26
|
+
binary_matrix = self.matrix.astype(np.uint8)
|
|
27
|
+
min_dim = min(binary_matrix.shape)
|
|
28
|
+
max_scale = 2 ** int(np.floor(np.log2(min_dim)))
|
|
29
|
+
|
|
30
|
+
num_scales = int(np.floor(np.log2(max_scale))) * 10
|
|
31
|
+
scales = np.unique(np.floor(np.logspace(0, np.log2(max_scale), num=num_scales, base=2))).astype(int)
|
|
32
|
+
scales = scales[scales >= 1]
|
|
33
|
+
|
|
34
|
+
box_counts = []
|
|
35
|
+
padded_matrices = []
|
|
36
|
+
|
|
37
|
+
# Animation setup if needed
|
|
38
|
+
if save_animated_as:
|
|
39
|
+
fig, ax = plt.subplots(figsize=(8, 8))
|
|
40
|
+
|
|
41
|
+
for scale in scales:
|
|
42
|
+
padded_size = (
|
|
43
|
+
int(np.ceil(binary_matrix.shape[0] / scale)) * scale,
|
|
44
|
+
int(np.ceil(binary_matrix.shape[1] / scale)) * scale,
|
|
45
|
+
)
|
|
46
|
+
padded_matrix = np.zeros(padded_size, dtype=np.uint8)
|
|
47
|
+
padded_matrix[: binary_matrix.shape[0], : binary_matrix.shape[1]] = binary_matrix
|
|
48
|
+
|
|
49
|
+
if save_animated_as:
|
|
50
|
+
padded_matrices.append((padded_matrix, scale))
|
|
51
|
+
|
|
52
|
+
reshaped = padded_matrix.reshape(padded_size[0] // scale, scale, padded_size[1] // scale, scale)
|
|
53
|
+
summed = reshaped.sum(axis=(1, 3))
|
|
54
|
+
occupied_boxes = np.count_nonzero(summed)
|
|
55
|
+
box_counts.append(occupied_boxes)
|
|
56
|
+
|
|
57
|
+
# calculate fractal dimension
|
|
58
|
+
scales = np.array(scales)
|
|
59
|
+
box_counts = np.array(box_counts)
|
|
60
|
+
nonzero = box_counts > 0
|
|
61
|
+
scales = scales[nonzero]
|
|
62
|
+
box_counts = box_counts[nonzero]
|
|
63
|
+
|
|
64
|
+
log_scales = np.log(1 / scales)
|
|
65
|
+
log_box_counts = np.log(box_counts)
|
|
66
|
+
|
|
67
|
+
coeffs = np.polyfit(log_scales, log_box_counts, 1)
|
|
68
|
+
fractal_dimension = coeffs[0]
|
|
69
|
+
|
|
70
|
+
# save static plot if requested
|
|
71
|
+
if save_plot_as:
|
|
72
|
+
save_plot_as = save_plot_as if save_plot_as.endswith(".png") else save_plot_as + ".png"
|
|
73
|
+
plt.figure(figsize=(8, 6))
|
|
74
|
+
plt.plot(log_scales, log_box_counts, "o-", label="Data")
|
|
75
|
+
plt.plot(
|
|
76
|
+
log_scales,
|
|
77
|
+
np.polyval(coeffs, log_scales),
|
|
78
|
+
"r--",
|
|
79
|
+
label=f"Fit (D = {fractal_dimension:.4f})",
|
|
80
|
+
)
|
|
81
|
+
plt.xlabel("log(1/ε)")
|
|
82
|
+
plt.ylabel("log(N(ε))")
|
|
83
|
+
plt.title("Fractal Dimension via Compact Box-Burning")
|
|
84
|
+
plt.legend()
|
|
85
|
+
plt.grid(True)
|
|
86
|
+
plt.tight_layout()
|
|
87
|
+
plt.savefig(save_plot_as, dpi=420, bbox_inches="tight")
|
|
88
|
+
plt.close()
|
|
89
|
+
|
|
90
|
+
# Create and save animation if requested
|
|
91
|
+
if save_animated_as:
|
|
92
|
+
|
|
93
|
+
def init():
|
|
94
|
+
ax.imshow(binary_matrix, cmap="binary", interpolation="nearest")
|
|
95
|
+
ax.axis("off")
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
def update(frame):
|
|
99
|
+
padded_matrix, scale = padded_matrices[frame]
|
|
100
|
+
ax.clear()
|
|
101
|
+
ax.imshow(padded_matrix, cmap="binary", interpolation="nearest")
|
|
102
|
+
ax.set_title(f"Scale: {scale} pixels, Boxes: {box_counts[frame]}")
|
|
103
|
+
ax.axis("off")
|
|
104
|
+
|
|
105
|
+
# Overlay red boxes on occupied regions
|
|
106
|
+
for y in range(0, padded_matrix.shape[0], scale):
|
|
107
|
+
for x in range(0, padded_matrix.shape[1], scale):
|
|
108
|
+
if np.any(padded_matrix[y : y + scale, x : x + scale]):
|
|
109
|
+
rect = plt.Rectangle(
|
|
110
|
+
(x - 0.5, y - 0.5),
|
|
111
|
+
scale,
|
|
112
|
+
scale,
|
|
113
|
+
edgecolor="red",
|
|
114
|
+
facecolor="none",
|
|
115
|
+
lw=1,
|
|
116
|
+
)
|
|
117
|
+
ax.add_patch(rect)
|
|
118
|
+
return []
|
|
119
|
+
|
|
120
|
+
ani = animation.FuncAnimation(
|
|
121
|
+
fig,
|
|
122
|
+
update,
|
|
123
|
+
frames=len(scales),
|
|
124
|
+
init_func=init,
|
|
125
|
+
blit=False,
|
|
126
|
+
repeat=False,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if not save_animated_as.endswith((".png", ".gif")):
|
|
130
|
+
save_animated_as += ".gif"
|
|
131
|
+
ani.save(save_animated_as.replace(".png", ".gif"), writer="pillow", fps=2)
|
|
132
|
+
plt.close(fig)
|
|
133
|
+
|
|
134
|
+
return fractal_dimension
|
|
File without changes
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/bin/python
|
|
2
|
+
import random
|
|
3
|
+
from PIL import Image
|
|
4
|
+
import numpy as np
|
|
5
|
+
import networkx as nx
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
from scipy import stats
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from networkx.exception import NetworkXError
|
|
10
|
+
import matplotlib.animation as animation
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
reference:
|
|
15
|
+
- https://github.com/PeterTKovacs/boxes/blob/main/boxes/cbb.py
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FractalHandlerNetworkX:
|
|
20
|
+
"""
|
|
21
|
+
Handles fractal analysis of NetworkX graphs using box-covering method.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, network: nx.Graph):
|
|
25
|
+
self.network = network
|
|
26
|
+
|
|
27
|
+
def auto_pick_box_sizes(self):
|
|
28
|
+
diameter = nx.diameter(self.network) if nx.is_connected(self.network) else 1
|
|
29
|
+
min_size = max(1, diameter // 21)
|
|
30
|
+
max_size = diameter
|
|
31
|
+
return np.logspace(np.log10(min_size), np.log10(max_size), num=12, dtype=int)
|
|
32
|
+
|
|
33
|
+
def compute_fractal_dimensions(self, box_sizes="auto", iterations=10, save_plot_as=None, save_animated_as=None):
|
|
34
|
+
box_sizes = self.auto_pick_box_sizes() if box_sizes == "auto" else box_sizes
|
|
35
|
+
network_image, box_counts = self.plot_network(), []
|
|
36
|
+
|
|
37
|
+
# If animation is requested, set up storage for animation frames
|
|
38
|
+
if save_animated_as:
|
|
39
|
+
animation_data = []
|
|
40
|
+
pos = nx.spring_layout(self.network, dim=3) # 3D layout
|
|
41
|
+
|
|
42
|
+
# Create a color mapping based on z-coordinates
|
|
43
|
+
z_coords = np.array([pos[node][2] for node in self.network.nodes()])
|
|
44
|
+
z_min, z_max = z_coords.min(), z_coords.max()
|
|
45
|
+
node_colors = {}
|
|
46
|
+
for node in self.network.nodes():
|
|
47
|
+
z = pos[node][2]
|
|
48
|
+
# Normalize z to [0,1] range for color mapping
|
|
49
|
+
color_val = (z - z_min) / (z_max - z_min) if z_max != z_min else 0
|
|
50
|
+
node_colors[node] = plt.cm.rainbow(color_val)
|
|
51
|
+
|
|
52
|
+
for max_distance in tqdm(box_sizes, desc="Box sizes"):
|
|
53
|
+
num_boxes_list = []
|
|
54
|
+
boxes_for_animation = []
|
|
55
|
+
for _ in tqdm(range(iterations), desc=f"Networks for size {max_distance}", leave=False):
|
|
56
|
+
boxes = self.random_box_covering(max_distance)
|
|
57
|
+
num_boxes_list.append(len(boxes))
|
|
58
|
+
if save_animated_as:
|
|
59
|
+
boxes_for_animation.append(boxes)
|
|
60
|
+
avg_num_boxes = sum(num_boxes_list) / iterations
|
|
61
|
+
box_counts.append(avg_num_boxes)
|
|
62
|
+
|
|
63
|
+
if save_animated_as:
|
|
64
|
+
# For animation, store the boxes from the first iteration
|
|
65
|
+
animation_data.append(
|
|
66
|
+
{
|
|
67
|
+
"boxes": boxes_for_animation[0],
|
|
68
|
+
"max_distance": max_distance,
|
|
69
|
+
"num_boxes": num_boxes_list[0],
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
log_box_sizes, log_counts = np.log(box_sizes), np.log(box_counts)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
slope, intercept, r_value, p_value, std_err = stats.linregress(log_box_sizes, log_counts)
|
|
77
|
+
except ValueError:
|
|
78
|
+
slope, intercept, r_value = 0, 0, 0
|
|
79
|
+
fractal_dimension = -slope
|
|
80
|
+
|
|
81
|
+
if save_plot_as:
|
|
82
|
+
save_plot_as = save_plot_as if save_plot_as.endswith(".png") else f"{save_plot_as}.png"
|
|
83
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
84
|
+
ax.scatter(log_box_sizes, log_counts, color="blue", label="Data")
|
|
85
|
+
ax.plot(
|
|
86
|
+
log_box_sizes,
|
|
87
|
+
slope * log_box_sizes + intercept,
|
|
88
|
+
color="red",
|
|
89
|
+
label=f"Fit (D={fractal_dimension:.2f})",
|
|
90
|
+
linestyle="-",
|
|
91
|
+
)
|
|
92
|
+
ax.set_xlabel("log(Box Size)", fontsize=21)
|
|
93
|
+
ax.set_ylabel("log(Number of Boxes)", fontsize=21)
|
|
94
|
+
ax.set_title("Fractal Dimension Regression", fontsize=21)
|
|
95
|
+
ax.legend(fontsize=21)
|
|
96
|
+
ax.grid(True)
|
|
97
|
+
ax.tick_params(axis="both", which="major", labelsize=21)
|
|
98
|
+
fig.canvas.draw()
|
|
99
|
+
plot_image = np.array(fig.canvas.renderer.buffer_rgba())
|
|
100
|
+
network_height, network_width = network_image.shape[:2]
|
|
101
|
+
plot_height, plot_width = plot_image.shape[:2]
|
|
102
|
+
aspect_ratio = network_width / network_height
|
|
103
|
+
new_network_height = plot_height
|
|
104
|
+
new_network_width = int(new_network_height * aspect_ratio)
|
|
105
|
+
resized_network_image = np.array(Image.fromarray(network_image).resize((new_network_width, new_network_height)))
|
|
106
|
+
resized_network_image = resized_network_image[:, :, :3]
|
|
107
|
+
plot_image = plot_image[:, :, :3]
|
|
108
|
+
combined_image = np.hstack((resized_network_image, plot_image))
|
|
109
|
+
plt.figure(
|
|
110
|
+
figsize=(
|
|
111
|
+
combined_image.shape[1] / 100,
|
|
112
|
+
combined_image.shape[0] / 100,
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
plt.imshow(combined_image)
|
|
116
|
+
plt.axis("off")
|
|
117
|
+
plt.tight_layout()
|
|
118
|
+
plt.savefig(save_plot_as, dpi=420, bbox_inches="tight")
|
|
119
|
+
plt.close()
|
|
120
|
+
|
|
121
|
+
# Create and save animation if requested
|
|
122
|
+
if save_animated_as:
|
|
123
|
+
fig = plt.figure(figsize=(15, 8)) # Wider figure to accommodate legend
|
|
124
|
+
gs = plt.GridSpec(1, 2, width_ratios=[2, 1]) # 2:1 ratio for plot and legend
|
|
125
|
+
ax = fig.add_subplot(gs[0], projection="3d")
|
|
126
|
+
legend_ax = fig.add_subplot(gs[1]) # Create legend subplot once
|
|
127
|
+
legend_ax.axis("off")
|
|
128
|
+
legend_ax.spines["top"].set_visible(False)
|
|
129
|
+
|
|
130
|
+
def update(frame):
|
|
131
|
+
ax.clear()
|
|
132
|
+
legend_ax.clear() # Clear previous legend
|
|
133
|
+
|
|
134
|
+
data = animation_data[frame]
|
|
135
|
+
boxes = data["boxes"]
|
|
136
|
+
max_distance = data["max_distance"]
|
|
137
|
+
num_boxes = data["num_boxes"]
|
|
138
|
+
|
|
139
|
+
# Draw network and boxes in 3D subplot
|
|
140
|
+
edge_list = list(self.network.edges())
|
|
141
|
+
|
|
142
|
+
# Assign box colors based on average z-coordinate of nodes in each box
|
|
143
|
+
box_colors = []
|
|
144
|
+
for box in boxes:
|
|
145
|
+
box_nodes = list(box)
|
|
146
|
+
box_colors.append(np.mean([node_colors[node] for node in box_nodes], axis=0))
|
|
147
|
+
|
|
148
|
+
# Create a mapping of nodes to their box colors
|
|
149
|
+
node_to_color = {}
|
|
150
|
+
for box, color in zip(boxes, box_colors):
|
|
151
|
+
for node in box:
|
|
152
|
+
node_to_color[node] = color
|
|
153
|
+
|
|
154
|
+
# Draw edges with box colors
|
|
155
|
+
for edge in edge_list:
|
|
156
|
+
start = pos[edge[0]]
|
|
157
|
+
end = pos[edge[1]]
|
|
158
|
+
# Use color of both nodes for edge
|
|
159
|
+
start_color = node_to_color.get(edge[0], "gray")
|
|
160
|
+
end_color = node_to_color.get(edge[1], "gray")
|
|
161
|
+
edge_color = start_color if np.array_equal(start_color, end_color) else "gray"
|
|
162
|
+
|
|
163
|
+
mid_point = (start + end) / 2
|
|
164
|
+
offset = np.random.normal(0, 0.05, 3)
|
|
165
|
+
control_point = mid_point + offset
|
|
166
|
+
t = np.linspace(0, 1, 20)
|
|
167
|
+
curve = np.array([(1 - t) ** 2 * start + 2 * (1 - t) * t * control_point + t**2 * end for t in t])
|
|
168
|
+
ax.plot3D(curve[:, 0], curve[:, 1], curve[:, 2], c=edge_color, alpha=0.7, linewidth=1.5)
|
|
169
|
+
|
|
170
|
+
# Draw boxes and create legend elements
|
|
171
|
+
legend_elements = []
|
|
172
|
+
for box_idx, (box, color) in enumerate(zip(boxes, box_colors)):
|
|
173
|
+
box_nodes = list(box)
|
|
174
|
+
box_pos = np.array([pos[node] for node in box_nodes])
|
|
175
|
+
scatter = ax.scatter(
|
|
176
|
+
box_pos[:, 0], box_pos[:, 1], box_pos[:, 2], c=[color], s=150, alpha=0.8, edgecolor="white"
|
|
177
|
+
)
|
|
178
|
+
legend_elements.append((scatter, f"Box {box_idx+1}"))
|
|
179
|
+
|
|
180
|
+
# Set 3D plot properties
|
|
181
|
+
ax.set_title(f"Step {frame+1}/{len(animation_data)}\nBox Size = {max_distance}\nBoxes Required: {num_boxes}")
|
|
182
|
+
ax.set_xlabel("X")
|
|
183
|
+
ax.set_ylabel("Y")
|
|
184
|
+
ax.set_zlabel("Z")
|
|
185
|
+
|
|
186
|
+
# Calculate optimal number of columns based on number of boxes
|
|
187
|
+
n_boxes = len(boxes)
|
|
188
|
+
n_cols = min(4, n_boxes) # Max 4 columns for more spacing
|
|
189
|
+
n_rows = (n_boxes + n_cols - 1) // n_cols # Ceiling division
|
|
190
|
+
|
|
191
|
+
# Create legend with more spacing
|
|
192
|
+
legend = legend_ax.legend(
|
|
193
|
+
*zip(*legend_elements),
|
|
194
|
+
loc="center",
|
|
195
|
+
ncol=n_cols,
|
|
196
|
+
bbox_to_anchor=(0.5, 0.5),
|
|
197
|
+
borderaxespad=1,
|
|
198
|
+
labelspacing=1.5, # Increase vertical spacing between legend entries
|
|
199
|
+
handletextpad=1.2, # Increase spacing between marker and text
|
|
200
|
+
title="Box Groups",
|
|
201
|
+
frameon=True, # Add frame around legend
|
|
202
|
+
fancybox=True, # Round corners
|
|
203
|
+
shadow=True, # Add shadow
|
|
204
|
+
)
|
|
205
|
+
legend.get_title().set_fontsize(14)
|
|
206
|
+
for text in legend.get_texts():
|
|
207
|
+
text.set_fontsize(12)
|
|
208
|
+
|
|
209
|
+
ax.set_box_aspect([1, 1, 1])
|
|
210
|
+
ax.view_init(elev=20, azim=45)
|
|
211
|
+
ax.dist = 8
|
|
212
|
+
|
|
213
|
+
ani = animation.FuncAnimation(fig, update, frames=len(animation_data), interval=2000, repeat=True)
|
|
214
|
+
|
|
215
|
+
save_animated_as = save_animated_as if save_animated_as.endswith((".png", ".gif")) else save_animated_as + ".gif"
|
|
216
|
+
ani.save(save_animated_as.replace(".png", ".gif"), writer="pillow", fps=0.5)
|
|
217
|
+
plt.close(fig)
|
|
218
|
+
|
|
219
|
+
return fractal_dimension, r_value**2, log_box_sizes, log_counts
|
|
220
|
+
|
|
221
|
+
# ---- functional helpers ----
|
|
222
|
+
def random_box_covering(self, max_distance: int):
|
|
223
|
+
"""
|
|
224
|
+
[NOTE] This implementation handles disconnected graphs by processing each connected component separately.
|
|
225
|
+
|
|
226
|
+
This function implements the random box covering algorithm to estimate
|
|
227
|
+
the boxes needed to cover a network at a given maximum distance.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
max_distance (int): The maximum distance allowed within each box, in number of hops.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List[Set]: A list of sets, each containing the nodes in a box.
|
|
234
|
+
"""
|
|
235
|
+
boxes = []
|
|
236
|
+
for component in nx.connected_components(self.network):
|
|
237
|
+
subgraph = self.network.subgraph(component)
|
|
238
|
+
uncovered_nodes = set(subgraph.nodes())
|
|
239
|
+
while uncovered_nodes:
|
|
240
|
+
p = random.choice(list(uncovered_nodes))
|
|
241
|
+
distances = nx.single_source_shortest_path_length(subgraph, p)
|
|
242
|
+
box_nodes = set(node for node, dist in distances.items() if dist <= max_distance).intersection(uncovered_nodes)
|
|
243
|
+
boxes.append(box_nodes)
|
|
244
|
+
uncovered_nodes -= box_nodes
|
|
245
|
+
return boxes
|
|
246
|
+
|
|
247
|
+
def plot_network(self):
|
|
248
|
+
pos = nx.get_node_attributes(self.network, "pos")
|
|
249
|
+
fig = plt.figure(figsize=(10, 8))
|
|
250
|
+
ax = fig.add_subplot(111, projection="3d")
|
|
251
|
+
|
|
252
|
+
if pos:
|
|
253
|
+
# Use provided positions if they exist
|
|
254
|
+
pos_3d = {node: (x, y, 0) for node, (x, y) in pos.items()} # Convert 2D to 3D
|
|
255
|
+
else:
|
|
256
|
+
# Generate 3D spring layout
|
|
257
|
+
pos_3d = nx.spring_layout(self.network, dim=3)
|
|
258
|
+
|
|
259
|
+
# Draw edges with curves
|
|
260
|
+
for edge in self.network.edges():
|
|
261
|
+
start = np.array(pos_3d[edge[0]])
|
|
262
|
+
end = np.array(pos_3d[edge[1]])
|
|
263
|
+
ax.plot([start[0], end[0]], [start[1], end[1]], [start[2], end[2]], "gray", alpha=0.5)
|
|
264
|
+
|
|
265
|
+
# Draw nodes
|
|
266
|
+
node_pos = np.array([pos_3d[node] for node in self.network.nodes()])
|
|
267
|
+
ax.scatter(node_pos[:, 0], node_pos[:, 1], node_pos[:, 2], c="lightblue", s=30)
|
|
268
|
+
|
|
269
|
+
ax.set_title("3D Network Visualization")
|
|
270
|
+
ax.set_xlabel("X")
|
|
271
|
+
ax.set_ylabel("Y")
|
|
272
|
+
ax.set_zlabel("Z")
|
|
273
|
+
|
|
274
|
+
plt.tight_layout()
|
|
275
|
+
fig.canvas.draw()
|
|
276
|
+
image = np.array(fig.canvas.renderer.buffer_rgba())
|
|
277
|
+
plt.close(fig)
|
|
278
|
+
return image
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if __name__ == "__main__":
|
|
282
|
+
from src.dummies.networks import WebFractalNetwork, SierpinskiFractal
|
|
283
|
+
|
|
284
|
+
network = SierpinskiFractal(recursion_depth=6)
|
|
285
|
+
network = WebFractalNetwork(complexity=4)
|
|
286
|
+
|
|
287
|
+
handler = FractalHandlerNetworkX(network.network)
|
|
288
|
+
handler.compute_fractal_dimensions(box_sizes=np.linspace(1, 10, 20), iterations=10)
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
- now we have two 1d series as signals make dummy data using numpy
|
|
5
|
+
- use either a sliding window or regression method to produce a new signal that represents the co-activation between the two signals
|
|
6
|
+
- in the sliding_window method, the window_size parameter is used to determine the size of the window used to calculate the co-activation
|
|
7
|
+
- it moves one step at a time and calculates the correlation between the two signals in the window
|
|
8
|
+
- in the regression method, the two signals are used as independent and dependent variables in a linear regression model
|
|
9
|
+
- the resulting signal is the predicted dependent variable
|
|
10
|
+
- if we use the sliding_window method, the window_size parameter must be an integer greater than 0
|
|
11
|
+
- if we use the regression method, we must plot the two signal and the resulting signal
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from sklearn.linear_model import LinearRegression
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_co_activation(
|
|
20
|
+
signal_1: np.ndarray, signal_2: np.ndarray, method: str, window_size: int = None, save_plot_as=None
|
|
21
|
+
) -> np.ndarray:
|
|
22
|
+
"""
|
|
23
|
+
Get a measure of co-activation between two 1D signals.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
signal_1: First 1D time series signal
|
|
27
|
+
signal_2: Second 1D time series signal
|
|
28
|
+
method: Either 'sliding_window' or 'regression'
|
|
29
|
+
window_size: Size of the window for 'sliding_window' method (must be an integer greater than 0)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
np.ndarray: A new signal representing the co-activation between the two signals
|
|
33
|
+
"""
|
|
34
|
+
match method:
|
|
35
|
+
case "sliding_window":
|
|
36
|
+
if window_size is None or window_size <= 0:
|
|
37
|
+
raise ValueError("window_size must be an integer greater than 0 for sliding_window method")
|
|
38
|
+
correlations = []
|
|
39
|
+
num_windows = len(signal_1) - window_size + 1
|
|
40
|
+
for i in range(num_windows):
|
|
41
|
+
window_signal_1 = signal_1[i : i + window_size]
|
|
42
|
+
window_signal_2 = signal_2[i : i + window_size]
|
|
43
|
+
corr = np.corrcoef(window_signal_1, window_signal_2)[0, 1]
|
|
44
|
+
correlations.append(corr)
|
|
45
|
+
|
|
46
|
+
# Plot signals and correlations
|
|
47
|
+
correlations = np.array(correlations)
|
|
48
|
+
if save_plot_as:
|
|
49
|
+
plt.figure(figsize=(10, 5))
|
|
50
|
+
plt.plot(signal_1, label="Signal 1", alpha=0.7)
|
|
51
|
+
plt.plot(signal_2, label="Signal 2", alpha=0.7)
|
|
52
|
+
plt.plot(
|
|
53
|
+
np.arange(window_size // 2, len(correlations) + window_size // 2),
|
|
54
|
+
correlations,
|
|
55
|
+
label="Sliding Window Correlation",
|
|
56
|
+
linestyle="--",
|
|
57
|
+
)
|
|
58
|
+
plt.legend()
|
|
59
|
+
plt.title(f"Sliding Window Method Co-Activation (window size={window_size})")
|
|
60
|
+
plt.savefig(save_plot_as, dpi=320)
|
|
61
|
+
plt.close()
|
|
62
|
+
|
|
63
|
+
return correlations
|
|
64
|
+
|
|
65
|
+
case "regression":
|
|
66
|
+
# Reshape signals for regression
|
|
67
|
+
X = signal_1.reshape(-1, 1)
|
|
68
|
+
y = signal_2
|
|
69
|
+
|
|
70
|
+
# Fit linear regression
|
|
71
|
+
model = LinearRegression()
|
|
72
|
+
model.fit(X, y)
|
|
73
|
+
|
|
74
|
+
# Predict y
|
|
75
|
+
y_pred = model.predict(X)
|
|
76
|
+
|
|
77
|
+
# Plot signals
|
|
78
|
+
if save_plot_as:
|
|
79
|
+
plt.figure(figsize=(10, 5))
|
|
80
|
+
plt.plot(signal_1, label="Signal 1 (IV)")
|
|
81
|
+
plt.plot(signal_2, label="Signal 2 (DV)")
|
|
82
|
+
plt.plot(y_pred, label="Predicted DV", linestyle="--")
|
|
83
|
+
plt.legend()
|
|
84
|
+
plt.title("Regression Method Co-Activation")
|
|
85
|
+
plt.savefig(save_plot_as, dpi=320)
|
|
86
|
+
plt.close()
|
|
87
|
+
|
|
88
|
+
return y_pred
|
|
89
|
+
|
|
90
|
+
case _:
|
|
91
|
+
raise ValueError("method must be 'sliding_window' or 'regression'")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
t = np.linspace(0, 10, 1000)
|
|
96
|
+
signal1 = np.sin(2 * np.pi * 0.5 * t) + 0.2 * np.random.randn(1000)
|
|
97
|
+
signal2 = 0.8 * np.sin(2 * np.pi * 0.5 * t + np.pi / 4) + 0.2 * np.random.randn(1000)
|
|
98
|
+
|
|
99
|
+
get_co_activation(signal1, signal2, method="sliding_window", window_size=32, save_plot_as="coactivation_sliding_window.png")
|
|
100
|
+
get_co_activation(signal1, signal2, method="regression", save_plot_as="coactivation_regression.png")
|
|
@@ -10,6 +10,15 @@ complexeva/dummies/matrix.py
|
|
|
10
10
|
complexeva/dummies/networks.py
|
|
11
11
|
complexeva/measures/__init__.py
|
|
12
12
|
complexeva/measures/validate.py
|
|
13
|
+
complexeva/measures/dimension_1/__init__.py
|
|
14
|
+
complexeva/measures/dimension_1/higuchi.py
|
|
15
|
+
complexeva/measures/dimension_1/lempel_ziv.py
|
|
16
|
+
complexeva/measures/dimension_2/__init__.py
|
|
17
|
+
complexeva/measures/dimension_2/cbb_matrix.py
|
|
18
|
+
complexeva/measures/network_space/__init__.py
|
|
19
|
+
complexeva/measures/network_space/cbb_networkx.py
|
|
20
|
+
complexeva/signal/__init__.py
|
|
21
|
+
complexeva/signal/co_activation.py
|
|
13
22
|
complexeva/utils/__init__.py
|
|
14
23
|
complexeva/utils/convert_image_matrix.py
|
|
15
24
|
complexeva/utils/convert_matrix_network.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|