complexeva 0.1.5__tar.gz → 0.1.6__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.
Files changed (23) hide show
  1. {complexeva-0.1.5 → complexeva-0.1.6}/PKG-INFO +1 -1
  2. complexeva-0.1.6/complexeva/measures/__init__.py +6 -0
  3. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/measures/validate.py +13 -7
  4. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva.egg-info/PKG-INFO +1 -1
  5. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva.egg-info/SOURCES.txt +0 -4
  6. {complexeva-0.1.5 → complexeva-0.1.6}/setup.py +1 -1
  7. complexeva-0.1.5/complexeva/measures/__init__.py +0 -5
  8. complexeva-0.1.5/complexeva/measures/cbb_matrix.py +0 -134
  9. complexeva-0.1.5/complexeva/measures/cbb_networkx.py +0 -288
  10. complexeva-0.1.5/complexeva/measures/co_activation.py +0 -100
  11. complexeva-0.1.5/complexeva/measures/higuchi.py +0 -91
  12. {complexeva-0.1.5 → complexeva-0.1.6}/README.md +0 -0
  13. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/__init__.py +0 -0
  14. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/dummies/__init__.py +0 -0
  15. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/dummies/matrix.py +0 -0
  16. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/dummies/networks.py +0 -0
  17. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/utils/__init__.py +0 -0
  18. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/utils/convert_image_matrix.py +0 -0
  19. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/utils/convert_matrix_network.py +0 -0
  20. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva/utils/device.py +0 -0
  21. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva.egg-info/dependency_links.txt +0 -0
  22. {complexeva-0.1.5 → complexeva-0.1.6}/complexeva.egg-info/top_level.txt +0 -0
  23. {complexeva-0.1.5 → complexeva-0.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: complexeva
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Home-page:
5
5
  Author: Don Yin
6
6
  Author-email: don_yin@outlook.com
@@ -0,0 +1,6 @@
1
+ from .dimension_1.lempel_ziv import lempel_ziv
2
+ from .dimension_1.higuchi import hfd_matlab_equivalent, hfd_pyeeg
3
+ from .dimension_2.cbb_matrix import FractalHandlerMatrix
4
+ from .network_space.cbb_networkx import FractalHandlerNetworkX
5
+
6
+ __all__ = ["lempel_ziv", "hfd_matlab_equivalent", "hfd_pyeeg", "FractalHandlerMatrix", "FractalHandlerNetworkX"]
@@ -1,16 +1,20 @@
1
1
  #!/Users/donyin/miniconda3/envs/rotation-1/bin/python
2
2
 
3
+ """
4
+ - validating measures of complexity is actually working using synthetic data on various Hurst exponents
5
+ """
3
6
 
4
7
  import numpy
5
8
  from scipy.fft import fft, ifft
6
9
  import matplotlib.pyplot as plt
7
10
  from donware import inspect_package
8
- from complexeva.measures.higuchi import hfd_pyeeg, hfd_matlab_equivalent
11
+ from complexeva.measures.dimension_1.higuchi import hfd_pyeeg, hfd_matlab_equivalent
12
+ from complexeva.measures.dimension_1.lempel_ziv import lempel_ziv_v1, lempel_ziv_v2, lempel_ziv, lempel_ziv_v4
9
13
 
10
14
 
11
- class HiguchiMethodValidate:
12
- def __init__(self, funcs, k_max: int, timeseries_len=1000, num_hurst=200):
13
- self.funcs, self.k_max, self.num_hurst, self.timeseries_len = funcs, k_max, num_hurst, timeseries_len
15
+ class ComplexityMeasuresValidator2D:
16
+ def __init__(self, funcs, timeseries_len=1000, num_hurst=200):
17
+ self.funcs, self.num_hurst, self.timeseries_len = funcs, num_hurst, timeseries_len
14
18
 
15
19
  def fbm(self, hurst):
16
20
  """
@@ -52,7 +56,7 @@ class HiguchiMethodValidate:
52
56
  hfd_results = []
53
57
 
54
58
  for func in self.funcs:
55
- hfd_results.append([func(series, self.k_max) for series in fBm_series])
59
+ hfd_results.append([func(series) for series in fBm_series])
56
60
 
57
61
  # ---- plotting ----
58
62
  fig, axs = plt.subplots(3, 1, figsize=(9, 10))
@@ -91,6 +95,8 @@ class HiguchiMethodValidate:
91
95
 
92
96
 
93
97
  if __name__ == "__main__":
94
- k_max = 30
95
- validator = HiguchiMethodValidate([hfd_pyeeg, hfd_matlab_equivalent], k_max)
98
+ validator = ComplexityMeasuresValidator2D([hfd_pyeeg, hfd_matlab_equivalent])
99
+ validator.plot_hfd_vs_hurst()
100
+
101
+ validator = ComplexityMeasuresValidator2D([lempel_ziv_v1, lempel_ziv_v2, lempel_ziv, lempel_ziv_v4])
96
102
  validator.plot_hfd_vs_hurst()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: complexeva
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Home-page:
5
5
  Author: Don Yin
6
6
  Author-email: don_yin@outlook.com
@@ -9,10 +9,6 @@ complexeva/dummies/__init__.py
9
9
  complexeva/dummies/matrix.py
10
10
  complexeva/dummies/networks.py
11
11
  complexeva/measures/__init__.py
12
- complexeva/measures/cbb_matrix.py
13
- complexeva/measures/cbb_networkx.py
14
- complexeva/measures/co_activation.py
15
- complexeva/measures/higuchi.py
16
12
  complexeva/measures/validate.py
17
13
  complexeva/utils/__init__.py
18
14
  complexeva/utils/convert_image_matrix.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="complexeva",
5
- version="0.1.5",
5
+ version="0.1.6",
6
6
  packages=find_packages(),
7
7
  install_requires=[],
8
8
  author="Don Yin",
@@ -1,5 +0,0 @@
1
- from .higuchi import hfd_matlab_equivalent, hfd_pyeeg
2
- from .cbb_matrix import FractalHandlerMatrix
3
- from .cbb_networkx import FractalHandlerNetworkX
4
-
5
- __all__ = ["hfd_matlab_equivalent", "hfd_pyeeg", "FractalHandlerMatrix", "FractalHandlerNetworkX"]
@@ -1,134 +0,0 @@
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
@@ -1,288 +0,0 @@
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)
@@ -1,100 +0,0 @@
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")
@@ -1,91 +0,0 @@
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):
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):
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):
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):
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]
File without changes
File without changes