Completed
Push — master ( a63a82...61f52c )
by
unknown
01:32
created

draw_dynamics()   F

Complexity

Conditions 16

Size

Total Lines 75

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 16
dl 0
loc 75
rs 2.289

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like draw_dynamics() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 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 due to - 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 heaviside(value):
407
    """!
408
    @brief Calculates Heaviside function that represents step function.
409
    @details If input value is greater than 0 then returns 1, otherwise returns 0.
410
    
411
    @param[in] value (double): Argument of Heaviside function.
412
    
413
    @return (double) Value of Heaviside function.
414
    
415
    """
416
    if (value >= 0.0): return 1.0;
417
    return 0.0;
418
419
420
def timedcall(executable_function, *args):
421
    """!
422
    @brief Executes specified method or function with measuring of execution time.
423
    
424
    @param[in] executable_function (pointer): Pointer to function or method.
425
    @param[in] args (*): Arguments of called function or method.
426
    
427
    @return (tuple) Execution time and result of execution of function or method (execution_time, result_execution).
428
    
429
    """
430
    
431
    time_start = time.clock();
432
    result = executable_function(*args);
433
    time_end = time.clock();
434
    
435
    return (time_end - time_start, result);
436
437
438
def extract_number_oscillations(osc_dyn, index = 0, amplitude_threshold = 1.0):
439
    """!
440
    @brief Extracts number of oscillations of specified oscillator.
441
    
442
    @param[in] osc_dyn (list): Dynamic of oscillators.
443
    @param[in] index (uint): Index of oscillator in dynamic.
444
    @param[in] amplitude_threshold (double): Amplitude threshold, when oscillator value is greater than threshold then
445
               oscillation is incremented.
446
    
447
    @return (uint) Number of oscillations of specified oscillator.
448
    
449
    """
450
    
451
    number_oscillations = 0;
452
    high_level_trigger = False;
453
    
454
    for values in osc_dyn:
455
        if ( (values[index] > amplitude_threshold) and (high_level_trigger is False) ):
456
            number_oscillations += 1;
457
            high_level_trigger = True;
458
        
459
        elif ( (values[index] < amplitude_threshold) and (high_level_trigger is True) ):
460
            high_level_trigger = False;
461
            
462
    return number_oscillations;
463
464
465
def allocate_sync_ensembles(dynamic, tolerance = 0.1, threshold = 1.0, ignore = None):
466
    """!
467
    @brief Allocate clusters in line with ensembles of synchronous oscillators where each
468
           synchronous ensemble corresponds to only one cluster.
469
    
470
    @param[in] dynamic (dynamic): Dynamic of each oscillator.
471
    @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
472
    @param[in] threshold (double): Amlitude trigger when spike is taken into account.
473
    @param[in] ignore (bool): Set of indexes that shouldn't be taken into account.
474
    
475
    @return (list) Grours (lists) of indexes of synchronous oscillators, for example, 
476
            [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
477
            
478
    """
479
    
480
    descriptors = [] * len(dynamic);
481
    
482
    # Check from the end for obtaining result
483
    for index_dyn in range(0, len(dynamic[0]), 1):
484
        if ((ignore is not None) and (index_dyn in ignore)):
485
            continue;
486
        
487
        time_stop_simulation = len(dynamic) - 1;
488
        active_state = False;
489
        
490
        if (dynamic[time_stop_simulation][index_dyn] > threshold):
491
            active_state = True;
492
            
493
        # if active state is detected, it means we don't have whole oscillatory period for the considered oscillator, should be skipped.
494
        if (active_state is True):
495
            while ( (dynamic[time_stop_simulation][index_dyn] > threshold) and (time_stop_simulation > 0) ):
496
                time_stop_simulation -= 1;
497
            
498
            # if there are no any oscillation than let's consider it like noise
499
            if (time_stop_simulation == 0):
500
                continue;
501
            
502
            # reset
503
            active_state = False;
504
        
505
        desc = [0, 0, 0]; # end, start, average time of oscillation
506
        for t in range(time_stop_simulation, 0, -1):
507
            if ( (dynamic[t][index_dyn] > 0) and (active_state is False) ):
508
                desc[0] = t;
509
                active_state = True;
510
            elif ( (dynamic[t][index_dyn] < 0) and (active_state is True) ):
511
                desc[1] = t;
512
                active_state = False;
513
                
514
                break;
515
        
516
        if (desc == [0, 0, 0]):
517
            continue;
518
        
519
        desc[2] = desc[1] + (desc[0] - desc[1]) / 2.0;
520
        descriptors.append(desc);
521
        
522
    
523
    # Cluster allocation
524
    sync_ensembles = [];
525
    desc_sync_ensembles = [];
526
    
527
    for index_desc in range(0, len(descriptors), 1):
528
        if (descriptors[index_desc] == []):
529
            continue;
530
        
531
        if (len(sync_ensembles) == 0):
532
            desc_ensemble = descriptors[index_desc];
533
            reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
534
            
535
            desc_ensemble[0] = desc_ensemble[2] + reducer;
536
            desc_ensemble[1] = desc_ensemble[2] - reducer;
537
            
538
            desc_sync_ensembles.append(desc_ensemble);
539
            sync_ensembles.append([ index_desc ]);
540
        else:
541
            oscillator_captured = False;
542
            for index_ensemble in range(0, len(sync_ensembles), 1):
543
                if ( (desc_sync_ensembles[index_ensemble][0] > descriptors[index_desc][2]) and (desc_sync_ensembles[index_ensemble][1] < descriptors[index_desc][2])):
544
                    sync_ensembles[index_ensemble].append(index_desc);
545
                    oscillator_captured = True;
546
                    break;
547
                
548
            if (oscillator_captured is False):
549
                desc_ensemble = descriptors[index_desc];
550
                reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
551
        
552
                desc_ensemble[0] = desc_ensemble[2] + reducer;
553
                desc_ensemble[1] = desc_ensemble[2] - reducer;
554
        
555
                desc_sync_ensembles.append(desc_ensemble);
556
                sync_ensembles.append([ index_desc ]);
557
    
558
    return sync_ensembles;
559
    
560
    
561
def draw_clusters(data, clusters, noise = [], marker_descr = '.', hide_axes = False, axes = None, display_result = True):
562
    """!
563
    @brief Displays clusters for data in 2D or 3D.
564
    
565
    @param[in] data (list): Points that are described by coordinates represented.
566
    @param[in] clusters (list): Clusters that are represented by lists of indexes where each index corresponds to point in data.
567
    @param[in] noise (list): Points that are regarded to noise.
568
    @param[in] marker_descr (string): Marker for displaying points.
569
    @param[in] hide_axes (bool): If True - axes is not displayed.
570
    @param[in] axes (ax) Matplotlib axes where clusters should be drawn, if it is not specified (None) then new plot will be created.
571
    @param[in] display_result (bool): If specified then matplotlib axes will be used for drawing and plot will not be shown.
572
    
573
    @return (ax) Matplotlib axes where drawn clusters are presented.
574
    
575
    """
576
    # Get dimension
577
    dimension = 0;
578
    if ( (data is not None) and (clusters is not None) ):
579
        dimension = len(data[0]);
580
    elif ( (data is None) and (clusters is not None) ):
581
        dimension = len(clusters[0][0]);
582
    else:
583
        raise NameError('Data or clusters should be specified exactly.');
584
    
585
    "Draw clusters"
586
    colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet', 
587
               'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
588
               'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
589
               'silver', 'lime', 'pink', 'gold', 'bisque' ];
590
               
591
    if (len(clusters) > len(colors)):
592
        raise NameError('Impossible to represent clusters due to number of specified colors.');
593
    
594
    fig = plt.figure();
595
    
596
    if (axes is None):
597
        # Check for dimensions
598
        if ((dimension) == 1 or (dimension == 2)):
599
            axes = fig.add_subplot(111);
600
        elif (dimension == 3):
601
            axes = fig.gca(projection='3d');
602
        else:
603
            raise NameError('Drawer supports only 2d and 3d data representation');
604
    
605
    color_index = 0;
606
    for cluster in clusters:
607
        color = colors[color_index];
608
        for item in cluster:
609
            if (dimension == 1):
610
                if (data is None):
611
                    axes.plot(item[0], 0.0, color = color, marker = marker_descr);
612
                else:
613
                    axes.plot(data[item][0], 0.0, color = color, marker = marker_descr);
614
            
615
            if (dimension == 2):
616
                if (data is None):
617
                    axes.plot(item[0], item[1], color = color, marker = marker_descr);
618
                else:
619
                    axes.plot(data[item][0], data[item][1], color = color, marker = marker_descr);
620
                    
621 View Code Duplication
            elif (dimension == 3):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
622
                if (data is None):
623
                    axes.scatter(item[0], item[1], item[2], c = color, marker = marker_descr);
624
                else:
625
                    axes.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker_descr);
626
        
627
        color_index += 1;
628
    
629
    for item in noise:
630
        if (dimension == 1):
631
            if (data is None):
632
                axes.plot(item[0], 0.0, 'w' + marker_descr);
633
            else:
634
                axes.plot(data[item][0], 0.0, 'w' + marker_descr);
635
636
        if (dimension == 2):
637
            if (data is None):
638
                axes.plot(item[0], item[1], 'w' + marker_descr);
639
            else:
640
                axes.plot(data[item][0], data[item][1], 'w' + marker_descr);
641
                
642 View Code Duplication
        elif (dimension == 3):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
643
            if (data is None):
644
                axes.scatter(item[0], item[1], item[2], c = 'w', marker = marker_descr);
645
            else:
646
                axes.scatter(data[item][0], data[item][1], data[item][2], c = 'w', marker = marker_descr);
647
    
648
    axes.grid(True);
649
    
650
    if (hide_axes is True):
651
        axes.xaxis.set_ticklabels([]);
652
        axes.yaxis.set_ticklabels([]);
653
        
654
        if (dimension == 3):
655
            axes.zaxis.set_ticklabels([]);
656
    
657
    if (display_result is True):
658
        plt.show();
659
660
    return axes;
661
662
663
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):
664
    """!
665
    @brief Draw dynamics of neurons (oscillators) in the network.
666
    @details It draws if matplotlib is not specified (None), othewise it should be performed manually.
667
    
668
    @param[in] t (list): Values of time (used by x axis).
669
    @param[in] dyn (list): Values of output of oscillators (used by y axis).
670
    @param[in] x_title (string): Title for Y.
671
    @param[in] y_title (string): Title for X.
672
    @param[in] x_lim (double): X limit.
673
    @param[in] y_lim (double): Y limit.
674
    @param[in] x_labels (bool): If True - shows X labels.
675
    @param[in] y_labels (bool): If True - shows Y labels.
676
    @param[in] separate (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
677
    @param[in] axes (ax): If specified then matplotlib axes will be used for drawing and plot will not be shown.
678
    
679
    @return (ax) Axes of matplotlib.
680
    
681
    """
682
         
683
    number_lines = 0;
684
    
685
    if ( (isinstance(separate, bool) is True) and (separate is True) ):
686
        if (isinstance(dyn[0], list) is True):
687
            number_lines = len(dyn[0]);
688
        else:
689
            number_lines = 1;
690
            
691
    elif (isinstance(separate, list) is True):
692
        number_lines = len(separate);
693
        
694
    else:
695
        number_lines = 1;
696
    
697
    dysplay_result = False;
698
    if (axes is None):
699
        dysplay_result = True;
700
        (fig, axes) = plt.subplots(number_lines, 1);
701
    
702
    # Check if we have more than one dynamic
703
    if (isinstance(dyn[0], list) is True):
704
        num_items = len(dyn[0]);
705
        for index in range(0, num_items, 1):       
706
            y = [item[index] for item in dyn];
707
            
708
            if (number_lines > 1): 
709
                index_stage = -1;
710
                
711
                # Find required axes for the y
712
                if (isinstance(separate, bool) is True):
713
                    index_stage = index;
714
                    
715
                elif (isinstance(separate, list) is True):
716
                    for index_group in range(0, len(separate), 1):
717
                        if (index in separate[index_group]): 
718
                            index_stage = index_group;
719
                            break;
720
                
721
                if (index_stage != -1):
722
                    # raise NameError('Index ' + str(index) + ' is not specified in the separation list.');
723
                              
724
                    axes[index_stage].plot(t, y, 'b-', linewidth = 0.5); 
725
                    set_ax_param(axes[index_stage], x_title, y_title, x_lim, y_lim, x_labels, y_labels, True);
726
                
727
            else:
728
                axes.plot(t, y, 'b-', linewidth = 0.5);
729
                set_ax_param(axes, x_title, y_title, x_lim, y_lim, x_labels, y_labels, True);
730
    else:
731
        axes.plot(t, dyn, 'b-', linewidth = 0.5);     
732
        set_ax_param(axes, x_title, y_title, x_lim, y_lim, x_labels, y_labels, True);
733
    
734
    if (dysplay_result is True):
735
        plt.show();
736
    
737
    return axes;
738
739
740
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):
741
    """!
742
    @brief Sets parameters for matplotlib ax.
743
    
744
    @param[in] ax (Axes): Axes for which parameters should applied.
745
    @param[in] x_title (string): Title for Y.
746
    @param[in] y_title (string): Title for X.
747
    @param[in] x_lim (double): X limit.
748
    @param[in] y_lim (double): Y limit.
749
    @param[in] x_labels (bool): If True - shows X labels.
750
    @param[in] y_labels (bool): If True - shows Y labels.
751
    @param[in] grid (bool): If True - shows grid.
752
    
753
    """
754
    from matplotlib.font_manager import FontProperties;
755
    from matplotlib import rcParams;
756
    
757
    if (_platform == "linux") or (_platform == "linux2"):
758
        rcParams['font.sans-serif'] = ['Liberation Serif'];
759
    else:
760
        rcParams['font.sans-serif'] = ['Arial'];
761
        
762
    rcParams['font.size'] = 12;
763
        
764
    surface_font = FontProperties();
765
    if (_platform == "linux") or (_platform == "linux2"):
766
        surface_font.set_name('Liberation Serif');
767
    else:
768
        surface_font.set_name('Arial');
769
        
770
    surface_font.set_size('12');
771
    
772
    if (y_title is not None): ax.set_ylabel(y_title, fontproperties = surface_font);
773
    if (x_title is not None): ax.set_xlabel(x_title, fontproperties = surface_font);
774
    
775
    if (x_lim is not None): ax.set_xlim(x_lim[0], x_lim[1]);
776
    if (y_lim is not None): ax.set_ylim(y_lim[0], y_lim[1]);
777
    
778
    if (x_labels is False): ax.xaxis.set_ticklabels([]);
779
    if (y_labels is False): ax.yaxis.set_ticklabels([]);
780
    
781
    ax.grid(grid);
782
783
784
def draw_dynamics_set(dynamics, xtitle = None, ytitle = None, xlim = None, ylim = None, xlabels = False, ylabels = False):
785
    """!
786
    @brief Draw lists of dynamics of neurons (oscillators) in the network.
787
    
788
    @param[in] dynamics (list): List of network outputs that are represented by values of output of oscillators (used by y axis).
789
    @param[in] xtitle (string): Title for Y.
790
    @param[in] ytitle (string): Title for X.
791
    @param[in] xlim (double): X limit.
792
    @param[in] ylim (double): Y limit.
793
    @param[in] xlabels (bool): If True - shows X labels.
794
    @param[in] ylabels (bool): If True - shows Y labels.
795
    
796
    """
797
    # Calculate edge for confortable representation.
798
    number_dynamics = len(dynamics);
799
    
800
    number_cols = int(numpy.ceil(number_dynamics ** 0.5));
801
    number_rows = int(numpy.ceil(number_dynamics / number_cols));
802
    
803
804
    real_index = 0, 0;
805
    double_indexer = True;
806
    if ( (number_cols == 1) or (number_rows == 1) ):
807
        real_index = 0;
808
        double_indexer = False;
809
    
810
    (fig, axarr) = plt.subplots(number_rows, number_cols);
811
    #plt.setp([ax for ax in axarr], visible = False);
812
    
813
    for dynamic in dynamics:
814
        axarr[real_index] = draw_dynamics(dynamic[0], dynamic[1], xtitle, ytitle, xlim, ylim, xlabels, ylabels, axes = axarr[real_index]);
815
        #plt.setp(axarr[real_index], visible = True);
816
        
817
        if (double_indexer is True):
818
            real_index = real_index[0], real_index[1] + 1;
819
            if (real_index[1] >= number_cols):
820
                real_index = real_index[0] + 1, 0; 
821
        else:
822
            real_index += 1;
823
            
824
    plt.show();
825
826
827
def draw_image_color_segments(source, clusters, hide_axes = True):
828
    """!
829
    @brief Shows image segments using colored image.
830
    @details Each color on result image represents allocated segment. The first image is initial and other is result of segmentation.
831
    
832
    @param[in] source (string): Path to image.
833
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
834
                                consists of indexes of pixel from source image.
835
    @param[in] hide_axes (bool): If True then axes will not be displayed.
836
    
837
    """
838
        
839
    image_source = Image.open(source);
840
    image_size = image_source.size;
841
    
842
    (fig, axarr) = plt.subplots(1, 2);
843
    
844
    plt.setp([ax for ax in axarr], visible = False);
845
    
846
    available_colors = [ (0, 162, 232),   (34, 177, 76),   (237, 28, 36),
847
                         (255, 242, 0),   (0, 0, 0),       (237, 28, 36),
848
                         (255, 174, 201), (127, 127, 127), (185, 122, 87), 
849
                         (200, 191, 231), (136, 0, 21),    (255, 127, 39),
850
                         (63, 72, 204),   (195, 195, 195), (255, 201, 14),
851
                         (239, 228, 176), (181, 230, 29),  (153, 217, 234),
852
                         (112, 146, 180) ];
853
    
854
    image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
855
    
856
    for index_segment in range(len(clusters)):
857
        for index_pixel in clusters[index_segment]:
858
            image_color_segments[index_pixel] = available_colors[index_segment];
859
    
860
    stage = array(image_color_segments, numpy.uint8);
861
    stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
862
    image_cluster = Image.fromarray(stage, 'RGB');
863
    
864
    axarr[0].imshow(image_source, interpolation = 'none');
865
    axarr[1].imshow(image_cluster, interpolation = 'none');
866
    
867
    for i in range(2):
868
        plt.setp(axarr[i], visible = True);
869
        
870
        if (hide_axes is True):
871
            axarr[i].xaxis.set_ticklabels([]);
872
            axarr[i].yaxis.set_ticklabels([]);
873
            axarr[i].xaxis.set_ticks_position('none');
874
            axarr[i].yaxis.set_ticks_position('none');
875
    
876
    plt.show();
877
878
879
def draw_image_mask_segments(source, clusters, hide_axes = True):
880
    """!
881
    @brief Shows image segments using black masks.
882
    @details Each black mask of allocated segment is presented on separate plot.
883
             The first image is initial and others are black masks of segments.
884
    
885
    @param[in] source (string): Path to image.
886
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
887
                                consists of indexes of pixel from source image.
888
    @param[in] hide_axes (bool): If True then axes will not be displayed.
889
    
890
    """
891
    if (len(clusters) == 0):
892
        print("Warning: Nothing to draw - list of clusters is empty.")
893
        return;
894
        
895
    image_source = Image.open(source);
896
    image_size = image_source.size;
897
    
898
    # Calculate edge for confortable representation.
899
    number_clusters = len(clusters) + 1; # show with the source image
900
    
901
    number_cols = int(numpy.ceil(number_clusters ** 0.5));
902
    number_rows = int(numpy.ceil(number_clusters / number_cols));
903
    
904
905
    real_index = 0, 0;
906
    double_indexer = True;
907
    if ( (number_cols == 1) or (number_rows == 1) ):
908
        real_index = 0;
909
        double_indexer = False;
910
    
911
    (fig, axarr) = plt.subplots(number_rows, number_cols);
912
    plt.setp([ax for ax in axarr], visible = False);
913
    
914
    axarr[real_index].imshow(image_source, interpolation = 'none');
915
    plt.setp(axarr[real_index], visible = True);
916
    
917
    if (hide_axes is True):
918
        axarr[real_index].xaxis.set_ticklabels([]);
919
        axarr[real_index].yaxis.set_ticklabels([]);
920
        axarr[real_index].xaxis.set_ticks_position('none');
921
        axarr[real_index].yaxis.set_ticks_position('none');
922
            
923
    if (double_indexer is True):
924
        real_index = 0, 1;
925
    else:
926
        real_index += 1;
927
    
928
    for cluster in clusters:
929
        stage_cluster = [(255, 255, 255)] * (image_size[0] * image_size[1]);
930
        for index in cluster:
931
            stage_cluster[index] = (0, 0, 0);
932
          
933
        stage = array(stage_cluster, numpy.uint8);
934
        stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
935
        
936
        image_cluster = Image.fromarray(stage, 'RGB');
937
        
938
        axarr[real_index].imshow(image_cluster, interpolation = 'none');
939
        plt.setp(axarr[real_index], visible = True);
940
        
941
        if (hide_axes is True):
942
            axarr[real_index].xaxis.set_ticklabels([]);
943
            axarr[real_index].yaxis.set_ticklabels([]);
944
            
945
            axarr[real_index].xaxis.set_ticks_position('none');
946
            axarr[real_index].yaxis.set_ticks_position('none');
947
        
948
        if (double_indexer is True):
949
            real_index = real_index[0], real_index[1] + 1;
950
            if (real_index[1] >= number_cols):
951
                real_index = real_index[0] + 1, 0; 
952
        else:
953
            real_index += 1;
954
955
            
956
    plt.show();
957
958
959
def linear_sum(list_vector):
960
    """!
961
    @brief Calculates linear sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
962
    
963
    @param[in] list_vector (list): Input vector.
964
    
965
    @return (list|double) Linear sum of vector that can be represented by list in case of multidimensional elements.
966
    
967
    """
968
    dimension = 1;
969
    linear_sum = 0.0;
970
    list_representation = (type(list_vector[0]) == list);
971
    
972
    if (list_representation is True):
973
        dimension = len(list_vector[0]);
974
        linear_sum = [0] * dimension;
975
        
976
    for index_element in range(0, len(list_vector)):
977
        if (list_representation is True):
978
            for index_dimension in range(0, dimension):
979
                linear_sum[index_dimension] += list_vector[index_element][index_dimension];
980
        else:
981
            linear_sum += list_vector[index_element];
982
983
    return linear_sum;
984
985
986
def square_sum(list_vector):
987
    """!
988
    @brief Calculates square sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
989
    
990
    @param[in] list_vector (list): Input vector.
991
    
992
    @return (double) Square sum of vector.
993
    
994
    """
995
    
996
    square_sum = 0.0;
997
    list_representation = (type(list_vector[0]) == list);
998
        
999
    for index_element in range(0, len(list_vector)):
1000
        if (list_representation is True):
1001
            square_sum += sum(list_math_multiplication(list_vector[index_element], list_vector[index_element]));
1002
        else:
1003
            square_sum += list_vector[index_element] * list_vector[index_element];
1004
         
1005
    return square_sum;
1006
1007
    
1008
def list_math_subtraction(a, b):
1009
    """!
1010
    @brief Calculates subtraction of two lists.
1011
    @details Each element from list 'a' is subtracted by element from list 'b' accordingly.
1012
    
1013
    @param[in] a (list): List of elements that supports mathematical subtraction.
1014
    @param[in] b (list): List of elements that supports mathematical subtraction.
1015
    
1016
    @return (list) Results of subtraction of two lists.
1017
    
1018
    """
1019
    return [a[i] - b[i] for i in range(len(a))];
1020
1021
1022
def list_math_substraction_number(a, b):
1023
    """!
1024
    @brief Calculates subtraction between list and number.
1025
    @details Each element from list 'a' is subtracted by number 'b'.
1026
    
1027
    @param[in] a (list): List of elements that supports mathematical subtraction.
1028
    @param[in] b (list): Value that supports mathematical subtraction.
1029
    
1030
    @return (list) Results of subtraction between list and number.
1031
    
1032
    """        
1033
    return [a[i] - b for i in range(len(a))];  
1034
1035
1036
def list_math_addition(a, b):
1037
    """!
1038
    @brief Addition of two lists.
1039
    @details Each element from list 'a' is added to element from list 'b' accordingly.
1040
    
1041
    @param[in] a (list): List of elements that supports mathematic addition..
1042
    @param[in] b (list): List of elements that supports mathematic addition..
1043
    
1044
    @return (list) Results of addtion of two lists.
1045
    
1046
    """    
1047
    return [a[i] + b[i] for i in range(len(a))];
1048
1049
1050
def list_math_addition_number(a, b):
1051
    """!
1052
    @brief Addition between list and number.
1053
    @details Each element from list 'a' is added to number 'b'.
1054
    
1055
    @param[in] a (list): List of elements that supports mathematic addition.
1056
    @param[in] b (double): Value that supports mathematic addition.
1057
    
1058
    @return (list) Result of addtion of two lists.
1059
    
1060
    """    
1061
    return [a[i] + b for i in range(len(a))];
1062
1063
1064
def list_math_division_number(a, b):
1065
    """!
1066
    @brief Division between list and number.
1067
    @details Each element from list 'a' is divided by number 'b'.
1068
    
1069
    @param[in] a (list): List of elements that supports mathematic division.
1070
    @param[in] b (double): Value that supports mathematic division.
1071
    
1072
    @return (list) Result of division between list and number.
1073
    
1074
    """    
1075
    return [a[i] / b for i in range(len(a))];
1076
1077
1078
def list_math_division(a, b):
1079
    """!
1080
    @brief Division of two lists.
1081
    @details Each element from list 'a' is divided by element from list 'b' accordingly.
1082
    
1083
    @param[in] a (list): List of elements that supports mathematic division.
1084
    @param[in] b (list): List of elements that supports mathematic division.
1085
    
1086
    @return (list) Result of division of two lists.
1087
    
1088
    """    
1089
    return [a[i] / b[i] for i in range(len(a))];
1090
1091
1092
def list_math_multiplication_number(a, b):
1093
    """!
1094
    @brief Multiplication between list and number.
1095
    @details Each element from list 'a' is multiplied by number 'b'.
1096
    
1097
    @param[in] a (list): List of elements that supports mathematic division.
1098
    @param[in] b (double): Number that supports mathematic division.
1099
    
1100
    @return (list) Result of division between list and number.
1101
    
1102
    """    
1103
    return [a[i] * b for i in range(len(a))];
1104
1105
1106
def list_math_multiplication(a, b):
1107
    """!
1108
    @brief Multiplication of two lists.
1109
    @details Each element from list 'a' is multiplied by element from list 'b' accordingly.
1110
    
1111
    @param[in] a (list): List of elements that supports mathematic multiplication.
1112
    @param[in] b (list): List of elements that supports mathematic multiplication.
1113
    
1114
    @return (list) Result of multiplication of elements in two lists.
1115
    
1116
    """        
1117
    return [a[i] * b[i] for i in range(len(a))];
1118