cellects 0.1.0.dev1__py3-none-any.whl
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.
- cellects/__init__.py +0 -0
- cellects/__main__.py +49 -0
- cellects/config/__init__.py +0 -0
- cellects/config/all_vars_dict.py +154 -0
- cellects/core/__init__.py +0 -0
- cellects/core/cellects_paths.py +30 -0
- cellects/core/cellects_threads.py +1464 -0
- cellects/core/motion_analysis.py +1931 -0
- cellects/core/one_image_analysis.py +1065 -0
- cellects/core/one_video_per_blob.py +679 -0
- cellects/core/program_organizer.py +1347 -0
- cellects/core/script_based_run.py +154 -0
- cellects/gui/__init__.py +0 -0
- cellects/gui/advanced_parameters.py +1258 -0
- cellects/gui/cellects.py +189 -0
- cellects/gui/custom_widgets.py +789 -0
- cellects/gui/first_window.py +449 -0
- cellects/gui/if_several_folders_window.py +239 -0
- cellects/gui/image_analysis_window.py +1909 -0
- cellects/gui/required_output.py +232 -0
- cellects/gui/video_analysis_window.py +656 -0
- cellects/icons/__init__.py +0 -0
- cellects/icons/cellects_icon.icns +0 -0
- cellects/icons/cellects_icon.ico +0 -0
- cellects/image_analysis/__init__.py +0 -0
- cellects/image_analysis/cell_leaving_detection.py +54 -0
- cellects/image_analysis/cluster_flux_study.py +102 -0
- cellects/image_analysis/extract_exif.py +61 -0
- cellects/image_analysis/fractal_analysis.py +184 -0
- cellects/image_analysis/fractal_functions.py +108 -0
- cellects/image_analysis/image_segmentation.py +272 -0
- cellects/image_analysis/morphological_operations.py +867 -0
- cellects/image_analysis/network_functions.py +1244 -0
- cellects/image_analysis/one_image_analysis_threads.py +289 -0
- cellects/image_analysis/progressively_add_distant_shapes.py +246 -0
- cellects/image_analysis/shape_descriptors.py +981 -0
- cellects/utils/__init__.py +0 -0
- cellects/utils/formulas.py +881 -0
- cellects/utils/load_display_save.py +1016 -0
- cellects/utils/utilitarian.py +516 -0
- cellects-0.1.0.dev1.dist-info/LICENSE.odt +0 -0
- cellects-0.1.0.dev1.dist-info/METADATA +131 -0
- cellects-0.1.0.dev1.dist-info/RECORD +46 -0
- cellects-0.1.0.dev1.dist-info/WHEEL +5 -0
- cellects-0.1.0.dev1.dist-info/entry_points.txt +2 -0
- cellects-0.1.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
""" This script contains formula functions,
|
|
3
|
+
they mainly are simple mathematical expressions, used many times by Cellects.
|
|
4
|
+
This is part of the lower level of the software. When possible, these functions are optimized using Numba's decorator
|
|
5
|
+
@njit.
|
|
6
|
+
- linear_model
|
|
7
|
+
- get_divisor
|
|
8
|
+
- eudist
|
|
9
|
+
- cart2pol
|
|
10
|
+
- pol2cart
|
|
11
|
+
- get_peak_number
|
|
12
|
+
- cohen_d_95
|
|
13
|
+
- cohen_d
|
|
14
|
+
- SlopeDeviation
|
|
15
|
+
- acf_fft
|
|
16
|
+
- moving_average
|
|
17
|
+
- max_cum_sum_from_rolling_window
|
|
18
|
+
"""
|
|
19
|
+
from copy import deepcopy
|
|
20
|
+
from numba import njit
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@njit()
|
|
25
|
+
def sum_of_abs_differences(array1, array2):
|
|
26
|
+
"""
|
|
27
|
+
Calculate the sum of absolute differences between two NumPy arrays.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
array1 : array_like
|
|
32
|
+
First input array.
|
|
33
|
+
array2 : array_like
|
|
34
|
+
Second input array.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
scalar
|
|
39
|
+
Sum of absolute differences between the two arrays.
|
|
40
|
+
"""
|
|
41
|
+
return np.sum(np.absolute(array1 - array2))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@njit()
|
|
45
|
+
def to_uint8(an_array):
|
|
46
|
+
"""
|
|
47
|
+
Convert an array to unsigned 8-bit integers.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
an_array : numpy.ndarray
|
|
52
|
+
Input array to be converted. It can be of any numeric dtype.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
numpy.ndarray
|
|
57
|
+
The input array rounded to the nearest integer and then cast to
|
|
58
|
+
unsigned 8-bit integers.
|
|
59
|
+
|
|
60
|
+
Raises
|
|
61
|
+
------
|
|
62
|
+
TypeError
|
|
63
|
+
If `an_array` is not a numpy.ndarray.
|
|
64
|
+
|
|
65
|
+
Notes
|
|
66
|
+
-----
|
|
67
|
+
This function uses Numba's `@njit` decorator for performance optimization.
|
|
68
|
+
|
|
69
|
+
Examples
|
|
70
|
+
--------
|
|
71
|
+
>>> import numpy as np
|
|
72
|
+
>>> result = to_uint8(np.array([1.2, 2.5, -3.7]))
|
|
73
|
+
>>> print(result)
|
|
74
|
+
[1 3 0]
|
|
75
|
+
"""
|
|
76
|
+
out = np.empty_like(an_array)
|
|
77
|
+
return np.round(an_array, 0, out).astype(np.uint8)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@njit()
|
|
81
|
+
def bracket_to_uint8_image_contrast(image):
|
|
82
|
+
"""
|
|
83
|
+
Convert an image with bracket contrast values to uint8 type.
|
|
84
|
+
|
|
85
|
+
This function normalizes an input image by scaling the minimum and maximum
|
|
86
|
+
values of the image to the range [0, 255] and then converts it to uint8
|
|
87
|
+
data type.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
image : np.ndarray
|
|
92
|
+
Input image as a numpy array with floating-point values.
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
np.ndarray
|
|
97
|
+
Output image converted to uint8 type after normalization.
|
|
98
|
+
"""
|
|
99
|
+
image -= np.min(image)
|
|
100
|
+
return to_uint8(255 * (image / np.max(image)))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@njit()
|
|
104
|
+
def linear_model(x, a, b):
|
|
105
|
+
"""
|
|
106
|
+
Perform a linear transformation on input data using slope and intercept.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
x : array_like
|
|
111
|
+
Input data.
|
|
112
|
+
a : float
|
|
113
|
+
Slope coefficient.
|
|
114
|
+
b : float
|
|
115
|
+
Intercept.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
float
|
|
120
|
+
Resulting value from linear transformation: `a` * `x` + `b`.
|
|
121
|
+
|
|
122
|
+
Examples
|
|
123
|
+
--------
|
|
124
|
+
>>> result = linear_model(5, 2.0, 1.5)
|
|
125
|
+
>>> print(result) # doctest: +SKIP
|
|
126
|
+
11.5
|
|
127
|
+
|
|
128
|
+
Notes
|
|
129
|
+
-----
|
|
130
|
+
This function uses Numba's @njit decorator for performance.
|
|
131
|
+
"""
|
|
132
|
+
return a * x + b
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@njit()
|
|
136
|
+
def get_power_dists(binary_image, cx, cy, n):
|
|
137
|
+
"""
|
|
138
|
+
Calculate the power distributions based on the given center coordinates and exponent.
|
|
139
|
+
|
|
140
|
+
This function computes the `n`th powers of x and y distances from
|
|
141
|
+
a given center point `(cx, cy)` for each pixel in the binary image.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
binary_image : np.ndarray
|
|
146
|
+
A 2D array (binary image) where the power distributions are calculated.
|
|
147
|
+
cx : float
|
|
148
|
+
The x-coordinate of the center point.
|
|
149
|
+
cy : float
|
|
150
|
+
The y-coordinate of the center point.
|
|
151
|
+
n : int
|
|
152
|
+
The exponent for power distribution calculation.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
tuple[np.ndarray, np.ndarray]
|
|
157
|
+
A tuple containing two arrays:
|
|
158
|
+
- The first array contains the `n`th power of x distances from the center.
|
|
159
|
+
- The second array contains the `n`th power of y distances from the center.
|
|
160
|
+
|
|
161
|
+
Notes
|
|
162
|
+
-----
|
|
163
|
+
This function uses Numba's `@njit` decorator for performance optimization.
|
|
164
|
+
Ensure that `binary_image` is a NumPy ndarray to avoid type issues.
|
|
165
|
+
|
|
166
|
+
Examples
|
|
167
|
+
--------
|
|
168
|
+
>>> binary_image = np.zeros((10, 10))
|
|
169
|
+
>>> xn, yn = get_power_dists(binary_image, 5.0, 5.0, 2)
|
|
170
|
+
>>> print(xn.shape), print(yn.shape
|
|
171
|
+
(10,) (10,)
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
|
+
xn = (np.arange(binary_image.shape[1]) - cx) ** n
|
|
175
|
+
yn = (np.arange(binary_image.shape[0]) - cy) ** n
|
|
176
|
+
return xn, yn
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@njit()
|
|
180
|
+
def get_var(mo, binary_image, Xn, Yn):
|
|
181
|
+
"""
|
|
182
|
+
Compute the center of mass in 2D space.
|
|
183
|
+
|
|
184
|
+
This function calculates the weighted average position (centroid) of
|
|
185
|
+
a binary image using given pixel coordinates and moments.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
mo : dict
|
|
190
|
+
Image moments dictionary with keys 'm00', 'm10', and 'm20'.
|
|
191
|
+
binary_image : ndarray
|
|
192
|
+
2D binary image where non-zero pixels are considered.
|
|
193
|
+
Xn : ndarray
|
|
194
|
+
Array of x-coordinates for each pixel in `binary_image`.
|
|
195
|
+
Yn : ndarray
|
|
196
|
+
Array of y-coordinates for each pixel in `binary_image`.
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
tuple
|
|
201
|
+
A tuple of two floats `(vx, vy)` representing the centroid coordinates.
|
|
202
|
+
|
|
203
|
+
Raises
|
|
204
|
+
------
|
|
205
|
+
ZeroDivisionError
|
|
206
|
+
If `mo['m00']` is zero, indicating no valid pixels in the image.
|
|
207
|
+
The function raises a `ZeroDivisionError`.
|
|
208
|
+
|
|
209
|
+
Notes
|
|
210
|
+
-----
|
|
211
|
+
Performance considerations: This function uses Numba's `@njit` decorator for performance.
|
|
212
|
+
"""
|
|
213
|
+
vx = np.sum(binary_image * Xn) / mo["m00"]
|
|
214
|
+
vy = np.sum(binary_image * Yn) / mo["m00"]
|
|
215
|
+
return vx, vy
|
|
216
|
+
|
|
217
|
+
@njit()
|
|
218
|
+
def get_skewness_kurtosis(mnx, mny, sx, sy, n):
|
|
219
|
+
"""
|
|
220
|
+
Calculates skewness and kurtosis of a distribution.
|
|
221
|
+
|
|
222
|
+
This function computes the skewness and kurtosis from given statistical
|
|
223
|
+
moments, standard deviations, and order of moments.
|
|
224
|
+
|
|
225
|
+
Parameters
|
|
226
|
+
----------
|
|
227
|
+
mnx : float
|
|
228
|
+
The third moment about the mean for x.
|
|
229
|
+
mny : float
|
|
230
|
+
The fourth moment about the mean for y.
|
|
231
|
+
sx : float
|
|
232
|
+
The standard deviation of x.
|
|
233
|
+
sy : float
|
|
234
|
+
The standard deviation of y.
|
|
235
|
+
n : int
|
|
236
|
+
Order of the moment (3 for skewness, 4 for kurtosis).
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
skewness : float
|
|
241
|
+
The computed skewness.
|
|
242
|
+
kurtosis : float
|
|
243
|
+
The computed kurtosis.
|
|
244
|
+
|
|
245
|
+
Notes
|
|
246
|
+
-----
|
|
247
|
+
This function uses Numba's `@njit` decorator for performance.
|
|
248
|
+
Ensure that the values of `mnx`, `mny`, `sx`, and `sy` are non-zero to avoid division by zero.
|
|
249
|
+
If `n = 3`, the function calculates skewness. If `n = 4`, it calculates kurtosis.
|
|
250
|
+
|
|
251
|
+
Examples
|
|
252
|
+
--------
|
|
253
|
+
>>> skewness, kurtosis = get_skewness_kurtosis(1.5, 2.0, 0.5, 0.75, 3)
|
|
254
|
+
>>> print("Skewness:", skewness)
|
|
255
|
+
Skewness: 8.0
|
|
256
|
+
>>> print("Kurtosis:", kurtosis)
|
|
257
|
+
Kurtosis: nan
|
|
258
|
+
|
|
259
|
+
"""
|
|
260
|
+
return mnx / sx ** n, mny / sy ** n
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_standard_deviations(mo, binary_image, cx, cy):
|
|
264
|
+
"""
|
|
265
|
+
Return spatial standard deviations for a given moment and binary image.
|
|
266
|
+
|
|
267
|
+
This function computes the square root of variances along `x` (horizontal)
|
|
268
|
+
and `y` (vertical) axes for the given binary image and moment.
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
mo : ndarray of float64
|
|
273
|
+
The moment matrix computed from the binary image.
|
|
274
|
+
binary_image : ndarray of bool or int8
|
|
275
|
+
The binary input image where the moments are computed.
|
|
276
|
+
cx : float64
|
|
277
|
+
X-coordinate of center of mass (horizontal position).
|
|
278
|
+
cy : float64
|
|
279
|
+
Y-coordinate of center of mass (vertical position).
|
|
280
|
+
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
tuple[ndarray of float64, ndarray of float64]
|
|
284
|
+
Tuple containing the standard deviations along the x and y axes.
|
|
285
|
+
|
|
286
|
+
Raises
|
|
287
|
+
------
|
|
288
|
+
ValueError
|
|
289
|
+
If `binary_image` is not a binary image or has an invalid datatype.
|
|
290
|
+
|
|
291
|
+
Notes
|
|
292
|
+
-----
|
|
293
|
+
This function uses the `get_power_dists` and `get_var` functions to compute
|
|
294
|
+
the distributed variances, which are then transformed into standard deviations.
|
|
295
|
+
|
|
296
|
+
Examples
|
|
297
|
+
--------
|
|
298
|
+
>>> import numpy as np
|
|
299
|
+
>>> binary_image = np.array([[0, 1], [1, 0]], dtype=np.int8)
|
|
300
|
+
>>> mo = np.array([[2.0], [3.0]])
|
|
301
|
+
>>> cx, cy = 1.5, 1.5
|
|
302
|
+
>>> stdx, stdy = get_standard_deviations(mo, binary_image, cx, cy)
|
|
303
|
+
>>> print(stdx)
|
|
304
|
+
[1.1]
|
|
305
|
+
>>> print(stdy)
|
|
306
|
+
[0.8366600265...]
|
|
307
|
+
"""
|
|
308
|
+
x2, y2 = get_power_dists(binary_image, cx, cy, 2)
|
|
309
|
+
X2, Y2 = np.meshgrid(x2, y2)
|
|
310
|
+
vx, vy = get_var(mo, binary_image, X2, Y2)
|
|
311
|
+
return np.sqrt(vx), np.sqrt(vy)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def get_skewness(mo, binary_image, cx, cy, sx, sy):
|
|
315
|
+
"""
|
|
316
|
+
Calculate the skewness of a moment image.
|
|
317
|
+
|
|
318
|
+
Parameters
|
|
319
|
+
----------
|
|
320
|
+
mo : ndarray
|
|
321
|
+
Moment matrix.
|
|
322
|
+
binary_image : ndarray
|
|
323
|
+
Binary image of the object.
|
|
324
|
+
cx, cy : int
|
|
325
|
+
Center coordinates of the moment calculation.
|
|
326
|
+
sx, sy : float or int
|
|
327
|
+
Standard deviation in x and y directions.
|
|
328
|
+
|
|
329
|
+
Returns
|
|
330
|
+
-------
|
|
331
|
+
float
|
|
332
|
+
Skewness value. If the values are not defined, returns `None`.
|
|
333
|
+
|
|
334
|
+
Examples
|
|
335
|
+
--------
|
|
336
|
+
>>> import numpy as np
|
|
337
|
+
>>> mo = np.array([[1, 2, 3], [4, 5, 6]]) # Example moment matrix
|
|
338
|
+
>>> binary_image = np.array([[0, 1], [1, 0]]) # Example binary image
|
|
339
|
+
>>> cx = 1
|
|
340
|
+
>>> cy = 0
|
|
341
|
+
>>> sx = 2.5
|
|
342
|
+
>>> sy = 1.8
|
|
343
|
+
|
|
344
|
+
>>> result = get_skewness(mo, binary_image, cx, cy, sx, sy)
|
|
345
|
+
>>> print(result) # Expected output may vary based on the input data
|
|
346
|
+
None
|
|
347
|
+
"""
|
|
348
|
+
x3, y3 = get_power_dists(binary_image, cx, cy, 3)
|
|
349
|
+
X3, Y3 = np.meshgrid(x3, y3)
|
|
350
|
+
m3x, m3y = get_var(mo, binary_image, X3, Y3)
|
|
351
|
+
return get_skewness_kurtosis(m3x, m3y, sx, sy, 3)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def get_kurtosis(mo, binary_image, cx, cy, sx, sy):
|
|
355
|
+
"""
|
|
356
|
+
Calculate the kurtosis of a binary image.
|
|
357
|
+
|
|
358
|
+
The function calculates the fourth moment (kurtosis) of the given
|
|
359
|
+
binary image around the specified center coordinates with an option
|
|
360
|
+
to specify the size of the square window.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
mo : np.ndarray
|
|
365
|
+
A 2D numpy ndarray of moments calculated from the image.
|
|
366
|
+
binary_image : np.ndarray
|
|
367
|
+
A 2D numpy ndarray representing a binary image.
|
|
368
|
+
cx : int or float
|
|
369
|
+
The x-coordinate of the center point of the square window.
|
|
370
|
+
cy : int or float
|
|
371
|
+
The y-coordinate of the center point of the square window.
|
|
372
|
+
sx : int or float
|
|
373
|
+
The x-length of the square window (width).
|
|
374
|
+
sy : int or float
|
|
375
|
+
The y-length of the square window (height).
|
|
376
|
+
|
|
377
|
+
Returns
|
|
378
|
+
-------
|
|
379
|
+
float
|
|
380
|
+
The kurtosis value calculated from the moments.
|
|
381
|
+
|
|
382
|
+
Examples
|
|
383
|
+
--------
|
|
384
|
+
>>> mo = np.array([[0, 1], [2, 3]])
|
|
385
|
+
>>> binary_image = np.array([[1, 0], [0, 1]])
|
|
386
|
+
>>> cx = 2
|
|
387
|
+
>>> cy = 3
|
|
388
|
+
>>> sx = 5
|
|
389
|
+
>>> sy = 6
|
|
390
|
+
>>> result = get_kurtosis(mo, binary_image, cx, cy, sx, sy)
|
|
391
|
+
>>> print(result)
|
|
392
|
+
expected output
|
|
393
|
+
"""
|
|
394
|
+
x4, y4 = get_power_dists(binary_image, cx, cy, 4)
|
|
395
|
+
X4, Y4 = np.meshgrid(x4, y4)
|
|
396
|
+
m4x, m4y = get_var(mo, binary_image, X4, Y4)
|
|
397
|
+
return get_skewness_kurtosis(m4x, m4y, sx, sy, 4)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
@njit()
|
|
401
|
+
def get_inertia_axes(mo):
|
|
402
|
+
"""
|
|
403
|
+
Calculate the inertia axes of a moment object.
|
|
404
|
+
|
|
405
|
+
This function computes the barycenters, central moments,
|
|
406
|
+
and the lengths of the major and minor axes, as well as
|
|
407
|
+
their orientation.
|
|
408
|
+
|
|
409
|
+
Parameters
|
|
410
|
+
----------
|
|
411
|
+
mo : dict
|
|
412
|
+
Dictionary containing moments, which should include keys:
|
|
413
|
+
'm00', 'm10', 'm01', 'm20', and 'm11'.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
tuple
|
|
418
|
+
A tuple containing:
|
|
419
|
+
- cx : float
|
|
420
|
+
The x-coordinate of the barycenter.
|
|
421
|
+
- cy : float
|
|
422
|
+
The y-coordinate of the barycenter.
|
|
423
|
+
- major_axis_len : float
|
|
424
|
+
The length of the major axis.
|
|
425
|
+
- minor_axis_len : float
|
|
426
|
+
The length of the minor axis.
|
|
427
|
+
- axes_orientation : float
|
|
428
|
+
The orientation of the axes in radians.
|
|
429
|
+
|
|
430
|
+
Notes
|
|
431
|
+
-----
|
|
432
|
+
This function uses Numba's @njit decorator for performance.
|
|
433
|
+
The moments in the input dictionary should be computed from
|
|
434
|
+
the same image region.
|
|
435
|
+
|
|
436
|
+
Examples
|
|
437
|
+
--------
|
|
438
|
+
>>> mo = {'m00': 1.0, 'm10': 2.0, 'm01': 3.0, 'm20': 4.0, 'm11': 5.0}
|
|
439
|
+
>>> get_inertia_axes(mo)
|
|
440
|
+
(2.0, 3.0, 9.165151389911677, 0.8421875803239, 0.7853981633974483)
|
|
441
|
+
|
|
442
|
+
"""
|
|
443
|
+
#L. Rocha, L. Velho and P.C.P. Calvalho (2002)
|
|
444
|
+
#http://sibgrapi.sid.inpe.br/col/sid.inpe.br/banon/2002/10.23.11.34/doc/35.pdf
|
|
445
|
+
# http://raphael.candelier.fr/?blog=Image%20Moments
|
|
446
|
+
|
|
447
|
+
# Calculate barycenters
|
|
448
|
+
cx = mo["m10"] / mo["m00"]
|
|
449
|
+
cy = mo["m01"] / mo["m00"]
|
|
450
|
+
# Calculate central moments
|
|
451
|
+
c20 = (mo["m20"] / mo["m00"]) - np.square(cx)
|
|
452
|
+
c02 = (mo["m02"] / mo["m00"]) - np.square(cy)
|
|
453
|
+
c11 = (mo["m11"] / mo["m00"]) - (cx * cy)
|
|
454
|
+
# Calculate major and minor axi lengths OK
|
|
455
|
+
major_axis_len = np.sqrt(6 * (c20 + c02 + np.sqrt(np.square(2 * c11) + np.square(c20 - c02))))
|
|
456
|
+
minor_axis_len = np.sqrt(6 * (c20 + c02 - np.sqrt(np.square(2 * c11) + np.square(c20 - c02))))
|
|
457
|
+
if (c20 - c02) != 0:
|
|
458
|
+
axes_orientation = (0.5 * np.arctan((2 * c11) / (c20 - c02))) + ((c20 < c02) * (np.pi /2))
|
|
459
|
+
else:
|
|
460
|
+
axes_orientation = 0
|
|
461
|
+
return cx, cy, major_axis_len, minor_axis_len, axes_orientation
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def eudist(v1, v2):
|
|
465
|
+
"""
|
|
466
|
+
Calculate the Euclidean distance between two points in n-dimensional space.
|
|
467
|
+
|
|
468
|
+
Parameters
|
|
469
|
+
----------
|
|
470
|
+
v1 : iterable of float
|
|
471
|
+
The coordinates of the first point.
|
|
472
|
+
v2 : iterable of float
|
|
473
|
+
The coordinates of the second point.
|
|
474
|
+
|
|
475
|
+
Returns
|
|
476
|
+
-------
|
|
477
|
+
float
|
|
478
|
+
The Euclidean distance between `v1` and `v2`.
|
|
479
|
+
|
|
480
|
+
Raises
|
|
481
|
+
------
|
|
482
|
+
ValueError
|
|
483
|
+
If `v1` and `v2` do not have the same length.
|
|
484
|
+
|
|
485
|
+
Notes
|
|
486
|
+
-----
|
|
487
|
+
The Euclidean distance is calculated using the standard formula:
|
|
488
|
+
√((x2 − x1)^2 + (y2 − y1)^2 + ...).
|
|
489
|
+
|
|
490
|
+
Examples
|
|
491
|
+
--------
|
|
492
|
+
>>> v1 = [1.0, 2.0]
|
|
493
|
+
>>> v2 = [4.0, 6.0]
|
|
494
|
+
>>> eudist(v1, v2)
|
|
495
|
+
5.0
|
|
496
|
+
|
|
497
|
+
>>> v1 = [1.0, 2.0, 3.0]
|
|
498
|
+
>>> v2 = [4.0, 6.0, 8.0]
|
|
499
|
+
>>> eudist(v1, v2)
|
|
500
|
+
7.0710678118654755
|
|
501
|
+
"""
|
|
502
|
+
dist = [(a - b)**2 for a, b in zip(v1, v2)]
|
|
503
|
+
dist = np.sqrt(np.sum(dist))
|
|
504
|
+
return dist
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def cart2pol(x, y):
|
|
508
|
+
"""
|
|
509
|
+
Convert Cartesian coordinates to polar coordinates.
|
|
510
|
+
|
|
511
|
+
Given kartesian coordinates x and y, return the corresponding
|
|
512
|
+
polar coordinates rho and phi. This is done by using the formula for
|
|
513
|
+
conversion from Cartesian to Polar.
|
|
514
|
+
|
|
515
|
+
Parameters
|
|
516
|
+
----------
|
|
517
|
+
x : float
|
|
518
|
+
The x-coordinate of the point in Cartesian space.
|
|
519
|
+
y : float
|
|
520
|
+
The y-coordinate of the point in Cartesian space.
|
|
521
|
+
|
|
522
|
+
Returns
|
|
523
|
+
-------
|
|
524
|
+
tuple[float, float]
|
|
525
|
+
A tuple containing (rho, phi) where rho is the radial distance
|
|
526
|
+
and phi is the angle in radians.
|
|
527
|
+
|
|
528
|
+
Examples
|
|
529
|
+
--------
|
|
530
|
+
>>> cart2pol(1, 0)
|
|
531
|
+
(1.0, 0.0)
|
|
532
|
+
|
|
533
|
+
>>> cart2pol(0, 1)
|
|
534
|
+
(1.0, 1.5707963267948966)
|
|
535
|
+
|
|
536
|
+
>>> cart2pol(-1, 0)
|
|
537
|
+
(1.0, 3.141592653589793)
|
|
538
|
+
"""
|
|
539
|
+
rho = np.sqrt(x**2 + y**2)
|
|
540
|
+
phi = np.arctan2(y, x)
|
|
541
|
+
return(rho, phi)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def pol2cart(rho, phi):
|
|
545
|
+
"""
|
|
546
|
+
Convert from polar to Cartesian coordinates.
|
|
547
|
+
|
|
548
|
+
Given a point in polar coordinates (rho, phi), this function
|
|
549
|
+
calculates the corresponding Cartesian coordinates (x, y).
|
|
550
|
+
|
|
551
|
+
Parameters
|
|
552
|
+
----------
|
|
553
|
+
rho : float
|
|
554
|
+
The radius in polar coordinates.
|
|
555
|
+
phi : float
|
|
556
|
+
The angle in radians in polar coordinates.
|
|
557
|
+
|
|
558
|
+
Returns
|
|
559
|
+
-------
|
|
560
|
+
tuple[float, float]
|
|
561
|
+
A tuple containing the x and y coordinates in Cartesian
|
|
562
|
+
representation.
|
|
563
|
+
|
|
564
|
+
Notes
|
|
565
|
+
-----
|
|
566
|
+
This function assumes the input values for phi are in radians.
|
|
567
|
+
If they are provided in degrees, convert them using
|
|
568
|
+
`np.radians(phi)` before calling this function.
|
|
569
|
+
|
|
570
|
+
Examples
|
|
571
|
+
--------
|
|
572
|
+
>>> pol2cart(1.0, 0)
|
|
573
|
+
(1.0, 0.0)
|
|
574
|
+
|
|
575
|
+
>>> pol2cart(5.0, np.pi / 4)
|
|
576
|
+
(3.5355339059327378, 3.5355339059327373)
|
|
577
|
+
"""
|
|
578
|
+
x = rho * np.cos(phi)
|
|
579
|
+
y = rho * np.sin(phi)
|
|
580
|
+
return x, y
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def cohen_d_95(vector_1, vector_2, nbboot=100000):
|
|
584
|
+
"""
|
|
585
|
+
Calculate Cohen's d with a 95% confidence interval through bootstrapping.
|
|
586
|
+
|
|
587
|
+
Performs bootstrapping on the provided data vectors to calculate Cohen's d
|
|
588
|
+
effect size along with its 95% confidence interval.
|
|
589
|
+
|
|
590
|
+
Parameters
|
|
591
|
+
----------
|
|
592
|
+
vector_1 : array_like
|
|
593
|
+
The first data vector.
|
|
594
|
+
vector_2 : array_like
|
|
595
|
+
The second data vector.
|
|
596
|
+
nbboot : int, optional
|
|
597
|
+
Number of bootstrap iterations. Default is ``100000``.
|
|
598
|
+
|
|
599
|
+
Returns
|
|
600
|
+
-------
|
|
601
|
+
ndarray
|
|
602
|
+
An array containing Cohen's d effect size and its 95% confidence interval.
|
|
603
|
+
The first element is the Cohen's d value, followed by the lower and upper
|
|
604
|
+
bounds of the confidence interval.
|
|
605
|
+
|
|
606
|
+
Raises
|
|
607
|
+
------
|
|
608
|
+
ValueError
|
|
609
|
+
If `vector_1` and `vector_2` are not of the same length.
|
|
610
|
+
TypeError
|
|
611
|
+
If `vector_1` or `vector_2` are not array-like.
|
|
612
|
+
|
|
613
|
+
Notes
|
|
614
|
+
-----
|
|
615
|
+
This function uses bootstrapping to estimate the 95% confidence interval for
|
|
616
|
+
Cohen's d. The calculation of Cohen’s d assumes equal population variances.
|
|
617
|
+
|
|
618
|
+
Examples
|
|
619
|
+
--------
|
|
620
|
+
>>> vector_1 = np.array([2.3, 3.4, 5.6, 7.8])
|
|
621
|
+
>>> vector_2 = np.array([1.1, 4.3, 9.8])
|
|
622
|
+
>>> result = cohen_d_95(vector_1, vector_2)
|
|
623
|
+
>>> print(result)
|
|
624
|
+
[ 0.73864592 -1.46522 1.88307 ]
|
|
625
|
+
"""
|
|
626
|
+
boot = np.zeros(nbboot, dtype=int)
|
|
627
|
+
n1 = len(vector_1)
|
|
628
|
+
n2 = len(vector_2)
|
|
629
|
+
for i in np.arange(nbboot):
|
|
630
|
+
v1bis = np.random.choice(vector_1, size=n1, replace=True)
|
|
631
|
+
v2bis = np.random.choice(vector_2, size=n2, replace=True)
|
|
632
|
+
boot[i] = cohen_d(v1bis,v2bis)
|
|
633
|
+
effect_low_top = np.append(cohen_d(vector_1, vector_2), np.quantile(boot, (0.025, 0.975)))
|
|
634
|
+
return effect_low_top
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def cohen_d(vector_1, vector_2):
|
|
638
|
+
"""
|
|
639
|
+
Calculate Cohen's d effect size between two independent groups.
|
|
640
|
+
|
|
641
|
+
Parameters
|
|
642
|
+
----------
|
|
643
|
+
vector_1 : array_like
|
|
644
|
+
First group's data.
|
|
645
|
+
vector_2 : array_like
|
|
646
|
+
Second group's data.
|
|
647
|
+
|
|
648
|
+
Returns
|
|
649
|
+
-------
|
|
650
|
+
float
|
|
651
|
+
Cohen's d effect size statistic between the two groups.
|
|
652
|
+
|
|
653
|
+
Notes
|
|
654
|
+
-----
|
|
655
|
+
Cohen's d is a measure of the difference between two means in terms
|
|
656
|
+
of standard deviation units. It can be used to compare the mean difference
|
|
657
|
+
between two groups, normalized by pooled standard deviation.
|
|
658
|
+
|
|
659
|
+
Examples
|
|
660
|
+
--------
|
|
661
|
+
>>> vector_1 = [2.3, 4.5, 6.7]
|
|
662
|
+
>>> vector_2 = [5.3, 7.8, 9.1]
|
|
663
|
+
>>> result = cohen_d(vector_1, vector_2)
|
|
664
|
+
>>> print(result)
|
|
665
|
+
0.864
|
|
666
|
+
"""
|
|
667
|
+
m1 = np.mean(vector_1)
|
|
668
|
+
m2 = np.mean(vector_2)
|
|
669
|
+
s1 = np.std(vector_1)
|
|
670
|
+
s2 = np.std(vector_2)
|
|
671
|
+
n1 = len(vector_1)
|
|
672
|
+
n2 = len(vector_2)
|
|
673
|
+
spooled = np.sqrt(((n2 - 1) * s2 ** 2 + (n1 - 1) * s1 ** 2) / (n1 + n2 - 2))
|
|
674
|
+
return (m2 - m1) / spooled
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def moving_average(vector, step):
|
|
678
|
+
"""
|
|
679
|
+
Calculate the moving average of a given vector with specified step size.
|
|
680
|
+
|
|
681
|
+
Computes the moving average of input `vector` using specified `step`
|
|
682
|
+
size. NaN values are treated as zeros in the calculation to allow
|
|
683
|
+
for continuous averaging.
|
|
684
|
+
|
|
685
|
+
Parameters
|
|
686
|
+
----------
|
|
687
|
+
vector : numpy.ndarray
|
|
688
|
+
Input vector for which to calculate the moving average.
|
|
689
|
+
step : int
|
|
690
|
+
Size of the window for computing the moving average.
|
|
691
|
+
|
|
692
|
+
Returns
|
|
693
|
+
-------
|
|
694
|
+
numpy.ndarray
|
|
695
|
+
Vector containing the moving averages of the input vector.
|
|
696
|
+
|
|
697
|
+
Raises
|
|
698
|
+
------
|
|
699
|
+
ValueError
|
|
700
|
+
If `step` is less than 1.
|
|
701
|
+
ValueError
|
|
702
|
+
If the input vector has no valid (non-NaN) elements.
|
|
703
|
+
|
|
704
|
+
Notes
|
|
705
|
+
-----
|
|
706
|
+
- The function considers NaN values as zeros during the averaging process.
|
|
707
|
+
- If `step` is greater than or equal to the length of the vector, a warning will be raised.
|
|
708
|
+
|
|
709
|
+
Examples
|
|
710
|
+
--------
|
|
711
|
+
>>> import numpy as np
|
|
712
|
+
>>> vector = np.array([1.0, 2.0, np.nan, 4.0, 5.0])
|
|
713
|
+
>>> step = 3
|
|
714
|
+
>>> result = moving_average(vector, step)
|
|
715
|
+
>>> print(result)
|
|
716
|
+
[1.5 2.33333333 3.66666667 4. nan]
|
|
717
|
+
"""
|
|
718
|
+
substep = np.array((- int(np.floor((step - 1) / 2)), int(np.ceil((step - 1) / 2))))
|
|
719
|
+
sums = np.zeros(vector.shape)
|
|
720
|
+
n_okays = deepcopy(sums)
|
|
721
|
+
true_numbers = np.logical_not(np.isnan(vector))
|
|
722
|
+
vector[np.logical_not(true_numbers)] = 0
|
|
723
|
+
for step_i in np.arange(substep[1] + 1):
|
|
724
|
+
sums[step_i: (sums.size - step_i)] = sums[step_i: (sums.size - step_i)] + vector[(2 * step_i):]
|
|
725
|
+
n_okays[step_i: (sums.size - step_i)] = n_okays[step_i: (sums.size - step_i)] + true_numbers[(2 * step_i):]
|
|
726
|
+
if np.logical_and(step_i > 0, step_i < np.absolute(substep[0])):
|
|
727
|
+
sums[step_i: (sums.size - step_i)] = sums[step_i: (sums.size - step_i)] + vector[:(sums.size - (2 * step_i)):]
|
|
728
|
+
n_okays[step_i: (sums.size - step_i)] = n_okays[step_i: (sums.size - step_i)] + true_numbers[:(
|
|
729
|
+
true_numbers.size - (2 * step_i))]
|
|
730
|
+
vector = sums / n_okays
|
|
731
|
+
return vector
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def max_cum_sum_from_rolling_window(side_length, window_step):
|
|
735
|
+
"""
|
|
736
|
+
Calculate the maximum cumulative sum from a rolling window.
|
|
737
|
+
|
|
738
|
+
Parameters
|
|
739
|
+
----------
|
|
740
|
+
side_length : int
|
|
741
|
+
The length of the side of the square window.
|
|
742
|
+
window_step : int
|
|
743
|
+
The step size for rolling the window.
|
|
744
|
+
|
|
745
|
+
Returns
|
|
746
|
+
-------
|
|
747
|
+
int
|
|
748
|
+
The maximum cumulative sum calculated from the rolling window.
|
|
749
|
+
|
|
750
|
+
Raises
|
|
751
|
+
------
|
|
752
|
+
ValueError
|
|
753
|
+
If `side_length` or `window_step` are not positive integers.
|
|
754
|
+
|
|
755
|
+
Notes
|
|
756
|
+
-----
|
|
757
|
+
- The function assumes that `side_length` and `window_step` are both positive integers.
|
|
758
|
+
- This function uses NumPy's ceiling function to handle non-integer division results.
|
|
759
|
+
|
|
760
|
+
Examples
|
|
761
|
+
--------
|
|
762
|
+
>>> result = max_cum_sum_from_rolling_window(10, 2)
|
|
763
|
+
>>> print(result)
|
|
764
|
+
9
|
|
765
|
+
|
|
766
|
+
"""
|
|
767
|
+
return np.square(np.ceil(side_length / window_step))
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def find_common_coord(array1, array2):
|
|
771
|
+
"""
|
|
772
|
+
Find common coordinates between two arrays.
|
|
773
|
+
|
|
774
|
+
Given two 2D arrays, this function finds the indices where all corresponding
|
|
775
|
+
elements are equal.
|
|
776
|
+
|
|
777
|
+
Parameters
|
|
778
|
+
----------
|
|
779
|
+
array1 : array_like
|
|
780
|
+
First 2D array of shape (M, N).
|
|
781
|
+
array2 : array_like
|
|
782
|
+
Second 2D array of shape (P, N).
|
|
783
|
+
|
|
784
|
+
Returns
|
|
785
|
+
-------
|
|
786
|
+
bool_array : ndarray
|
|
787
|
+
A 2D boolean array of shape (M, P) where each element indicates
|
|
788
|
+
whether the corresponding rows in `array1` and `array2` are equal.
|
|
789
|
+
|
|
790
|
+
Examples
|
|
791
|
+
--------
|
|
792
|
+
>>> array1 = np.array([[1, 2], [3, 4]])
|
|
793
|
+
>>> array2 = np.array([[5, 6], [1, 2]])
|
|
794
|
+
>>> result = find_common_coord(array1, array2)
|
|
795
|
+
>>> print(result)
|
|
796
|
+
array([ True, False])"""
|
|
797
|
+
return (array1[:, None, :] == array2[None, :, :]).all(-1).any(-1)
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def find_duplicates_coord(array1):
|
|
801
|
+
"""
|
|
802
|
+
Find duplicate rows in a 2D array and return their coordinate indices.
|
|
803
|
+
|
|
804
|
+
Given a 2D NumPy array, this function identifies rows that are duplicated (i.e., appear more than once) and returns a boolean array indicating their positions.
|
|
805
|
+
|
|
806
|
+
Parameters
|
|
807
|
+
----------
|
|
808
|
+
array1 : ndarray
|
|
809
|
+
Input 2D array of shape (n_rows, n_columns) from which to find duplicate rows.
|
|
810
|
+
|
|
811
|
+
Returns
|
|
812
|
+
-------
|
|
813
|
+
duplicates : ndarray
|
|
814
|
+
Boolean array of shape (n_rows,), where `True` indicates that the corresponding row in `array1` is a duplicate.
|
|
815
|
+
|
|
816
|
+
Examples
|
|
817
|
+
--------
|
|
818
|
+
>>> import numpy as np
|
|
819
|
+
>>> array1 = np.array([[1, 2], [3, 4], [1, 2], [5, 6]])
|
|
820
|
+
>>> find_duplicates_coord(array1)
|
|
821
|
+
array([ True, False, True, False])"""
|
|
822
|
+
unique_rows, inverse_indices = np.unique(array1, axis=0, return_inverse=True)
|
|
823
|
+
counts = np.bincount(inverse_indices)
|
|
824
|
+
# A row is duplicate if its count > 1
|
|
825
|
+
return counts[inverse_indices] > 1
|
|
826
|
+
|
|
827
|
+
def remove_excedent_duplicates_coord(array1):
|
|
828
|
+
"""
|
|
829
|
+
Remove duplicate rows in a 2D array based on coordinate order.
|
|
830
|
+
|
|
831
|
+
This function removes all but the first occurrence of duplicate rows in a 2D array.
|
|
832
|
+
The removal is based on the order of appearance (coordinates) in the input array.
|
|
833
|
+
|
|
834
|
+
Parameters
|
|
835
|
+
----------
|
|
836
|
+
array1 : numpy.ndarray
|
|
837
|
+
A 2D array of shape (n, m) containing rows to be deduplicated.
|
|
838
|
+
|
|
839
|
+
Other Parameters
|
|
840
|
+
----------------
|
|
841
|
+
None
|
|
842
|
+
|
|
843
|
+
Returns
|
|
844
|
+
-------
|
|
845
|
+
numpy.ndarray
|
|
846
|
+
A 2D array with duplicate rows removed.
|
|
847
|
+
|
|
848
|
+
Raises
|
|
849
|
+
------
|
|
850
|
+
None
|
|
851
|
+
|
|
852
|
+
Notes
|
|
853
|
+
-----
|
|
854
|
+
This function uses NumPy's `np.unique` for performance and efficiency.
|
|
855
|
+
|
|
856
|
+
Examples
|
|
857
|
+
--------
|
|
858
|
+
>>> array1 = np.array([[1, 2], [3, 4], [1, 2]])
|
|
859
|
+
>>> result = remove_excedent_duplicates_coord(array1)
|
|
860
|
+
>>> print(result)
|
|
861
|
+
[[1 2]
|
|
862
|
+
[3 4]]
|
|
863
|
+
|
|
864
|
+
>>> array1 = np.array([[5, 6], [7, 8], [9, 0], [5, 6]])
|
|
865
|
+
>>> result = remove_excedent_duplicates_coord(array1)
|
|
866
|
+
>>> print(result)
|
|
867
|
+
[[5 6]
|
|
868
|
+
[7 8]
|
|
869
|
+
[9 0]]
|
|
870
|
+
"""
|
|
871
|
+
# np.unique(array1, axis=0)
|
|
872
|
+
unique_rows, inverse_indices = np.unique(array1, axis=0, return_inverse=True)
|
|
873
|
+
to_remove = []
|
|
874
|
+
seen_indices = []
|
|
875
|
+
for i in inverse_indices:
|
|
876
|
+
if i in seen_indices:
|
|
877
|
+
to_remove.append(True)
|
|
878
|
+
else:
|
|
879
|
+
to_remove.append(False)
|
|
880
|
+
seen_indices.append(i)
|
|
881
|
+
return np.delete(array1, to_remove, 0)
|