Completed
Push — master ( 95ecf9...0ec04d )
by Andrei
01:27
created

unit_vector()   A

Complexity

Conditions 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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