Completed
Push — master ( 48d24e...c43c88 )
by Andrei
01:28
created

cluster_visualizer.append_cluster_attribute()   A

Complexity

Conditions 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
"""!
2
3
@brief pyclustering module for cluster analysis.
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 matplotlib.pyplot as plt;
27
import matplotlib.gridspec as gridspec;
28
29
import math;
30
31
32
class canvas_cluster_descr:
33
    """!
34
    @brief Description of cluster for representation on canvas.
35
    
36
    """
37
38
    def __init__(self, cluster, data, marker, markersize, color):
39
        """!
40
        @brief Constructor of cluster representation on the canvas.
41
        
42
        @param[in] cluster (list): Single cluster that consists of objects or indexes from data.
43
        @param[in] data (list): Objects that should be displayed, can be None if clusters consist of objects instead of indexes.
44
        @param[in] marker (string): Type of marker that is used for drawing objects.
45
        @param[in] markersize (uint): Size of marker that is used for drawing objects.
46
        @param[in] color (string): Color of the marker that is used for drawing objects.
47
        
48
        """
49
        ## Cluster that may consist of objects or indexes of objects from data.
50
        self.cluster = cluster;
51
        
52
        ## Data where objects are stored. It can be None if clusters consist of objects instead of indexes.
53
        self.data = data;
54
        
55
        ## Marker that is used for drawing objects.
56
        self.marker = marker;
57
        
58
        ## Size of marker that is used for drawing objects.
59
        self.markersize = markersize;
60
        
61
        ## Color that is used for coloring marker.
62
        self.color = color;
63
        
64
        ## Attribures of the clusters - additional collections of data points that are regarded to the cluster.
65
        self.attributes = [];
66
    
67
68
class cluster_visualizer:
69
    """!
70
    @brief Common visualizer of clusters on 2D or 3D surface.
71
    
72
    """
73
    
74
    __colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet', 
75
                 'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
76
                 'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
77
                 'silver', 'lime', 'pink', 'gold', 'bisque',
78
                 'dimgray', 'firebrick', 'darksalmon', 'chartreuse', 'skyblue',
79
                 'purple', 'fuchsia', 'palegoldenrod', 'coral', 'hotpink' ];
80
    
81
82
    def __init__(self, number_canvases = 1, size_row = 1):
83
        """!
84
        @brief Constructor of cluster visualizer.
85
        
86
        @param[in] number_canvases (uint): Number of canvases that is used for visualization.
87
        @param[in] size_row (uint): Amount of canvases that can be placed in one row.
88
        
89
        Example:
90
        @code
91
            # load 2D data sample
92
            sample_2d = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE1);
93
            
94
            # load 3D data sample
95
            sample_3d = read_sample(FCPS_SAMPLES.SAMPLE_HEPTA);
96
            
97
            # extract clusters from the first sample using DBSCAN algorithm
98
            dbscan_instance = dbscan(sample_2d, 0.4, 2, False);
99
            dbscan_instance.process();
100
            clusters_sample_2d = dbscan_instance.get_clusters();
101
        
102
            # extract clusters from the second sample using DBSCAN algorithm
103
            dbscan_instance = dbscan(sample_3d, 1, 3, True);
104
            dbscan_instance.process();
105
            clusters_sample_3d = dbscan_instance.get_clusters();
106
            
107
            # create plot with two canvases where each row contains 2 canvases.
108
            size = 2;
109
            row_size = 2;
110
            visualizer = cluster_visualizer(size, row_size);
111
            
112
            # place clustering result of sample_2d to the first canvas
113
            visualizer.append_clusters(clusters_sample_2d, sample_2d, 0, markersize = 5);
114
            
115
            # place clustering result of sample_3d to the second canvas
116
            visualizer.append_clusters(clusters_sample_3d, sample_3d, 1, markersize = 30);
117
            
118
            # show plot
119
            visualizer.show();
120
        @endcode
121
        
122
        """
123
        
124
        self.__number_canvases = number_canvases;
125
        self.__size_row = size_row;
126
        self.__canvas_clusters = [ [] for _ in range(number_canvases) ];
127
        self.__canvas_dimensions = [ None for _ in range(number_canvases) ];
128
        self.__canvas_titles = [ None for _ in range(number_canvases) ];
129
        
130
        self.__default_2d_marker_size = 5;
131
        self.__default_3d_marker_size = 30;
132
    
133
    
134
    def append_cluster(self, cluster, data = None, canvas = 0, marker = '.', markersize = None, color = None):
135
        """!
136
        @brief Appends cluster to canvas for drawing.
137
        
138
        @param[in] cluster (list): cluster that may consist of indexes of objects from the data or object itself.
139
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
140
        @param[in] canvas (uint): Number of canvas that should be used for displaying cluster.
141
        @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas.
142
        @param[in] markersize (uint): Size of marker.
143
        @param[in] color (string): Color of marker.
144
        
145
        @return Returns index of cluster descriptor on the canvas.
146
        
147
        """
148
        
149
        if (len(cluster) == 0):
150
            return;
151
        
152
        if (canvas > self.__number_canvases):
153
            raise NameError('Canvas does ' + canvas + ' not exists.');
154
        
155
        if (color is None):
156
            index_color = len(self.__canvas_clusters[canvas]) % len(self.__colors);
157
            color = self.__colors[index_color];
158
        
159
        added_canvas_descriptor = canvas_cluster_descr(cluster, data, marker, markersize, color);
160
        self.__canvas_clusters[canvas].append( added_canvas_descriptor );
161
        
162
        dimension = 0;
163
        if (data is None):
164
            dimension = len(cluster[0]);
165
            if (self.__canvas_dimensions[canvas] is None):
166
                self.__canvas_dimensions[canvas] = dimension;
167
            elif (self.__canvas_dimensions[canvas] != dimension):
168
                raise NameError('Only clusters with the same dimension of objects can be displayed on canvas.');
169
                
170
        else:
171
            dimension = len(data[0]);
172
            if (self.__canvas_dimensions[canvas] is None):
173
                self.__canvas_dimensions[canvas] = dimension;
174
            elif (self.__canvas_dimensions[canvas] != dimension):
175
                raise NameError('Only clusters with the same dimension of objects can be displayed on canvas.');
176
177
        if ( (dimension < 1) and (dimension > 3) ):
178
            raise NameError('Only objects with size dimension 1 (1D plot), 2 (2D plot) or 3 (3D plot) can be displayed.');
179
        
180
        if (markersize is None):
181
            if ( (dimension == 1) or (dimension == 2) ):
182
                added_canvas_descriptor.markersize = self.__default_2d_marker_size;
183
            elif (dimension == 3):
184
                added_canvas_descriptor.markersize = self.__default_3d_marker_size;
185
        
186
        return len(self.__canvas_clusters[canvas]) - 1;
187
    
188
    
189
    def append_cluster_attribute(self, index_canvas, index_cluster, data, marker = None, markersize = None):
190
        cluster_descr = self.__canvas_clusters[index_canvas][index_cluster];
191
        attribute_marker = marker;
192
        if (attribute_marker is None):
193
            attribute_marker = cluster_descr.marker;
194
        
195
        attribure_markersize = markersize;
196
        if (attribure_markersize is None):
197
            attribure_markersize = cluster_descr.markersize;
198
        
199
        attribute_color = cluster_descr.color;
200
        
201
        added_attribute_cluster_descriptor = canvas_cluster_descr(data, None, attribute_marker, attribure_markersize, attribute_color);
202
        self.__canvas_clusters[index_canvas][index_cluster].attributes.append(added_attribute_cluster_descriptor);
203
    
204
    
205
    def append_clusters(self, clusters, data = None, canvas = 0, marker = '.', markersize = None):
206
        """!
207
        @brief Appends list of cluster to canvas for drawing.
208
        
209
        @param[in] clusters (list): List of clusters where each cluster may consist of indexes of objects from the data or object itself.
210
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
211
        @param[in] canvas (uint): Number of canvas that should be used for displaying clusters.
212
        @param[in] marker (string): Marker that is used for displaying objects from clusters on the canvas.
213
        @param[in] markersize (uint): Size of marker.
214
        
215
        """
216
        
217
        for cluster in clusters:
218
            self.append_cluster(cluster, data, canvas, marker, markersize);
219
    
220
    
221
    def set_canvas_title(self, text, canvas):
222
        """!
223
        @brief Set title for specified canvas.
224
        
225
        @param[in] text (string): Title for canvas.
226
        @param[in] canvas (uint): Index of canvas where title should be displayed.
227
        
228
        """
229
        
230
        if (canvas > self.__number_canvases):
231
            raise NameError('Canvas does ' + canvas + ' not exists.');
232
        
233
        self.__canvas_titles[canvas] = text;
234
    
235
    
236
    def show(self, figure = None, visible_axis = True, visible_grid = True, display = True):
237
        """!
238
        @brief Shows clusters (visualize).
239
        
240
        @param[in] figure (fig): Defines requirement to use specified figure, if None - new figure is created for drawing clusters.
241
        @param[in] visible_axis (bool): Defines visibility of axes on each canvas, if True - axes are invisible.
242
        @param[in] visible_grid (bool): Defines visibility of axes on each canvas, if True - grid is displayed.
243
        @param[in] display (bool): Defines requirement to display clusters on a stage, if True - clusters are displayed, if False - plt.show() should be called by user."
244
        
245
        @return (fig) Figure where clusters are shown.
246
        
247
        """
248
        
249
        canvas_shift = 0;
250
        cluster_figure = None;
251
        if (figure is not None):
252 View Code Duplication
            canvas_shift = len(figure.get_axes());
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
253
            cluster_figure = figure;
254
        else:
255 View Code Duplication
            cluster_figure = plt.figure();
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
256
        
257
        maximum_cols = self.__size_row;
258
        maximum_rows = math.ceil( (self.__number_canvases + canvas_shift) / maximum_cols);
259
        
260
        grid_spec = gridspec.GridSpec(maximum_rows, maximum_cols);
261
        
262
        for index_canvas in range(len(self.__canvas_clusters)):
263
            canvas_data = self.__canvas_clusters[index_canvas];
264
            dimension = self.__canvas_dimensions[index_canvas];
265
            
266
            #ax = axes[real_index];
267
            if ( (dimension == 1) or (dimension == 2) ):
268
                ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift]);
269
            else:
270
                ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift], projection='3d');
271
            
272
            if (len(canvas_data) == 0):
273
                plt.setp(ax, visible = False);
274
            
275
            for cluster_descr in canvas_data:
276
                self.__draw_canvas_cluster(ax, dimension, cluster_descr);
277
                
278
                for attribute_descr in cluster_descr.attributes:
279
                    self.__draw_canvas_cluster(ax, dimension, attribute_descr);
280
            
281
            if (visible_axis is True):
282
                ax.xaxis.set_ticklabels([]);
283
                ax.yaxis.set_ticklabels([]);
284
                
285
                if (dimension == 3):
286
                    ax.zaxis.set_ticklabels([]);
287
            
288
            if (self.__canvas_titles[index_canvas] is not None):
289
                ax.set_title(self.__canvas_titles[index_canvas]);
290
            
291
            ax.grid(visible_grid);
292
        
293
        if (display is True):
294
            plt.show();
295
        
296
        return cluster_figure;
297
    
298
    
299
    """!
300
    @brief Draw canvas cluster descriptor.
301
    
302
    @param[in] ax (Axis): Axis of the canvas where canvas cluster descriptor should be displayed.
303
    @param[in] dimension (uint): Canvas dimension.
304
    @param[in] cluster_descr (canvas_cluster_descr): Canvas cluster descriptor that should be displayed.
305
306
    @return (fig) Figure where clusters are shown.
307
    
308
    """
309
    def __draw_canvas_cluster(self, ax, dimension, cluster_descr):
310
        cluster = cluster_descr.cluster;
311
        data = cluster_descr.data;
312
        marker = cluster_descr.marker;
313
        markersize = cluster_descr.markersize;
314
        color = cluster_descr.color;
315
        
316
        for item in cluster:
317
            if (dimension == 1):
318
                if (data is None):
319
                    ax.plot(item[0], 0.0, color = color, marker = marker, markersize = markersize);
320
                else:
321
                    ax.plot(data[item][0], 0.0, color = color, marker = marker, markersize = markersize);
322
323
            elif (dimension == 2):
324
                if (data is None):
325
                    ax.plot(item[0], item[1], color = color, marker = marker, markersize = markersize);
326
                else:
327
                    ax.plot(data[item][0], data[item][1], color = color, marker = marker, markersize = markersize);
328
        
329
            elif (dimension == 3):
330
                if (data is None):
331
                    ax.scatter(item[0], item[1], item[2], c = color, marker = marker, s = markersize);
332
                else:
333
                    ax.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker, s = markersize);