1
|
|
|
"""!
|
2
|
|
|
|
3
|
|
|
@brief Utils that are used by modules of pyclustering.
|
4
|
|
|
|
5
|
|
|
@authors Andrei Novikov ([email protected])
|
6
|
|
|
@date 2014-2018
|
7
|
|
|
@copyright GNU Public License
|
8
|
|
|
|
9
|
|
|
@cond GNU_PUBLIC_LICENSE
|
10
|
|
|
PyClustering is free software: you can redistribute it and/or modify
|
11
|
|
|
it under the terms of the GNU General Public License as published by
|
12
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
13
|
|
|
(at your option) any later version.
|
14
|
|
|
|
15
|
|
|
PyClustering is distributed in the hope that it will be useful,
|
16
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
|
|
GNU General Public License for more details.
|
19
|
|
|
|
20
|
|
|
You should have received a copy of the GNU General Public License
|
21
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
22
|
|
|
@endcond
|
23
|
|
|
|
24
|
|
|
"""
|
25
|
|
|
|
26
|
|
|
import time;
|
27
|
|
|
import numpy;
|
28
|
|
|
|
29
|
|
|
from PIL import Image;
|
30
|
|
|
from numpy import array;
|
31
|
|
|
|
32
|
|
|
import matplotlib.pyplot as plt;
|
33
|
|
|
from mpl_toolkits.mplot3d import Axes3D;
|
34
|
|
|
|
35
|
|
|
from sys import platform as _platform;
|
36
|
|
|
|
37
|
|
|
from pyclustering.utils.metric import distance_metric, type_metric;
|
38
|
|
|
|
39
|
|
|
|
40
|
|
|
## The number \f$pi\f$ is a mathematical constant, the ratio of a circle's circumference to its diameter.
|
41
|
|
|
pi = 3.1415926535;
|
42
|
|
|
|
43
|
|
|
|
44
|
|
|
def read_sample(filename):
|
45
|
|
|
"""!
|
46
|
|
|
@brief Returns data sample from simple text file.
|
47
|
|
|
@details This function should be used for text file with following format:
|
48
|
|
|
@code
|
49
|
|
|
point_1_coord_1 point_1_coord_2 ... point_1_coord_n
|
50
|
|
|
point_2_coord_1 point_2_coord_2 ... point_2_coord_n
|
51
|
|
|
... ...
|
52
|
|
|
@endcode
|
53
|
|
|
|
54
|
|
|
@param[in] filename (string): Path to file with data.
|
55
|
|
|
|
56
|
|
|
@return (list) Points where each point represented by list of coordinates.
|
57
|
|
|
|
58
|
|
|
"""
|
59
|
|
|
|
60
|
|
|
file = open(filename, 'r');
|
61
|
|
|
|
62
|
|
|
sample = [[float(val) for val in line.split()] for line in file if len(line.strip()) > 0];
|
63
|
|
|
|
64
|
|
|
file.close();
|
65
|
|
|
return sample;
|
66
|
|
|
|
67
|
|
|
|
68
|
|
|
def calculate_distance_matrix(sample):
|
69
|
|
|
"""!
|
70
|
|
|
@brief Calculates distance matrix for data sample (sequence of points) using Euclidean distance as a metric.
|
71
|
|
|
|
72
|
|
|
@param[in] sample (array_like): Data points that are used for distance calculation.
|
73
|
|
|
|
74
|
|
|
@return (list) Matrix distance between data points.
|
75
|
|
|
|
76
|
|
|
"""
|
77
|
|
|
|
78
|
|
|
amount_rows = len(sample);
|
79
|
|
|
return [ [ euclidean_distance(sample[i], sample[j]) for j in range(amount_rows) ] for i in range(amount_rows) ];
|
80
|
|
|
|
81
|
|
|
|
82
|
|
|
def read_image(filename):
|
83
|
|
|
"""!
|
84
|
|
|
@brief Returns image as N-dimension (depends on the input image) matrix, where one element of list describes pixel.
|
85
|
|
|
|
86
|
|
|
@param[in] filename (string): Path to image.
|
87
|
|
|
|
88
|
|
|
@return (list) Pixels where each pixel described by list of RGB-values.
|
89
|
|
|
|
90
|
|
|
"""
|
91
|
|
|
|
92
|
|
|
with Image.open(filename) as image_source:
|
93
|
|
|
data = [pixel for pixel in image_source.getdata()];
|
94
|
|
|
return data;
|
95
|
|
|
|
96
|
|
|
|
97
|
|
|
def rgb2gray(image_rgb_array):
|
98
|
|
|
"""!
|
99
|
|
|
@brief Returns image as 1-dimension (gray colored) matrix, where one element of list describes pixel.
|
100
|
|
|
@details Luma coding is used for transformation and that is calculated directly from gamma-compressed primary intensities as a weighted sum:
|
101
|
|
|
|
102
|
|
|
\f[Y = 0.2989R + 0.587G + 0.114B\f]
|
103
|
|
|
|
104
|
|
|
@param[in] image_rgb_array (list): Image represented by RGB list.
|
105
|
|
|
|
106
|
|
|
@return (list) Image as gray colored matrix, where one element of list describes pixel.
|
107
|
|
|
|
108
|
|
|
@code
|
109
|
|
|
colored_image = read_image(file_name);
|
110
|
|
|
gray_image = rgb2gray(colored_image);
|
111
|
|
|
@endcode
|
112
|
|
|
|
113
|
|
|
@see read_image()
|
114
|
|
|
|
115
|
|
|
"""
|
116
|
|
|
|
117
|
|
|
image_gray_array = [0.0] * len(image_rgb_array);
|
118
|
|
|
for index in range(0, len(image_rgb_array), 1):
|
119
|
|
|
image_gray_array[index] = float(image_rgb_array[index][0]) * 0.2989 + float(image_rgb_array[index][1]) * 0.5870 + float(image_rgb_array[index][2]) * 0.1140;
|
120
|
|
|
|
121
|
|
|
return image_gray_array;
|
122
|
|
|
|
123
|
|
|
|
124
|
|
|
def stretch_pattern(image_source):
|
125
|
|
|
"""!
|
126
|
|
|
@brief Returns stretched content as 1-dimension (gray colored) matrix with size of input image.
|
127
|
|
|
|
128
|
|
|
@param[in] image_source (Image): PIL Image instance.
|
129
|
|
|
|
130
|
|
|
@return (list, Image) Stretched image as gray colored matrix and source image.
|
131
|
|
|
|
132
|
|
|
"""
|
133
|
|
|
wsize, hsize = image_source.size;
|
134
|
|
|
|
135
|
|
|
# Crop digit exactly
|
136
|
|
|
(ws, hs, we, he) = gray_pattern_borders(image_source);
|
137
|
|
|
image_source = image_source.crop((ws, hs, we, he));
|
138
|
|
|
|
139
|
|
|
# Stretch it to initial sizes
|
140
|
|
|
image_source = image_source.resize((wsize, hsize), Image.ANTIALIAS);
|
141
|
|
|
|
142
|
|
|
# Transform image to simple array
|
143
|
|
|
data = [pixel for pixel in image_source.getdata()];
|
144
|
|
|
image_pattern = rgb2gray(data);
|
145
|
|
|
|
146
|
|
|
return (image_pattern, image_source);
|
147
|
|
|
|
148
|
|
|
|
149
|
|
|
def gray_pattern_borders(image):
|
150
|
|
|
"""!
|
151
|
|
|
@brief Returns coordinates of gray image content on the input image.
|
152
|
|
|
|
153
|
|
|
@param[in] image (Image): PIL Image instance that is processed.
|
154
|
|
|
|
155
|
|
|
@return (tuple) Returns coordinates of gray image content as (width_start, height_start, width_end, height_end).
|
156
|
|
|
|
157
|
|
|
"""
|
158
|
|
|
|
159
|
|
|
width, height = image.size;
|
160
|
|
|
|
161
|
|
|
width_start = width;
|
162
|
|
|
width_end = 0;
|
163
|
|
|
height_start = height;
|
164
|
|
|
height_end = 0;
|
165
|
|
|
|
166
|
|
|
row, col = 0, 0;
|
167
|
|
|
for pixel in image.getdata():
|
168
|
|
|
value = float(pixel[0]) * 0.2989 + float(pixel[1]) * 0.5870 + float(pixel[2]) * 0.1140;
|
169
|
|
|
|
170
|
|
|
if (value < 128):
|
171
|
|
|
if (width_end < col):
|
172
|
|
|
width_end = col;
|
173
|
|
|
|
174
|
|
|
if (height_end < row):
|
175
|
|
|
height_end = row;
|
176
|
|
|
|
177
|
|
|
if (width_start > col):
|
178
|
|
|
width_start = col;
|
179
|
|
|
|
180
|
|
|
if (height_start > row):
|
181
|
|
|
height_start = row;
|
182
|
|
|
|
183
|
|
|
col += 1;
|
184
|
|
|
if (col >= width):
|
185
|
|
|
col = 0;
|
186
|
|
|
row += 1;
|
187
|
|
|
|
188
|
|
|
return (width_start, height_start, width_end + 1, height_end + 1);
|
189
|
|
|
|
190
|
|
|
|
191
|
|
|
def average_neighbor_distance(points, num_neigh):
|
192
|
|
|
"""!
|
193
|
|
|
@brief Returns average distance for establish links between specified number of nearest neighbors.
|
194
|
|
|
|
195
|
|
|
@param[in] points (list): Input data, list of points where each point represented by list.
|
196
|
|
|
@param[in] num_neigh (uint): Number of neighbors that should be used for distance calculation.
|
197
|
|
|
|
198
|
|
|
@return (double) Average distance for establish links between 'num_neigh' in data set 'points'.
|
199
|
|
|
|
200
|
|
|
"""
|
201
|
|
|
|
202
|
|
|
if num_neigh > len(points) - 1:
|
203
|
|
|
raise NameError('Impossible to calculate average distance to neighbors when number of object is less than number of neighbors.');
|
204
|
|
|
|
205
|
|
|
dist_matrix = [ [ 0.0 for i in range(len(points)) ] for j in range(len(points)) ];
|
206
|
|
|
for i in range(0, len(points), 1):
|
207
|
|
|
for j in range(i + 1, len(points), 1):
|
208
|
|
|
distance = euclidean_distance(points[i], points[j]);
|
209
|
|
|
dist_matrix[i][j] = distance;
|
210
|
|
|
dist_matrix[j][i] = distance;
|
211
|
|
|
|
212
|
|
|
dist_matrix[i] = sorted(dist_matrix[i]);
|
213
|
|
|
|
214
|
|
|
total_distance = 0;
|
215
|
|
|
for i in range(0, len(points), 1):
|
216
|
|
|
# start from 0 - first element is distance to itself.
|
217
|
|
|
for j in range(0, num_neigh, 1):
|
218
|
|
|
total_distance += dist_matrix[i][j + 1];
|
219
|
|
|
|
220
|
|
|
return ( total_distance / (num_neigh * len(points)) );
|
221
|
|
|
|
222
|
|
|
|
223
|
|
|
def centroid(points, indexes = None):
|
224
|
|
|
"""!
|
225
|
|
|
@brief Calculate centroid of input set of points.
|
226
|
|
|
|
227
|
|
|
@param[in] points (list): Set of points for centroid calculation.
|
228
|
|
|
@param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during centroid calculation.
|
229
|
|
|
|
230
|
|
|
@return (list) centroid on the set of points where each element of list is corresponding to value in its dimension.
|
231
|
|
|
|
232
|
|
|
"""
|
233
|
|
|
|
234
|
|
|
dimension = len(points[0]);
|
235
|
|
|
centroid_value = [0.0] * dimension;
|
236
|
|
|
|
237
|
|
|
range_points = None;
|
238
|
|
|
if indexes is None:
|
239
|
|
|
range_points = range(len(points));
|
240
|
|
|
else:
|
241
|
|
|
range_points = indexes;
|
242
|
|
|
|
243
|
|
|
for index_point in range_points:
|
244
|
|
|
centroid_value = list_math_addition(centroid_value, points[index_point]);
|
245
|
|
|
|
246
|
|
|
centroid_value = list_math_division_number(centroid_value, len(range_points));
|
247
|
|
|
return centroid_value;
|
248
|
|
|
|
249
|
|
|
|
250
|
|
|
def median(data, indexes = None, **kwargs):
|
251
|
|
|
"""!
|
252
|
|
|
@brief Calculate geometric median of input set of points using Euclidean distance.
|
253
|
|
|
|
254
|
|
|
@param[in] data (list): Set of points for median calculation.
|
255
|
|
|
@param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during median calculation.
|
256
|
|
|
@param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric', 'data_type').
|
257
|
|
|
|
258
|
|
|
<b>Keyword Args:</b><br>
|
259
|
|
|
- metric (distance_metric): Metric that is used for distance calculation between two points.
|
260
|
|
|
- data_type (string): Data type of input sample 'data' (available values: 'points', 'distance_matrix').
|
261
|
|
|
|
262
|
|
|
@return (uint) index of point in input set that corresponds to median.
|
263
|
|
|
|
264
|
|
|
"""
|
265
|
|
|
|
266
|
|
|
index_median = None;
|
267
|
|
|
distance = float('Inf');
|
268
|
|
|
|
269
|
|
|
metric = kwargs.get('metric', type_metric.EUCLIDEAN_SQUARE);
|
270
|
|
|
data_type = kwargs.get('data_type', 'points');
|
271
|
|
|
|
272
|
|
|
if data_type == 'points':
|
273
|
|
|
calculator = lambda index1, index2: metric(data[index1], data[index2]);
|
274
|
|
|
elif data_type == 'distance_matrix':
|
275
|
|
|
if isinstance(data, numpy.matrix):
|
276
|
|
|
calculator = lambda index1, index2: data.item(index1, index2);
|
277
|
|
|
|
278
|
|
|
else:
|
279
|
|
|
calculator = lambda index1, index2: data[index1][index2];
|
280
|
|
|
else:
|
281
|
|
|
raise TypeError("Unknown type of data is specified '%s'." % data_type);
|
282
|
|
|
|
283
|
|
|
if indexes is None:
|
284
|
|
|
range_points = range(len(data));
|
285
|
|
|
else:
|
286
|
|
|
range_points = indexes;
|
287
|
|
|
|
288
|
|
|
for index_candidate in range_points:
|
289
|
|
|
distance_candidate = 0.0;
|
290
|
|
|
for index in range_points:
|
291
|
|
|
distance_candidate += calculator(index_candidate, index);
|
292
|
|
|
|
293
|
|
|
if distance_candidate < distance:
|
294
|
|
|
distance = distance_candidate;
|
295
|
|
|
index_median = index_candidate;
|
296
|
|
|
|
297
|
|
|
return index_median;
|
298
|
|
|
|
299
|
|
|
|
300
|
|
|
def euclidean_distance(a, b):
|
301
|
|
|
"""!
|
|
|
|
|
302
|
|
|
@brief Calculate Euclidean distance between vector a and b.
|
303
|
|
|
@details The Euclidean between vectors (points) a and b is calculated by following formula:
|
304
|
|
|
|
305
|
|
|
\f[
|
306
|
|
|
dist(a, b) = \sqrt{ \sum_{i=0}^{N}(b_{i} - a_{i})^{2}) };
|
307
|
|
|
\f]
|
308
|
|
|
|
309
|
|
|
Where N is a length of each vector.
|
310
|
|
|
|
311
|
|
|
@param[in] a (list): The first vector.
|
312
|
|
|
@param[in] b (list): The second vector.
|
313
|
|
|
|
314
|
|
|
@return (double) Euclidian distance between two vectors.
|
315
|
|
|
|
316
|
|
|
@note This function for calculation is faster then standard function in ~100 times!
|
317
|
|
|
|
318
|
|
|
"""
|
319
|
|
|
|
320
|
|
|
distance = euclidean_distance_square(a, b);
|
321
|
|
|
return distance**(0.5);
|
322
|
|
|
|
323
|
|
|
|
324
|
|
|
def euclidean_distance_square(a, b):
|
325
|
|
|
"""!
|
326
|
|
|
@brief Calculate square Euclidian distance between vector a and b.
|
327
|
|
|
|
328
|
|
|
@param[in] a (list): The first vector.
|
329
|
|
|
@param[in] b (list): The second vector.
|
330
|
|
|
|
331
|
|
|
@return (double) Square Euclidian distance between two vectors.
|
332
|
|
|
|
333
|
|
|
"""
|
334
|
|
|
|
335
|
|
|
if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
|
336
|
|
|
return (a - b)**2.0;
|
337
|
|
|
|
338
|
|
|
distance = 0.0;
|
339
|
|
|
for i in range(0, len(a)):
|
340
|
|
|
distance += (a[i] - b[i])**2.0;
|
341
|
|
|
|
342
|
|
|
return distance;
|
343
|
|
|
|
344
|
|
|
|
345
|
|
|
def manhattan_distance(a, b):
|
346
|
|
|
"""!
|
347
|
|
|
@brief Calculate Manhattan distance between vector a and b.
|
348
|
|
|
|
349
|
|
|
@param[in] a (list): The first cluster.
|
350
|
|
|
@param[in] b (list): The second cluster.
|
351
|
|
|
|
352
|
|
|
@return (double) Manhattan distance between two vectors.
|
353
|
|
|
|
354
|
|
|
"""
|
355
|
|
|
|
356
|
|
|
if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
|
357
|
|
|
return abs(a - b);
|
358
|
|
|
|
359
|
|
|
distance = 0.0;
|
360
|
|
|
dimension = len(a);
|
361
|
|
|
|
362
|
|
|
for i in range(0, dimension):
|
363
|
|
|
distance += abs(a[i] - b[i]);
|
364
|
|
|
|
365
|
|
|
return distance;
|
366
|
|
|
|
367
|
|
|
|
368
|
|
|
def average_inter_cluster_distance(cluster1, cluster2, data = None):
|
369
|
|
|
"""!
|
370
|
|
|
@brief Calculates average inter-cluster distance between two clusters.
|
371
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
372
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
373
|
|
|
data should be specified.
|
374
|
|
|
|
375
|
|
|
@param[in] cluster1 (list): The first cluster where each element can represent index from the data or object itself.
|
376
|
|
|
@param[in] cluster2 (list): The second cluster where each element can represent index from the data or object itself.
|
377
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
378
|
|
|
otherwise elements of cluster will be considered as points.
|
379
|
|
|
|
380
|
|
|
@return (double) Average inter-cluster distance between two clusters.
|
381
|
|
|
|
382
|
|
|
"""
|
383
|
|
|
|
384
|
|
|
distance = 0.0;
|
385
|
|
|
|
386
|
|
|
if (data is None):
|
387
|
|
|
for i in range(len(cluster1)):
|
388
|
|
|
for j in range(len(cluster2)):
|
389
|
|
|
distance += euclidean_distance_square(cluster1[i], cluster2[j]);
|
390
|
|
|
else:
|
391
|
|
|
for i in range(len(cluster1)):
|
392
|
|
|
for j in range(len(cluster2)):
|
393
|
|
|
distance += euclidean_distance_square(data[ cluster1[i]], data[ cluster2[j]]);
|
394
|
|
|
|
395
|
|
|
distance /= float(len(cluster1) * len(cluster2));
|
396
|
|
|
return distance ** 0.5;
|
397
|
|
|
|
398
|
|
|
|
399
|
|
|
def average_intra_cluster_distance(cluster1, cluster2, data=None):
|
400
|
|
|
"""!
|
401
|
|
|
@brief Calculates average intra-cluster distance between two clusters.
|
402
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
403
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
404
|
|
|
data should be specified.
|
405
|
|
|
|
406
|
|
|
@param[in] cluster1 (list): The first cluster.
|
407
|
|
|
@param[in] cluster2 (list): The second cluster.
|
408
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
409
|
|
|
otherwise elements of cluster will be considered as points.
|
410
|
|
|
|
411
|
|
|
@return (double) Average intra-cluster distance between two clusters.
|
412
|
|
|
|
413
|
|
|
"""
|
414
|
|
|
|
415
|
|
|
distance = 0.0
|
416
|
|
|
|
417
|
|
|
for i in range(len(cluster1) + len(cluster2)):
|
418
|
|
|
for j in range(len(cluster1) + len(cluster2)):
|
419
|
|
|
if data is None:
|
420
|
|
|
# the first point
|
421
|
|
|
if i < len(cluster1):
|
422
|
|
|
first_point = cluster1[i]
|
423
|
|
|
else:
|
424
|
|
|
first_point = cluster2[i - len(cluster1)]
|
425
|
|
|
|
426
|
|
|
# the second point
|
427
|
|
|
if j < len(cluster1):
|
428
|
|
|
second_point = cluster1[j]
|
429
|
|
|
else:
|
430
|
|
|
second_point = cluster2[j - len(cluster1)]
|
431
|
|
|
|
432
|
|
|
else:
|
433
|
|
|
# the first point
|
434
|
|
|
if i < len(cluster1):
|
435
|
|
|
first_point = data[cluster1[i]]
|
436
|
|
|
else:
|
437
|
|
|
first_point = data[cluster2[i - len(cluster1)]]
|
438
|
|
|
|
439
|
|
|
if j < len(cluster1):
|
440
|
|
|
second_point = data[cluster1[j]]
|
441
|
|
|
else:
|
442
|
|
|
second_point = data[cluster2[j - len(cluster1)]]
|
443
|
|
|
|
444
|
|
|
distance += euclidean_distance_square(first_point, second_point)
|
445
|
|
|
|
446
|
|
|
distance /= float((len(cluster1) + len(cluster2)) * (len(cluster1) + len(cluster2) - 1.0))
|
447
|
|
|
return distance ** 0.5
|
448
|
|
|
|
449
|
|
|
|
450
|
|
|
def variance_increase_distance(cluster1, cluster2, data = None):
|
451
|
|
|
"""!
|
452
|
|
|
@brief Calculates variance increase distance between two clusters.
|
453
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
454
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
455
|
|
|
data should be specified.
|
456
|
|
|
|
457
|
|
|
@param[in] cluster1 (list): The first cluster.
|
458
|
|
|
@param[in] cluster2 (list): The second cluster.
|
459
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
460
|
|
|
otherwise elements of cluster will be considered as points.
|
461
|
|
|
|
462
|
|
|
@return (double) Average variance increase distance between two clusters.
|
463
|
|
|
|
464
|
|
|
"""
|
465
|
|
|
|
466
|
|
|
# calculate local sum
|
467
|
|
|
if data is None:
|
468
|
|
|
member_cluster1 = [0.0] * len(cluster1[0])
|
469
|
|
|
member_cluster2 = [0.0] * len(cluster2[0])
|
470
|
|
|
|
471
|
|
|
else:
|
472
|
|
|
member_cluster1 = [0.0] * len(data[0])
|
473
|
|
|
member_cluster2 = [0.0] * len(data[0])
|
474
|
|
|
|
475
|
|
|
for i in range(len(cluster1)):
|
476
|
|
|
if data is None:
|
477
|
|
|
member_cluster1 = list_math_addition(member_cluster1, cluster1[i])
|
478
|
|
|
else:
|
479
|
|
|
member_cluster1 = list_math_addition(member_cluster1, data[ cluster1[i] ])
|
480
|
|
|
|
481
|
|
|
for j in range(len(cluster2)):
|
482
|
|
|
if data is None:
|
483
|
|
|
member_cluster2 = list_math_addition(member_cluster2, cluster2[j])
|
484
|
|
|
else:
|
485
|
|
|
member_cluster2 = list_math_addition(member_cluster2, data[ cluster2[j] ])
|
486
|
|
|
|
487
|
|
|
member_cluster_general = list_math_addition(member_cluster1, member_cluster2)
|
488
|
|
|
member_cluster_general = list_math_division_number(member_cluster_general, len(cluster1) + len(cluster2))
|
489
|
|
|
|
490
|
|
|
member_cluster1 = list_math_division_number(member_cluster1, len(cluster1))
|
491
|
|
|
member_cluster2 = list_math_division_number(member_cluster2, len(cluster2))
|
492
|
|
|
|
493
|
|
|
# calculate global sum
|
494
|
|
|
distance_general = 0.0
|
495
|
|
|
distance_cluster1 = 0.0
|
496
|
|
|
distance_cluster2 = 0.0
|
497
|
|
|
|
498
|
|
|
for i in range(len(cluster1)):
|
499
|
|
|
if data is None:
|
500
|
|
|
distance_cluster1 += euclidean_distance_square(cluster1[i], member_cluster1)
|
501
|
|
|
distance_general += euclidean_distance_square(cluster1[i], member_cluster_general)
|
502
|
|
|
|
503
|
|
|
else:
|
504
|
|
|
distance_cluster1 += euclidean_distance_square(data[ cluster1[i]], member_cluster1)
|
505
|
|
|
distance_general += euclidean_distance_square(data[ cluster1[i]], member_cluster_general)
|
506
|
|
|
|
507
|
|
|
for j in range(len(cluster2)):
|
508
|
|
|
if data is None:
|
509
|
|
|
distance_cluster2 += euclidean_distance_square(cluster2[j], member_cluster2)
|
510
|
|
|
distance_general += euclidean_distance_square(cluster2[j], member_cluster_general)
|
511
|
|
|
|
512
|
|
|
else:
|
513
|
|
|
distance_cluster2 += euclidean_distance_square(data[ cluster2[j]], member_cluster2)
|
514
|
|
|
distance_general += euclidean_distance_square(data[ cluster2[j]], member_cluster_general)
|
515
|
|
|
|
516
|
|
|
return distance_general - distance_cluster1 - distance_cluster2
|
517
|
|
|
|
518
|
|
|
|
519
|
|
|
def calculate_ellipse_description(covariance, scale = 2.0):
|
520
|
|
|
"""!
|
521
|
|
|
@brief Calculates description of ellipse using covariance matrix.
|
522
|
|
|
|
523
|
|
|
@param[in] covariance (numpy.array): Covariance matrix for which ellipse area should be calculated.
|
524
|
|
|
@param[in] scale (float): Scale of the ellipse.
|
525
|
|
|
|
526
|
|
|
@return (float, float, float) Return ellipse description: angle, width, height.
|
527
|
|
|
|
528
|
|
|
"""
|
529
|
|
|
|
530
|
|
|
eigh_values, eigh_vectors = numpy.linalg.eigh(covariance)
|
531
|
|
|
order = eigh_values.argsort()[::-1]
|
532
|
|
|
|
533
|
|
|
values, vectors = eigh_values[order], eigh_vectors[order]
|
534
|
|
|
angle = numpy.degrees(numpy.arctan2(*vectors[:,0][::-1]))
|
535
|
|
|
|
536
|
|
|
if 0.0 in values:
|
537
|
|
|
return 0, 0, 0
|
538
|
|
|
|
539
|
|
|
width, height = 2.0 * scale * numpy.sqrt(values)
|
540
|
|
|
return angle, width, height
|
541
|
|
|
|
542
|
|
|
|
543
|
|
|
def data_corners(data, data_filter = None):
|
544
|
|
|
"""!
|
545
|
|
|
@brief Finds maximum and minimum corner in each dimension of the specified data.
|
546
|
|
|
|
547
|
|
|
@param[in] data (list): List of points that should be analysed.
|
548
|
|
|
@param[in] data_filter (list): List of indexes of the data that should be analysed,
|
549
|
|
|
if it is 'None' then whole 'data' is analysed to obtain corners.
|
550
|
|
|
|
551
|
|
|
@return (list) Tuple of two points that corresponds to minimum and maximum corner (min_corner, max_corner).
|
552
|
|
|
|
553
|
|
|
"""
|
554
|
|
|
|
555
|
|
|
dimensions = len(data[0])
|
556
|
|
|
|
557
|
|
|
bypass = data_filter
|
558
|
|
|
if bypass is None:
|
559
|
|
|
bypass = range(len(data))
|
560
|
|
|
|
561
|
|
|
maximum_corner = data[bypass[0]][:]
|
562
|
|
|
minimum_corner = data[bypass[0]][:]
|
563
|
|
|
|
564
|
|
|
for index_point in bypass:
|
565
|
|
|
for index_dimension in range(dimensions):
|
566
|
|
|
if data[index_point][index_dimension] > maximum_corner[index_dimension]:
|
567
|
|
|
maximum_corner[index_dimension] = data[index_point][index_dimension]
|
568
|
|
|
|
569
|
|
|
if data[index_point][index_dimension] < minimum_corner[index_dimension]:
|
570
|
|
|
minimum_corner[index_dimension] = data[index_point][index_dimension]
|
571
|
|
|
|
572
|
|
|
return minimum_corner, maximum_corner
|
573
|
|
|
|
574
|
|
|
|
575
|
|
|
def norm_vector(vector):
|
576
|
|
|
"""!
|
577
|
|
|
@brief Calculates norm of an input vector that is known as a vector length.
|
578
|
|
|
|
579
|
|
|
@param[in] vector (list): The input vector whose length is calculated.
|
580
|
|
|
|
581
|
|
|
@return (double) vector norm known as vector length.
|
582
|
|
|
|
583
|
|
|
"""
|
584
|
|
|
|
585
|
|
|
length = 0.0
|
586
|
|
|
for component in vector:
|
587
|
|
|
length += component * component
|
588
|
|
|
|
589
|
|
|
length = length ** 0.5
|
590
|
|
|
|
591
|
|
|
return length
|
592
|
|
|
|
593
|
|
|
|
594
|
|
|
def unit_vector(vector):
|
595
|
|
|
"""!
|
596
|
|
|
@brief Calculates unit vector.
|
597
|
|
|
@details Unit vector calculates of an input vector involves two steps.
|
598
|
|
|
The first, calculate vector length. The second,
|
599
|
|
|
divide each vector component by the obtained length.
|
600
|
|
|
|
601
|
|
|
@param[in] vector (list): The input vector that is used for unit vector calculation.
|
602
|
|
|
|
603
|
|
|
"""
|
604
|
|
|
|
605
|
|
|
length = norm_vector(vector);
|
606
|
|
|
unit_vector_instance = [];
|
607
|
|
|
|
608
|
|
|
for component in vector:
|
609
|
|
|
unit_vector_instance.append(component / length);
|
610
|
|
|
|
611
|
|
|
return unit_vector_instance;
|
612
|
|
|
|
613
|
|
|
|
614
|
|
|
def heaviside(value):
|
615
|
|
|
"""!
|
616
|
|
|
@brief Calculates Heaviside function that represents step function.
|
617
|
|
|
@details If input value is greater than 0 then returns 1, otherwise returns 0.
|
618
|
|
|
|
619
|
|
|
@param[in] value (double): Argument of Heaviside function.
|
620
|
|
|
|
621
|
|
|
@return (double) Value of Heaviside function.
|
622
|
|
|
|
623
|
|
|
"""
|
624
|
|
|
if (value > 0.0):
|
625
|
|
|
return 1.0;
|
626
|
|
|
|
627
|
|
|
return 0.0;
|
628
|
|
|
|
629
|
|
|
|
630
|
|
|
def timedcall(executable_function, *args):
|
631
|
|
|
"""!
|
632
|
|
|
@brief Executes specified method or function with measuring of execution time.
|
633
|
|
|
|
634
|
|
|
@param[in] executable_function (pointer): Pointer to function or method.
|
635
|
|
|
@param[in] args (*): Arguments of called function or method.
|
636
|
|
|
|
637
|
|
|
@return (tuple) Execution time and result of execution of function or method (execution_time, result_execution).
|
638
|
|
|
|
639
|
|
|
"""
|
640
|
|
|
|
641
|
|
|
time_start = time.clock();
|
642
|
|
|
result = executable_function(*args);
|
643
|
|
|
time_end = time.clock();
|
644
|
|
|
|
645
|
|
|
return (time_end - time_start, result);
|
646
|
|
|
|
647
|
|
|
|
648
|
|
|
def extract_number_oscillations(osc_dyn, index = 0, amplitude_threshold = 1.0):
|
649
|
|
|
"""!
|
650
|
|
|
@brief Extracts number of oscillations of specified oscillator.
|
651
|
|
|
|
652
|
|
|
@param[in] osc_dyn (list): Dynamic of oscillators.
|
653
|
|
|
@param[in] index (uint): Index of oscillator in dynamic.
|
654
|
|
|
@param[in] amplitude_threshold (double): Amplitude threshold when oscillation is taken into account, for example,
|
655
|
|
|
when oscillator amplitude is greater than threshold then oscillation is incremented.
|
656
|
|
|
|
657
|
|
|
@return (uint) Number of oscillations of specified oscillator.
|
658
|
|
|
|
659
|
|
|
"""
|
660
|
|
|
|
661
|
|
|
number_oscillations = 0;
|
662
|
|
|
waiting_differential = False;
|
663
|
|
|
threshold_passed = False;
|
664
|
|
|
high_level_trigger = True if (osc_dyn[0][index] > amplitude_threshold) else False;
|
665
|
|
|
|
666
|
|
|
for values in osc_dyn:
|
667
|
|
|
if ( (values[index] >= amplitude_threshold) and (high_level_trigger is False) ):
|
668
|
|
|
high_level_trigger = True;
|
669
|
|
|
threshold_passed = True;
|
670
|
|
|
|
671
|
|
|
elif ( (values[index] < amplitude_threshold) and (high_level_trigger is True) ):
|
672
|
|
|
high_level_trigger = False;
|
673
|
|
|
threshold_passed = True;
|
674
|
|
|
|
675
|
|
|
if (threshold_passed is True):
|
676
|
|
|
threshold_passed = False;
|
677
|
|
|
if (waiting_differential is True and high_level_trigger is False):
|
678
|
|
|
number_oscillations += 1;
|
679
|
|
|
waiting_differential = False;
|
680
|
|
|
|
681
|
|
|
else:
|
682
|
|
|
waiting_differential = True;
|
683
|
|
|
|
684
|
|
|
return number_oscillations;
|
685
|
|
|
|
686
|
|
|
|
687
|
|
|
def allocate_sync_ensembles(dynamic, tolerance = 0.1, threshold = 1.0, ignore = None):
|
688
|
|
|
"""!
|
689
|
|
|
@brief Allocate clusters in line with ensembles of synchronous oscillators where each
|
690
|
|
|
synchronous ensemble corresponds to only one cluster.
|
691
|
|
|
|
692
|
|
|
@param[in] dynamic (dynamic): Dynamic of each oscillator.
|
693
|
|
|
@param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
|
694
|
|
|
@param[in] threshold (double): Amlitude trigger when spike is taken into account.
|
695
|
|
|
@param[in] ignore (bool): Set of indexes that shouldn't be taken into account.
|
696
|
|
|
|
697
|
|
|
@return (list) Grours (lists) of indexes of synchronous oscillators, for example,
|
698
|
|
|
[ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
|
699
|
|
|
|
700
|
|
|
"""
|
701
|
|
|
|
702
|
|
|
descriptors = [ [] for _ in range(len(dynamic[0])) ];
|
703
|
|
|
|
704
|
|
|
# Check from the end for obtaining result
|
705
|
|
|
for index_dyn in range(0, len(dynamic[0]), 1):
|
706
|
|
|
if ((ignore is not None) and (index_dyn in ignore)):
|
707
|
|
|
continue;
|
708
|
|
|
|
709
|
|
|
time_stop_simulation = len(dynamic) - 1;
|
710
|
|
|
active_state = False;
|
711
|
|
|
|
712
|
|
|
if (dynamic[time_stop_simulation][index_dyn] > threshold):
|
713
|
|
|
active_state = True;
|
714
|
|
|
|
715
|
|
|
# if active state is detected, it means we don't have whole oscillatory period for the considered oscillator, should be skipped.
|
716
|
|
|
if (active_state is True):
|
717
|
|
|
while ( (dynamic[time_stop_simulation][index_dyn] > threshold) and (time_stop_simulation > 0) ):
|
718
|
|
|
time_stop_simulation -= 1;
|
719
|
|
|
|
720
|
|
|
# if there are no any oscillation than let's consider it like noise
|
721
|
|
|
if (time_stop_simulation == 0):
|
722
|
|
|
continue;
|
723
|
|
|
|
724
|
|
|
# reset
|
725
|
|
|
active_state = False;
|
726
|
|
|
|
727
|
|
|
desc = [0, 0, 0]; # end, start, average time of oscillation
|
728
|
|
|
for t in range(time_stop_simulation, 0, -1):
|
729
|
|
|
if ( (dynamic[t][index_dyn] > threshold) and (active_state is False) ):
|
730
|
|
|
desc[0] = t;
|
731
|
|
|
active_state = True;
|
732
|
|
|
elif ( (dynamic[t][index_dyn] < threshold) and (active_state is True) ):
|
733
|
|
|
desc[1] = t;
|
734
|
|
|
active_state = False;
|
735
|
|
|
|
736
|
|
|
break;
|
737
|
|
|
|
738
|
|
|
if (desc == [0, 0, 0]):
|
739
|
|
|
continue;
|
740
|
|
|
|
741
|
|
|
desc[2] = desc[1] + (desc[0] - desc[1]) / 2.0;
|
742
|
|
|
descriptors[index_dyn] = desc;
|
743
|
|
|
|
744
|
|
|
|
745
|
|
|
# Cluster allocation
|
746
|
|
|
sync_ensembles = [];
|
747
|
|
|
desc_sync_ensembles = [];
|
748
|
|
|
|
749
|
|
|
for index_desc in range(0, len(descriptors), 1):
|
750
|
|
|
if (descriptors[index_desc] == []):
|
751
|
|
|
continue;
|
752
|
|
|
|
753
|
|
|
if (len(sync_ensembles) == 0):
|
754
|
|
|
desc_ensemble = descriptors[index_desc];
|
755
|
|
|
reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
|
756
|
|
|
|
757
|
|
|
desc_ensemble[0] = desc_ensemble[2] + reducer;
|
758
|
|
|
desc_ensemble[1] = desc_ensemble[2] - reducer;
|
759
|
|
|
|
760
|
|
|
desc_sync_ensembles.append(desc_ensemble);
|
761
|
|
|
sync_ensembles.append([ index_desc ]);
|
762
|
|
|
else:
|
763
|
|
|
oscillator_captured = False;
|
764
|
|
|
for index_ensemble in range(0, len(sync_ensembles), 1):
|
765
|
|
|
if ( (desc_sync_ensembles[index_ensemble][0] > descriptors[index_desc][2]) and (desc_sync_ensembles[index_ensemble][1] < descriptors[index_desc][2])):
|
766
|
|
|
sync_ensembles[index_ensemble].append(index_desc);
|
767
|
|
|
oscillator_captured = True;
|
768
|
|
|
break;
|
769
|
|
|
|
770
|
|
|
if (oscillator_captured is False):
|
771
|
|
|
desc_ensemble = descriptors[index_desc];
|
772
|
|
|
reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
|
773
|
|
|
|
774
|
|
|
desc_ensemble[0] = desc_ensemble[2] + reducer;
|
775
|
|
|
desc_ensemble[1] = desc_ensemble[2] - reducer;
|
776
|
|
|
|
777
|
|
|
desc_sync_ensembles.append(desc_ensemble);
|
778
|
|
|
sync_ensembles.append([ index_desc ]);
|
779
|
|
|
|
780
|
|
|
return sync_ensembles;
|
781
|
|
|
|
782
|
|
|
|
783
|
|
|
def draw_clusters(data, clusters, noise = [], marker_descr = '.', hide_axes = False, axes = None, display_result = True):
|
784
|
|
|
"""!
|
785
|
|
|
@brief Displays clusters for data in 2D or 3D.
|
786
|
|
|
|
787
|
|
|
@param[in] data (list): Points that are described by coordinates represented.
|
788
|
|
|
@param[in] clusters (list): Clusters that are represented by lists of indexes where each index corresponds to point in data.
|
789
|
|
|
@param[in] noise (list): Points that are regarded to noise.
|
790
|
|
|
@param[in] marker_descr (string): Marker for displaying points.
|
791
|
|
|
@param[in] hide_axes (bool): If True - axes is not displayed.
|
792
|
|
|
@param[in] axes (ax) Matplotlib axes where clusters should be drawn, if it is not specified (None) then new plot will be created.
|
793
|
|
|
@param[in] display_result (bool): If specified then matplotlib axes will be used for drawing and plot will not be shown.
|
794
|
|
|
|
795
|
|
|
@return (ax) Matplotlib axes where drawn clusters are presented.
|
796
|
|
|
|
797
|
|
|
"""
|
798
|
|
|
# Get dimension
|
799
|
|
|
dimension = 0;
|
800
|
|
|
if ( (data is not None) and (clusters is not None) ):
|
801
|
|
|
dimension = len(data[0]);
|
802
|
|
|
elif ( (data is None) and (clusters is not None) ):
|
803
|
|
|
dimension = len(clusters[0][0]);
|
804
|
|
|
else:
|
805
|
|
|
raise NameError('Data or clusters should be specified exactly.');
|
806
|
|
|
|
807
|
|
|
"Draw clusters"
|
808
|
|
|
colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet',
|
809
|
|
|
'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
|
810
|
|
|
'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
|
811
|
|
|
'silver', 'lime', 'pink', 'gold', 'bisque' ];
|
812
|
|
|
|
813
|
|
|
if (len(clusters) > len(colors)):
|
814
|
|
|
raise NameError('Impossible to represent clusters due to number of specified colors.');
|
815
|
|
|
|
816
|
|
|
fig = plt.figure();
|
817
|
|
|
|
818
|
|
|
if (axes is None):
|
819
|
|
|
# Check for dimensions
|
820
|
|
|
if ((dimension) == 1 or (dimension == 2)):
|
821
|
|
|
axes = fig.add_subplot(111);
|
822
|
|
|
elif (dimension == 3):
|
823
|
|
|
axes = fig.gca(projection='3d');
|
824
|
|
|
else:
|
825
|
|
|
raise NameError('Drawer supports only 2d and 3d data representation');
|
826
|
|
|
|
827
|
|
|
color_index = 0;
|
828
|
|
|
for cluster in clusters:
|
829
|
|
|
color = colors[color_index];
|
830
|
|
|
for item in cluster:
|
831
|
|
|
if (dimension == 1):
|
832
|
|
|
if (data is None):
|
833
|
|
|
axes.plot(item[0], 0.0, color = color, marker = marker_descr);
|
834
|
|
|
else:
|
835
|
|
|
axes.plot(data[item][0], 0.0, color = color, marker = marker_descr);
|
836
|
|
|
|
837
|
|
|
if (dimension == 2):
|
838
|
|
|
if (data is None):
|
839
|
|
|
axes.plot(item[0], item[1], color = color, marker = marker_descr);
|
840
|
|
|
else:
|
841
|
|
|
axes.plot(data[item][0], data[item][1], color = color, marker = marker_descr);
|
842
|
|
|
|
843
|
|
|
elif (dimension == 3):
|
844
|
|
|
if (data is None):
|
845
|
|
|
axes.scatter(item[0], item[1], item[2], c = color, marker = marker_descr);
|
846
|
|
|
else:
|
847
|
|
|
axes.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker_descr);
|
848
|
|
View Code Duplication |
|
|
|
|
|
849
|
|
|
color_index += 1;
|
850
|
|
|
|
851
|
|
|
for item in noise:
|
852
|
|
|
if (dimension == 1):
|
853
|
|
View Code Duplication |
if (data is None):
|
|
|
|
|
854
|
|
|
axes.plot(item[0], 0.0, 'w' + marker_descr);
|
855
|
|
|
else:
|
856
|
|
|
axes.plot(data[item][0], 0.0, 'w' + marker_descr);
|
857
|
|
|
|
858
|
|
|
if (dimension == 2):
|
859
|
|
|
if (data is None):
|
860
|
|
|
axes.plot(item[0], item[1], 'w' + marker_descr);
|
861
|
|
|
else:
|
862
|
|
|
axes.plot(data[item][0], data[item][1], 'w' + marker_descr);
|
863
|
|
|
|
864
|
|
|
elif (dimension == 3):
|
865
|
|
|
if (data is None):
|
866
|
|
|
axes.scatter(item[0], item[1], item[2], c = 'w', marker = marker_descr);
|
867
|
|
|
else:
|
868
|
|
|
axes.scatter(data[item][0], data[item][1], data[item][2], c = 'w', marker = marker_descr);
|
869
|
|
View Code Duplication |
|
|
|
|
|
870
|
|
|
axes.grid(True);
|
871
|
|
|
|
872
|
|
|
if (hide_axes is True):
|
873
|
|
|
axes.xaxis.set_ticklabels([]);
|
874
|
|
View Code Duplication |
axes.yaxis.set_ticklabels([]);
|
|
|
|
|
875
|
|
|
|
876
|
|
|
if (dimension == 3):
|
877
|
|
|
axes.zaxis.set_ticklabels([]);
|
878
|
|
|
|
879
|
|
|
if (display_result is True):
|
880
|
|
|
plt.show();
|
881
|
|
|
|
882
|
|
|
return axes;
|
883
|
|
|
|
884
|
|
|
|
885
|
|
|
def draw_dynamics(t, dyn, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, separate = False, axes = None):
|
886
|
|
|
"""!
|
887
|
|
|
@brief Draw dynamics of neurons (oscillators) in the network.
|
888
|
|
|
@details It draws if matplotlib is not specified (None), othewise it should be performed manually.
|
889
|
|
|
|
890
|
|
|
@param[in] t (list): Values of time (used by x axis).
|
891
|
|
|
@param[in] dyn (list): Values of output of oscillators (used by y axis).
|
892
|
|
|
@param[in] x_title (string): Title for Y.
|
893
|
|
|
@param[in] y_title (string): Title for X.
|
894
|
|
|
@param[in] x_lim (double): X limit.
|
895
|
|
|
@param[in] y_lim (double): Y limit.
|
896
|
|
|
@param[in] x_labels (bool): If True - shows X labels.
|
897
|
|
|
@param[in] y_labels (bool): If True - shows Y labels.
|
898
|
|
|
@param[in] separate (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
|
899
|
|
|
@param[in] axes (ax): If specified then matplotlib axes will be used for drawing and plot will not be shown.
|
900
|
|
|
|
901
|
|
|
@return (ax) Axes of matplotlib.
|
902
|
|
|
|
903
|
|
|
"""
|
904
|
|
|
|
905
|
|
|
number_lines = 0;
|
906
|
|
|
|
907
|
|
|
stage_xlim = None;
|
908
|
|
|
if (x_lim is not None):
|
909
|
|
|
stage_xlim = x_lim;
|
910
|
|
|
elif (len(t) > 0):
|
911
|
|
|
stage_xlim = [0, t[len(t) - 1]];
|
912
|
|
|
|
913
|
|
|
if ( (isinstance(separate, bool) is True) and (separate is True) ):
|
914
|
|
|
if (isinstance(dyn[0], list) is True):
|
915
|
|
|
number_lines = len(dyn[0]);
|
916
|
|
|
else:
|
917
|
|
|
number_lines = 1;
|
918
|
|
|
|
919
|
|
|
elif (isinstance(separate, list) is True):
|
920
|
|
|
number_lines = len(separate);
|
921
|
|
|
|
922
|
|
|
else:
|
923
|
|
|
number_lines = 1;
|
924
|
|
|
|
925
|
|
|
dysplay_result = False;
|
926
|
|
|
if (axes is None):
|
927
|
|
|
dysplay_result = True;
|
928
|
|
|
(fig, axes) = plt.subplots(number_lines, 1);
|
929
|
|
|
|
930
|
|
|
# Check if we have more than one dynamic
|
931
|
|
|
if (isinstance(dyn[0], list) is True):
|
932
|
|
|
num_items = len(dyn[0]);
|
933
|
|
|
for index in range(0, num_items, 1):
|
934
|
|
|
y = [item[index] for item in dyn];
|
935
|
|
|
|
936
|
|
|
if (number_lines > 1):
|
937
|
|
|
index_stage = -1;
|
938
|
|
|
|
939
|
|
|
# Find required axes for the y
|
940
|
|
|
if (isinstance(separate, bool) is True):
|
941
|
|
|
index_stage = index;
|
942
|
|
|
|
943
|
|
|
elif (isinstance(separate, list) is True):
|
944
|
|
|
for index_group in range(0, len(separate), 1):
|
945
|
|
|
if (index in separate[index_group]):
|
946
|
|
|
index_stage = index_group;
|
947
|
|
|
break;
|
948
|
|
|
|
949
|
|
|
if (index_stage != -1):
|
950
|
|
|
if (index_stage != number_lines - 1):
|
951
|
|
|
axes[index_stage].get_xaxis().set_visible(False);
|
952
|
|
|
|
953
|
|
|
axes[index_stage].plot(t, y, 'b-', linewidth = 0.5);
|
954
|
|
|
set_ax_param(axes[index_stage], x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
955
|
|
|
|
956
|
|
|
else:
|
957
|
|
|
axes.plot(t, y, 'b-', linewidth = 0.5);
|
958
|
|
|
set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
959
|
|
|
else:
|
960
|
|
|
axes.plot(t, dyn, 'b-', linewidth = 0.5);
|
961
|
|
|
set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
962
|
|
|
|
963
|
|
|
if (dysplay_result is True):
|
964
|
|
|
plt.show();
|
965
|
|
|
|
966
|
|
|
return axes;
|
967
|
|
|
|
968
|
|
|
|
969
|
|
|
def set_ax_param(ax, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, grid = True):
|
970
|
|
|
"""!
|
971
|
|
|
@brief Sets parameters for matplotlib ax.
|
972
|
|
|
|
973
|
|
|
@param[in] ax (Axes): Axes for which parameters should applied.
|
974
|
|
|
@param[in] x_title (string): Title for Y.
|
975
|
|
|
@param[in] y_title (string): Title for X.
|
976
|
|
|
@param[in] x_lim (double): X limit.
|
977
|
|
|
@param[in] y_lim (double): Y limit.
|
978
|
|
|
@param[in] x_labels (bool): If True - shows X labels.
|
979
|
|
|
@param[in] y_labels (bool): If True - shows Y labels.
|
980
|
|
|
@param[in] grid (bool): If True - shows grid.
|
981
|
|
|
|
982
|
|
|
"""
|
983
|
|
|
from matplotlib.font_manager import FontProperties;
|
984
|
|
|
from matplotlib import rcParams;
|
985
|
|
|
|
986
|
|
|
if (_platform == "linux") or (_platform == "linux2"):
|
987
|
|
|
rcParams['font.sans-serif'] = ['Liberation Serif'];
|
988
|
|
|
else:
|
989
|
|
|
rcParams['font.sans-serif'] = ['Arial'];
|
990
|
|
|
|
991
|
|
|
rcParams['font.size'] = 12;
|
992
|
|
|
|
993
|
|
|
surface_font = FontProperties();
|
994
|
|
|
if (_platform == "linux") or (_platform == "linux2"):
|
995
|
|
|
surface_font.set_name('Liberation Serif');
|
996
|
|
|
else:
|
997
|
|
|
surface_font.set_name('Arial');
|
998
|
|
|
|
999
|
|
|
surface_font.set_size('12');
|
1000
|
|
|
|
1001
|
|
|
if (y_title is not None): ax.set_ylabel(y_title, fontproperties = surface_font);
|
1002
|
|
|
if (x_title is not None): ax.set_xlabel(x_title, fontproperties = surface_font);
|
1003
|
|
|
|
1004
|
|
|
if (x_lim is not None): ax.set_xlim(x_lim[0], x_lim[1]);
|
1005
|
|
|
if (y_lim is not None): ax.set_ylim(y_lim[0], y_lim[1]);
|
1006
|
|
|
|
1007
|
|
|
if (x_labels is False): ax.xaxis.set_ticklabels([]);
|
1008
|
|
|
if (y_labels is False): ax.yaxis.set_ticklabels([]);
|
1009
|
|
|
|
1010
|
|
|
ax.grid(grid);
|
1011
|
|
|
|
1012
|
|
|
|
1013
|
|
|
def draw_dynamics_set(dynamics, xtitle = None, ytitle = None, xlim = None, ylim = None, xlabels = False, ylabels = False):
|
1014
|
|
|
"""!
|
1015
|
|
|
@brief Draw lists of dynamics of neurons (oscillators) in the network.
|
1016
|
|
|
|
1017
|
|
|
@param[in] dynamics (list): List of network outputs that are represented by values of output of oscillators (used by y axis).
|
1018
|
|
|
@param[in] xtitle (string): Title for Y.
|
1019
|
|
|
@param[in] ytitle (string): Title for X.
|
1020
|
|
|
@param[in] xlim (double): X limit.
|
1021
|
|
|
@param[in] ylim (double): Y limit.
|
1022
|
|
|
@param[in] xlabels (bool): If True - shows X labels.
|
1023
|
|
|
@param[in] ylabels (bool): If True - shows Y labels.
|
1024
|
|
|
|
1025
|
|
|
"""
|
1026
|
|
|
# Calculate edge for confortable representation.
|
1027
|
|
|
number_dynamics = len(dynamics);
|
1028
|
|
|
if (number_dynamics == 1):
|
1029
|
|
|
draw_dynamics(dynamics[0][0], dynamics[0][1], xtitle, ytitle, xlim, ylim, xlabels, ylabels);
|
1030
|
|
|
return;
|
1031
|
|
|
|
1032
|
|
|
number_cols = int(numpy.ceil(number_dynamics ** 0.5));
|
1033
|
|
|
number_rows = int(numpy.ceil(number_dynamics / number_cols));
|
1034
|
|
|
|
1035
|
|
|
real_index = 0, 0;
|
1036
|
|
|
double_indexer = True;
|
1037
|
|
|
if ( (number_cols == 1) or (number_rows == 1) ):
|
1038
|
|
|
real_index = 0;
|
1039
|
|
|
double_indexer = False;
|
1040
|
|
|
|
1041
|
|
|
(_, axarr) = plt.subplots(number_rows, number_cols);
|
1042
|
|
|
#plt.setp([ax for ax in axarr], visible = False);
|
1043
|
|
|
|
1044
|
|
|
for dynamic in dynamics:
|
1045
|
|
|
axarr[real_index] = draw_dynamics(dynamic[0], dynamic[1], xtitle, ytitle, xlim, ylim, xlabels, ylabels, axes = axarr[real_index]);
|
1046
|
|
|
#plt.setp(axarr[real_index], visible = True);
|
1047
|
|
|
|
1048
|
|
|
if (double_indexer is True):
|
1049
|
|
|
real_index = real_index[0], real_index[1] + 1;
|
1050
|
|
|
if (real_index[1] >= number_cols):
|
1051
|
|
|
real_index = real_index[0] + 1, 0;
|
1052
|
|
|
else:
|
1053
|
|
|
real_index += 1;
|
1054
|
|
|
|
1055
|
|
|
plt.show();
|
1056
|
|
|
|
1057
|
|
|
|
1058
|
|
|
def draw_image_color_segments(source, clusters, hide_axes = True):
|
1059
|
|
|
"""!
|
1060
|
|
|
@brief Shows image segments using colored image.
|
1061
|
|
|
@details Each color on result image represents allocated segment. The first image is initial and other is result of segmentation.
|
1062
|
|
|
|
1063
|
|
|
@param[in] source (string): Path to image.
|
1064
|
|
|
@param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
|
1065
|
|
|
consists of indexes of pixel from source image.
|
1066
|
|
|
@param[in] hide_axes (bool): If True then axes will not be displayed.
|
1067
|
|
|
|
1068
|
|
|
"""
|
1069
|
|
|
|
1070
|
|
|
image_source = Image.open(source);
|
1071
|
|
|
image_size = image_source.size;
|
1072
|
|
|
|
1073
|
|
|
(fig, axarr) = plt.subplots(1, 2);
|
1074
|
|
|
|
1075
|
|
|
plt.setp([ax for ax in axarr], visible = False);
|
1076
|
|
|
|
1077
|
|
|
available_colors = [ (0, 162, 232), (34, 177, 76), (237, 28, 36),
|
1078
|
|
|
(255, 242, 0), (0, 0, 0), (237, 28, 36),
|
1079
|
|
|
(255, 174, 201), (127, 127, 127), (185, 122, 87),
|
1080
|
|
|
(200, 191, 231), (136, 0, 21), (255, 127, 39),
|
1081
|
|
|
(63, 72, 204), (195, 195, 195), (255, 201, 14),
|
1082
|
|
|
(239, 228, 176), (181, 230, 29), (153, 217, 234),
|
1083
|
|
|
(112, 146, 180) ];
|
1084
|
|
|
|
1085
|
|
|
image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
|
1086
|
|
|
|
1087
|
|
|
for index_segment in range(len(clusters)):
|
1088
|
|
|
for index_pixel in clusters[index_segment]:
|
1089
|
|
|
image_color_segments[index_pixel] = available_colors[index_segment];
|
1090
|
|
|
|
1091
|
|
|
stage = array(image_color_segments, numpy.uint8);
|
1092
|
|
|
stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
|
1093
|
|
|
image_cluster = Image.fromarray(stage, 'RGB');
|
1094
|
|
|
|
1095
|
|
|
axarr[0].imshow(image_source, interpolation = 'none');
|
1096
|
|
|
axarr[1].imshow(image_cluster, interpolation = 'none');
|
1097
|
|
|
|
1098
|
|
|
for i in range(2):
|
1099
|
|
|
plt.setp(axarr[i], visible = True);
|
1100
|
|
|
|
1101
|
|
|
if (hide_axes is True):
|
1102
|
|
|
axarr[i].xaxis.set_ticklabels([]);
|
1103
|
|
|
axarr[i].yaxis.set_ticklabels([]);
|
1104
|
|
|
axarr[i].xaxis.set_ticks_position('none');
|
1105
|
|
|
axarr[i].yaxis.set_ticks_position('none');
|
1106
|
|
|
|
1107
|
|
|
plt.show();
|
1108
|
|
|
|
1109
|
|
|
|
1110
|
|
|
def draw_image_mask_segments(source, clusters, hide_axes = True):
|
1111
|
|
|
"""!
|
1112
|
|
|
@brief Shows image segments using black masks.
|
1113
|
|
|
@details Each black mask of allocated segment is presented on separate plot.
|
1114
|
|
|
The first image is initial and others are black masks of segments.
|
1115
|
|
|
|
1116
|
|
|
@param[in] source (string): Path to image.
|
1117
|
|
|
@param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
|
1118
|
|
|
consists of indexes of pixel from source image.
|
1119
|
|
|
@param[in] hide_axes (bool): If True then axes will not be displayed.
|
1120
|
|
|
|
1121
|
|
|
"""
|
1122
|
|
|
if (len(clusters) == 0):
|
1123
|
|
|
print("Warning: Nothing to draw - list of clusters is empty.")
|
1124
|
|
|
return;
|
1125
|
|
|
|
1126
|
|
|
image_source = Image.open(source);
|
1127
|
|
|
image_size = image_source.size;
|
1128
|
|
|
|
1129
|
|
|
# Calculate edge for confortable representation.
|
1130
|
|
|
number_clusters = len(clusters) + 1; # show with the source image
|
1131
|
|
|
|
1132
|
|
|
number_cols = int(numpy.ceil(number_clusters ** 0.5));
|
1133
|
|
|
number_rows = int(numpy.ceil(number_clusters / number_cols));
|
1134
|
|
|
|
1135
|
|
|
|
1136
|
|
|
real_index = 0, 0;
|
1137
|
|
|
double_indexer = True;
|
1138
|
|
|
if ( (number_cols == 1) or (number_rows == 1) ):
|
1139
|
|
|
real_index = 0;
|
1140
|
|
|
double_indexer = False;
|
1141
|
|
|
|
1142
|
|
|
(fig, axarr) = plt.subplots(number_rows, number_cols);
|
1143
|
|
|
plt.setp([ax for ax in axarr], visible = False);
|
1144
|
|
|
|
1145
|
|
|
axarr[real_index].imshow(image_source, interpolation = 'none');
|
1146
|
|
|
plt.setp(axarr[real_index], visible = True);
|
1147
|
|
|
|
1148
|
|
|
if (hide_axes is True):
|
1149
|
|
|
axarr[real_index].xaxis.set_ticklabels([]);
|
1150
|
|
|
axarr[real_index].yaxis.set_ticklabels([]);
|
1151
|
|
|
axarr[real_index].xaxis.set_ticks_position('none');
|
1152
|
|
|
axarr[real_index].yaxis.set_ticks_position('none');
|
1153
|
|
|
|
1154
|
|
|
if (double_indexer is True):
|
1155
|
|
|
real_index = 0, 1;
|
1156
|
|
|
else:
|
1157
|
|
|
real_index += 1;
|
1158
|
|
|
|
1159
|
|
|
for cluster in clusters:
|
1160
|
|
|
stage_cluster = [(255, 255, 255)] * (image_size[0] * image_size[1]);
|
1161
|
|
|
for index in cluster:
|
1162
|
|
|
stage_cluster[index] = (0, 0, 0);
|
1163
|
|
|
|
1164
|
|
|
stage = array(stage_cluster, numpy.uint8);
|
1165
|
|
|
stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
|
1166
|
|
|
|
1167
|
|
|
image_cluster = Image.fromarray(stage, 'RGB');
|
1168
|
|
|
|
1169
|
|
|
axarr[real_index].imshow(image_cluster, interpolation = 'none');
|
1170
|
|
|
plt.setp(axarr[real_index], visible = True);
|
1171
|
|
|
|
1172
|
|
|
if (hide_axes is True):
|
1173
|
|
|
axarr[real_index].xaxis.set_ticklabels([]);
|
1174
|
|
|
axarr[real_index].yaxis.set_ticklabels([]);
|
1175
|
|
|
|
1176
|
|
|
axarr[real_index].xaxis.set_ticks_position('none');
|
1177
|
|
|
axarr[real_index].yaxis.set_ticks_position('none');
|
1178
|
|
|
|
1179
|
|
|
if (double_indexer is True):
|
1180
|
|
|
real_index = real_index[0], real_index[1] + 1;
|
1181
|
|
|
if (real_index[1] >= number_cols):
|
1182
|
|
|
real_index = real_index[0] + 1, 0;
|
1183
|
|
|
else:
|
1184
|
|
|
real_index += 1;
|
1185
|
|
|
|
1186
|
|
|
|
1187
|
|
|
plt.show();
|
1188
|
|
|
|
1189
|
|
|
|
1190
|
|
|
def linear_sum(list_vector):
|
1191
|
|
|
"""!
|
1192
|
|
|
@brief Calculates linear sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
|
1193
|
|
|
|
1194
|
|
|
@param[in] list_vector (list): Input vector.
|
1195
|
|
|
|
1196
|
|
|
@return (list|double) Linear sum of vector that can be represented by list in case of multidimensional elements.
|
1197
|
|
|
|
1198
|
|
|
"""
|
1199
|
|
|
dimension = 1;
|
1200
|
|
|
linear_sum = 0.0;
|
1201
|
|
|
list_representation = (type(list_vector[0]) == list);
|
1202
|
|
|
|
1203
|
|
|
if (list_representation is True):
|
1204
|
|
|
dimension = len(list_vector[0]);
|
1205
|
|
|
linear_sum = [0] * dimension;
|
1206
|
|
|
|
1207
|
|
|
for index_element in range(0, len(list_vector)):
|
1208
|
|
|
if (list_representation is True):
|
1209
|
|
|
for index_dimension in range(0, dimension):
|
1210
|
|
|
linear_sum[index_dimension] += list_vector[index_element][index_dimension];
|
1211
|
|
|
else:
|
1212
|
|
|
linear_sum += list_vector[index_element];
|
1213
|
|
|
|
1214
|
|
|
return linear_sum;
|
1215
|
|
|
|
1216
|
|
|
|
1217
|
|
|
def square_sum(list_vector):
|
1218
|
|
|
"""!
|
1219
|
|
|
@brief Calculates square sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
|
1220
|
|
|
|
1221
|
|
|
@param[in] list_vector (list): Input vector.
|
1222
|
|
|
|
1223
|
|
|
@return (double) Square sum of vector.
|
1224
|
|
|
|
1225
|
|
|
"""
|
1226
|
|
|
|
1227
|
|
|
square_sum = 0.0;
|
1228
|
|
|
list_representation = (type(list_vector[0]) == list);
|
1229
|
|
|
|
1230
|
|
|
for index_element in range(0, len(list_vector)):
|
1231
|
|
|
if (list_representation is True):
|
1232
|
|
|
square_sum += sum(list_math_multiplication(list_vector[index_element], list_vector[index_element]));
|
1233
|
|
|
else:
|
1234
|
|
|
square_sum += list_vector[index_element] * list_vector[index_element];
|
1235
|
|
|
|
1236
|
|
|
return square_sum;
|
1237
|
|
|
|
1238
|
|
|
|
1239
|
|
|
def list_math_subtraction(a, b):
|
1240
|
|
|
"""!
|
1241
|
|
|
@brief Calculates subtraction of two lists.
|
1242
|
|
|
@details Each element from list 'a' is subtracted by element from list 'b' accordingly.
|
1243
|
|
|
|
1244
|
|
|
@param[in] a (list): List of elements that supports mathematical subtraction.
|
1245
|
|
|
@param[in] b (list): List of elements that supports mathematical subtraction.
|
1246
|
|
|
|
1247
|
|
|
@return (list) Results of subtraction of two lists.
|
1248
|
|
|
|
1249
|
|
|
"""
|
1250
|
|
|
return [a[i] - b[i] for i in range(len(a))];
|
1251
|
|
|
|
1252
|
|
|
|
1253
|
|
|
def list_math_substraction_number(a, b):
|
1254
|
|
|
"""!
|
1255
|
|
|
@brief Calculates subtraction between list and number.
|
1256
|
|
|
@details Each element from list 'a' is subtracted by number 'b'.
|
1257
|
|
|
|
1258
|
|
|
@param[in] a (list): List of elements that supports mathematical subtraction.
|
1259
|
|
|
@param[in] b (list): Value that supports mathematical subtraction.
|
1260
|
|
|
|
1261
|
|
|
@return (list) Results of subtraction between list and number.
|
1262
|
|
|
|
1263
|
|
|
"""
|
1264
|
|
|
return [a[i] - b for i in range(len(a))];
|
1265
|
|
|
|
1266
|
|
|
|
1267
|
|
|
def list_math_addition(a, b):
|
1268
|
|
|
"""!
|
1269
|
|
|
@brief Addition of two lists.
|
1270
|
|
|
@details Each element from list 'a' is added to element from list 'b' accordingly.
|
1271
|
|
|
|
1272
|
|
|
@param[in] a (list): List of elements that supports mathematic addition..
|
1273
|
|
|
@param[in] b (list): List of elements that supports mathematic addition..
|
1274
|
|
|
|
1275
|
|
|
@return (list) Results of addtion of two lists.
|
1276
|
|
|
|
1277
|
|
|
"""
|
1278
|
|
|
return [a[i] + b[i] for i in range(len(a))];
|
1279
|
|
|
|
1280
|
|
|
|
1281
|
|
|
def list_math_addition_number(a, b):
|
1282
|
|
|
"""!
|
1283
|
|
|
@brief Addition between list and number.
|
1284
|
|
|
@details Each element from list 'a' is added to number 'b'.
|
1285
|
|
|
|
1286
|
|
|
@param[in] a (list): List of elements that supports mathematic addition.
|
1287
|
|
|
@param[in] b (double): Value that supports mathematic addition.
|
1288
|
|
|
|
1289
|
|
|
@return (list) Result of addtion of two lists.
|
1290
|
|
|
|
1291
|
|
|
"""
|
1292
|
|
|
return [a[i] + b for i in range(len(a))];
|
1293
|
|
|
|
1294
|
|
|
|
1295
|
|
|
def list_math_division_number(a, b):
|
1296
|
|
|
"""!
|
1297
|
|
|
@brief Division between list and number.
|
1298
|
|
|
@details Each element from list 'a' is divided by number 'b'.
|
1299
|
|
|
|
1300
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1301
|
|
|
@param[in] b (double): Value that supports mathematic division.
|
1302
|
|
|
|
1303
|
|
|
@return (list) Result of division between list and number.
|
1304
|
|
|
|
1305
|
|
|
"""
|
1306
|
|
|
return [a[i] / b for i in range(len(a))];
|
1307
|
|
|
|
1308
|
|
|
|
1309
|
|
|
def list_math_division(a, b):
|
1310
|
|
|
"""!
|
1311
|
|
|
@brief Division of two lists.
|
1312
|
|
|
@details Each element from list 'a' is divided by element from list 'b' accordingly.
|
1313
|
|
|
|
1314
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1315
|
|
|
@param[in] b (list): List of elements that supports mathematic division.
|
1316
|
|
|
|
1317
|
|
|
@return (list) Result of division of two lists.
|
1318
|
|
|
|
1319
|
|
|
"""
|
1320
|
|
|
return [a[i] / b[i] for i in range(len(a))];
|
1321
|
|
|
|
1322
|
|
|
|
1323
|
|
|
def list_math_multiplication_number(a, b):
|
1324
|
|
|
"""!
|
1325
|
|
|
@brief Multiplication between list and number.
|
1326
|
|
|
@details Each element from list 'a' is multiplied by number 'b'.
|
1327
|
|
|
|
1328
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1329
|
|
|
@param[in] b (double): Number that supports mathematic division.
|
1330
|
|
|
|
1331
|
|
|
@return (list) Result of division between list and number.
|
1332
|
|
|
|
1333
|
|
|
"""
|
1334
|
|
|
return [a[i] * b for i in range(len(a))];
|
1335
|
|
|
|
1336
|
|
|
|
1337
|
|
|
def list_math_multiplication(a, b):
|
1338
|
|
|
"""!
|
1339
|
|
|
@brief Multiplication of two lists.
|
1340
|
|
|
@details Each element from list 'a' is multiplied by element from list 'b' accordingly.
|
1341
|
|
|
|
1342
|
|
|
@param[in] a (list): List of elements that supports mathematic multiplication.
|
1343
|
|
|
@param[in] b (list): List of elements that supports mathematic multiplication.
|
1344
|
|
|
|
1345
|
|
|
@return (list) Result of multiplication of elements in two lists.
|
1346
|
|
|
|
1347
|
|
|
"""
|
1348
|
|
|
return [a[i] * b[i] for i in range(len(a))];
|
1349
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
r
orR
are they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.