| Total Complexity | 46 |
| Total Lines | 270 |
| Duplicated Lines | 3.33 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like cluster_visualizer 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 | """! |
||
| 70 | class cluster_visualizer: |
||
| 71 | """! |
||
| 72 | @brief Common visualizer of clusters on 2D or 3D surface. |
||
| 73 | |||
| 74 | """ |
||
| 75 | |||
| 76 | def __init__(self, number_canvases = 1, size_row = 1): |
||
| 77 | """! |
||
| 78 | @brief Constructor of cluster visualizer. |
||
| 79 | |||
| 80 | @param[in] number_canvases (uint): Number of canvases that is used for visualization. |
||
| 81 | @param[in] size_row (uint): Amount of canvases that can be placed in one row. |
||
| 82 | |||
| 83 | Example: |
||
| 84 | @code |
||
| 85 | # load 2D data sample |
||
| 86 | sample_2d = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE1); |
||
| 87 | |||
| 88 | # load 3D data sample |
||
| 89 | sample_3d = read_sample(FCPS_SAMPLES.SAMPLE_HEPTA); |
||
| 90 | |||
| 91 | # extract clusters from the first sample using DBSCAN algorithm |
||
| 92 | dbscan_instance = dbscan(sample_2d, 0.4, 2, False); |
||
| 93 | dbscan_instance.process(); |
||
| 94 | clusters_sample_2d = dbscan_instance.get_clusters(); |
||
| 95 | |||
| 96 | # extract clusters from the second sample using DBSCAN algorithm |
||
| 97 | dbscan_instance = dbscan(sample_3d, 1, 3, True); |
||
| 98 | dbscan_instance.process(); |
||
| 99 | clusters_sample_3d = dbscan_instance.get_clusters(); |
||
| 100 | |||
| 101 | # create plot with two canvases where each row contains 2 canvases. |
||
| 102 | size = 2; |
||
| 103 | row_size = 2; |
||
| 104 | visualizer = cluster_visualizer(size, row_size); |
||
| 105 | |||
| 106 | # place clustering result of sample_2d to the first canvas |
||
| 107 | visualizer.append_clusters(clusters_sample_2d, sample_2d, 0, markersize = 5); |
||
| 108 | |||
| 109 | # place clustering result of sample_3d to the second canvas |
||
| 110 | visualizer.append_clusters(clusters_sample_3d, sample_3d, 1, markersize = 30); |
||
| 111 | |||
| 112 | # show plot |
||
| 113 | visualizer.show(); |
||
| 114 | @endcode |
||
| 115 | |||
| 116 | """ |
||
| 117 | |||
| 118 | self.__number_canvases = number_canvases; |
||
| 119 | self.__size_row = size_row; |
||
| 120 | self.__canvas_clusters = [ [] for _ in range(number_canvases) ]; |
||
| 121 | self.__canvas_dimensions = [ None for _ in range(number_canvases) ]; |
||
| 122 | self.__canvas_titles = [ None for _ in range(number_canvases) ]; |
||
| 123 | |||
| 124 | self.__default_2d_marker_size = 5; |
||
| 125 | self.__default_3d_marker_size = 30; |
||
| 126 | |||
| 127 | |||
| 128 | def append_cluster(self, cluster, data = None, canvas = 0, marker = '.', markersize = None, color = None): |
||
| 129 | """! |
||
| 130 | @brief Appends cluster to canvas for drawing. |
||
| 131 | |||
| 132 | @param[in] cluster (list): cluster that may consist of indexes of objects from the data or object itself. |
||
| 133 | @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data. |
||
| 134 | @param[in] canvas (uint): Number of canvas that should be used for displaying cluster. |
||
| 135 | @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas. |
||
| 136 | @param[in] markersize (uint): Size of marker. |
||
| 137 | @param[in] color (string): Color of marker. |
||
| 138 | |||
| 139 | @return Returns index of cluster descriptor on the canvas. |
||
| 140 | |||
| 141 | """ |
||
| 142 | |||
| 143 | if (len(cluster) == 0): |
||
| 144 | return; |
||
| 145 | |||
| 146 | if (canvas > self.__number_canvases): |
||
| 147 | raise NameError('Canvas does ' + canvas + ' not exists.');
|
||
| 148 | |||
| 149 | if (color is None): |
||
| 150 | index_color = len(self.__canvas_clusters[canvas]) % len(color_list.TITLES); |
||
| 151 | color = color_list.TITLES[index_color]; |
||
| 152 | |||
| 153 | added_canvas_descriptor = canvas_cluster_descr(cluster, data, marker, markersize, color); |
||
| 154 | self.__canvas_clusters[canvas].append( added_canvas_descriptor ); |
||
| 155 | |||
| 156 | dimension = 0; |
||
| 157 | if (data is None): |
||
| 158 | dimension = len(cluster[0]); |
||
| 159 | if (self.__canvas_dimensions[canvas] is None): |
||
| 160 | self.__canvas_dimensions[canvas] = dimension; |
||
| 161 | elif (self.__canvas_dimensions[canvas] != dimension): |
||
| 162 | raise NameError('Only clusters with the same dimension of objects can be displayed on canvas.');
|
||
| 163 | |||
| 164 | else: |
||
| 165 | dimension = len(data[0]); |
||
| 166 | if (self.__canvas_dimensions[canvas] is None): |
||
| 167 | self.__canvas_dimensions[canvas] = dimension; |
||
| 168 | elif (self.__canvas_dimensions[canvas] != dimension): |
||
| 169 | raise NameError('Only clusters with the same dimension of objects can be displayed on canvas.');
|
||
| 170 | |||
| 171 | if ( (dimension < 1) and (dimension > 3) ): |
||
| 172 | raise NameError('Only objects with size dimension 1 (1D plot), 2 (2D plot) or 3 (3D plot) can be displayed.');
|
||
| 173 | |||
| 174 | if (markersize is None): |
||
| 175 | if ( (dimension == 1) or (dimension == 2) ): |
||
| 176 | added_canvas_descriptor.markersize = self.__default_2d_marker_size; |
||
| 177 | elif (dimension == 3): |
||
| 178 | added_canvas_descriptor.markersize = self.__default_3d_marker_size; |
||
| 179 | |||
| 180 | return len(self.__canvas_clusters[canvas]) - 1; |
||
| 181 | |||
| 182 | |||
| 183 | def append_cluster_attribute(self, index_canvas, index_cluster, data, marker = None, markersize = None): |
||
| 184 | """! |
||
| 185 | @brief Append cluster attribure for cluster on specific canvas. |
||
| 186 | @details Attribute it is data that is visualized for specific cluster using its color, marker and markersize if last two is not specified. |
||
| 187 | |||
| 188 | @param[in] index_canvas (uint): Index canvas where cluster is located. |
||
| 189 | @param[in] index_cluster (uint): Index cluster whose attribute should be added. |
||
| 190 | @param[in] data (list): List of points (data) that represents attribute. |
||
| 191 | @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas. |
||
| 192 | @param[in] markersize (uint): Size of marker. |
||
| 193 | |||
| 194 | """ |
||
| 195 | |||
| 196 | cluster_descr = self.__canvas_clusters[index_canvas][index_cluster]; |
||
| 197 | attribute_marker = marker; |
||
| 198 | if (attribute_marker is None): |
||
| 199 | attribute_marker = cluster_descr.marker; |
||
| 200 | |||
| 201 | attribure_markersize = markersize; |
||
| 202 | if (attribure_markersize is None): |
||
| 203 | attribure_markersize = cluster_descr.markersize; |
||
| 204 | |||
| 205 | attribute_color = cluster_descr.color; |
||
| 206 | |||
| 207 | added_attribute_cluster_descriptor = canvas_cluster_descr(data, None, attribute_marker, attribure_markersize, attribute_color); |
||
| 208 | self.__canvas_clusters[index_canvas][index_cluster].attributes.append(added_attribute_cluster_descriptor); |
||
| 209 | |||
| 210 | |||
| 211 | def append_clusters(self, clusters, data = None, canvas = 0, marker = '.', markersize = None): |
||
| 212 | """! |
||
| 213 | @brief Appends list of cluster to canvas for drawing. |
||
| 214 | |||
| 215 | @param[in] clusters (list): List of clusters where each cluster may consist of indexes of objects from the data or object itself. |
||
| 216 | @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data. |
||
| 217 | @param[in] canvas (uint): Number of canvas that should be used for displaying clusters. |
||
| 218 | @param[in] marker (string): Marker that is used for displaying objects from clusters on the canvas. |
||
| 219 | @param[in] markersize (uint): Size of marker. |
||
| 220 | |||
| 221 | """ |
||
| 222 | |||
| 223 | for cluster in clusters: |
||
| 224 | self.append_cluster(cluster, data, canvas, marker, markersize); |
||
| 225 | |||
| 226 | |||
| 227 | def set_canvas_title(self, text, canvas = 0): |
||
| 228 | """! |
||
| 229 | @brief Set title for specified canvas. |
||
| 230 | |||
| 231 | @param[in] text (string): Title for canvas. |
||
| 232 | @param[in] canvas (uint): Index of canvas where title should be displayed. |
||
| 233 | |||
| 234 | """ |
||
| 235 | |||
| 236 | if (canvas > self.__number_canvases): |
||
| 237 | raise NameError('Canvas does ' + canvas + ' not exists.');
|
||
| 238 | |||
| 239 | self.__canvas_titles[canvas] = text; |
||
| 240 | |||
| 241 | |||
| 242 | def show(self, figure = None, visible_axis = True, visible_grid = True, display = True): |
||
| 243 | """! |
||
| 244 | @brief Shows clusters (visualize). |
||
| 245 | |||
| 246 | @param[in] figure (fig): Defines requirement to use specified figure, if None - new figure is created for drawing clusters. |
||
| 247 | @param[in] visible_axis (bool): Defines visibility of axes on each canvas, if True - axes are invisible. |
||
| 248 | @param[in] visible_grid (bool): Defines visibility of axes on each canvas, if True - grid is displayed. |
||
| 249 | @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." |
||
| 250 | |||
| 251 | @return (fig) Figure where clusters are shown. |
||
| 252 | |||
| 253 | """ |
||
| 254 | |||
| 255 | canvas_shift = 0; |
||
| 256 | cluster_figure = None; |
||
| 257 | if (figure is not None): |
||
| 258 | canvas_shift = len(figure.get_axes()); |
||
| 259 | cluster_figure = figure; |
||
| 260 | else: |
||
| 261 | cluster_figure = plt.figure(); |
||
| 262 | |||
| 263 | maximum_cols = self.__size_row; |
||
| 264 | maximum_rows = math.ceil( (self.__number_canvases + canvas_shift) / maximum_cols); |
||
| 265 | |||
| 266 | grid_spec = gridspec.GridSpec(maximum_rows, maximum_cols); |
||
| 267 | |||
| 268 | for index_canvas in range(len(self.__canvas_clusters)): |
||
| 269 | canvas_data = self.__canvas_clusters[index_canvas]; |
||
| 270 | dimension = self.__canvas_dimensions[index_canvas]; |
||
| 271 | |||
| 272 | #ax = axes[real_index]; |
||
| 273 | if ( (dimension == 1) or (dimension == 2) ): |
||
| 274 | ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift]); |
||
| 275 | else: |
||
| 276 | ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift], projection='3d'); |
||
| 277 | |||
| 278 | if (len(canvas_data) == 0): |
||
| 279 | plt.setp(ax, visible = False); |
||
| 280 | |||
| 281 | for cluster_descr in canvas_data: |
||
| 282 | self.__draw_canvas_cluster(ax, dimension, cluster_descr); |
||
| 283 | |||
| 284 | for attribute_descr in cluster_descr.attributes: |
||
| 285 | self.__draw_canvas_cluster(ax, dimension, attribute_descr); |
||
| 286 | |||
| 287 | if (visible_axis is True): |
||
| 288 | ax.xaxis.set_ticklabels([]); |
||
| 289 | ax.yaxis.set_ticklabels([]); |
||
| 290 | |||
| 291 | if (dimension == 3): |
||
| 292 | ax.zaxis.set_ticklabels([]); |
||
| 293 | |||
| 294 | if (self.__canvas_titles[index_canvas] is not None): |
||
| 295 | ax.set_title(self.__canvas_titles[index_canvas]); |
||
| 296 | |||
| 297 | ax.grid(visible_grid); |
||
| 298 | |||
| 299 | if (display is True): |
||
| 300 | plt.show(); |
||
| 301 | |||
| 302 | return cluster_figure; |
||
| 303 | |||
| 304 | |||
| 305 | """! |
||
| 306 | @brief Draw canvas cluster descriptor. |
||
| 307 | |||
| 308 | @param[in] ax (Axis): Axis of the canvas where canvas cluster descriptor should be displayed. |
||
| 309 | @param[in] dimension (uint): Canvas dimension. |
||
| 310 | @param[in] cluster_descr (canvas_cluster_descr): Canvas cluster descriptor that should be displayed. |
||
| 311 | |||
| 312 | @return (fig) Figure where clusters are shown. |
||
| 313 | |||
| 314 | """ |
||
| 315 | def __draw_canvas_cluster(self, ax, dimension, cluster_descr): |
||
| 316 | cluster = cluster_descr.cluster; |
||
| 317 | data = cluster_descr.data; |
||
| 318 | marker = cluster_descr.marker; |
||
| 319 | markersize = cluster_descr.markersize; |
||
| 320 | color = cluster_descr.color; |
||
| 321 | |||
| 322 | for item in cluster: |
||
| 323 | View Code Duplication | if (dimension == 1): |
|
|
|
|||
| 324 | if (data is None): |
||
| 325 | ax.plot(item[0], 0.0, color = color, marker = marker, markersize = markersize); |
||
| 326 | else: |
||
| 327 | ax.plot(data[item][0], 0.0, color = color, marker = marker, markersize = markersize); |
||
| 328 | |||
| 329 | elif (dimension == 2): |
||
| 330 | View Code Duplication | if (data is None): |
|
| 331 | ax.plot(item[0], item[1], color = color, marker = marker, markersize = markersize); |
||
| 332 | else: |
||
| 333 | ax.plot(data[item][0], data[item][1], color = color, marker = marker, markersize = markersize); |
||
| 334 | |||
| 335 | elif (dimension == 3): |
||
| 336 | if (data is None): |
||
| 337 | ax.scatter(item[0], item[1], item[2], c = color, marker = marker, s = markersize); |
||
| 338 | else: |
||
| 339 | ax.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker, s = markersize); |