Completed
Push — master ( 0def1b...f9065b )
by Andrei
01:45
created

knearest()   B

Complexity

Conditions 6

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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