if self.__ccore is True:<EOL><INDENT>(self.__clusters, self.__noise) = wrapper.dbscan(self.__pointer_data, self.__eps, self.__neighbors, self.__data_type)<EOL><DEDENT>else:<EOL><INDENT>if self.__data_type == '<STR_LIT>':<EOL><INDENT>self.__kdtree = kdtree(self.__pointer_data, range(len(self.__pointer_data)))<EOL><DEDENT>for i in range(<NUM_LIT:0>, len(self.__pointer_data)):<EOL><INDENT>if self.__visited[i] is False:<EOL><INDENT>cluster = self.__expand_cluster(i)<EOL>if cluster is not None:<EOL><INDENT>self.__clusters.append(cluster)<EOL><DEDENT><DEDENT><DEDENT>for i in range(<NUM_LIT:0>, len(self.__pointer_data)):<EOL><INDENT>if self.__belong[i] is False:<EOL><INDENT>self.__noise.append(i)<EOL><DEDENT><DEDENT><DEDENT>

!
        @brief Performs cluster analysis in line with rules of DBSCAN algorithm.

        @see get_clusters()
        @see get_noise()


return self.__clusters<EOL>

!
        @brief Returns allocated clusters.

        @remark Allocated clusters can be returned only after data processing (use method process()). Otherwise empty list is returned.

        @return (list) List of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()
        @see get_noise()


return self.__noise<EOL>

!
        @brief Returns allocated noise.

        @remark Allocated noise can be returned only after data processing (use method process() before). Otherwise empty list is returned.

        @return (list) List of indexes that are marked as a noise.

        @see process()
        @see get_clusters()


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


if data_type == '<STR_LIT>':<EOL><INDENT>return self.__neighbor_indexes_points<EOL><DEDENT>elif data_type == '<STR_LIT>':<EOL><INDENT>return self.__neighbor_indexes_distance_matrix<EOL><DEDENT>else:<EOL><INDENT>raise TypeError("<STR_LIT>" % data_type)<EOL><DEDENT>

!
        @brief Returns neighbor searcher in line with data type.

        @param[in] data_type (string): Data type (points or distance matrix).


cluster = None<EOL>self.__visited[index_point] = True<EOL>neighbors = self.__neighbor_searcher(index_point)<EOL>if len(neighbors) >= self.__neighbors:<EOL><INDENT>cluster = [index_point]<EOL>self.__belong[index_point] = True<EOL>for i in neighbors:<EOL><INDENT>if self.__visited[i] is False:<EOL><INDENT>self.__visited[i] = True<EOL>next_neighbors = self.__neighbor_searcher(i)<EOL>if len(next_neighbors) >= self.__neighbors:<EOL><INDENT>neighbors += [k for k in next_neighbors if ( (k in neighbors) == False) and k != index_point]<EOL><DEDENT><DEDENT>if self.__belong[i] is False:<EOL><INDENT>cluster.append(i)<EOL>self.__belong[i] = True<EOL><DEDENT><DEDENT><DEDENT>return cluster<EOL>

!
        @brief Expands cluster from specified point in the input data space.

        @param[in] index_point (list): Index of a point from the data.

        @return (list) Return tuple of list of indexes that belong to the same cluster and list of points that are marked as noise: (cluster, noise), or None if nothing has been expanded.


kdnodes = self.__kdtree.find_nearest_dist_nodes(self.__pointer_data[index_point], self.__eps)<EOL>return [node_tuple[<NUM_LIT:1>].payload for node_tuple in kdnodes if node_tuple[<NUM_LIT:1>].payload != index_point]<EOL>

!
        @brief Return neighbors of the specified object in case of sequence of points.

        @param[in] index_point (uint): Index point whose neighbors are should be found.

        @return (list) List of indexes of neighbors in line the connectivity radius.


distances = self.__pointer_data[index_point]<EOL>return [index_neighbor for index_neighbor in range(len(distances))<EOL>if ((distances[index_neighbor] <= self.__eps) and (index_neighbor != index_point))]<EOL>

!
        @brief Return neighbors of the specified object in case of distance matrix.

        @param[in] index_point (uint): Index point whose neighbors are should be found.

        @return (list) List of indexes of neighbors in line the connectivity radius.


def __init__(self, source_data, number_clusters, osc_initial_phases = initial_type.RANDOM_GAUSSIAN, initial_neighbors = <NUM_LIT:3>, increase_persent = <NUM_LIT>, ccore = True):

self.__ccore_network_pointer = None;<EOL>if (initial_neighbors >= len(source_data)):<EOL><INDENT>initial_neighbors = len(source_data) - <NUM_LIT:1>;<EOL><DEDENT>if ( (ccore is True) and ccore_library.workable() ):<EOL><INDENT>self.__ccore_network_pointer = wrapper.hsyncnet_create_network(source_data, number_clusters, osc_initial_phases, initial_neighbors, increase_persent);<EOL><DEDENT>else: <EOL><INDENT>super().__init__(source_data, <NUM_LIT:0>, initial_phases = osc_initial_phases, ccore=False);<EOL>self.__initial_neighbors = initial_neighbors;<EOL>self.__increase_persent = increase_persent;<EOL>self._number_clusters = number_clusters;<EOL><DEDENT>

!
        @brief Costructor of the oscillatory network hSyncNet for cluster analysis.

        @param[in] source_data (list): Input data set defines structure of the network.
        @param[in] number_clusters (uint): Number of clusters that should be allocated.
        @param[in] osc_initial_phases (initial_type): Type of initialization of initial values of phases of oscillators.
        @param[in] initial_neighbors (uint): Defines initial radius connectivity by calculation average distance to connect specify number of oscillators.
        @param[in] increase_persent (double): Percent of increasing of radius connectivity on each step (input values in range (0.0; 1.0) correspond to (0%; 100%)).
        @param[in] ccore (bool): If True than DLL CCORE (C++ solution) will be used for solving.


if (self.__ccore_network_pointer is not None):<EOL><INDENT>wrapper.hsyncnet_destroy_network(self.__ccore_network_pointer);<EOL>self.__ccore_network_pointer = None;<EOL><DEDENT>

!
        @brief Destructor of oscillatory network HSyncNet.


def process(self, order = <NUM_LIT>, solution = solve_type.FAST, collect_dynamic = False):

if (self.__ccore_network_pointer is not None):<EOL><INDENT>analyser = wrapper.hsyncnet_process(self.__ccore_network_pointer, order, solution, collect_dynamic);<EOL>return syncnet_analyser(None, None, analyser);<EOL><DEDENT>number_neighbors = self.__initial_neighbors;<EOL>current_number_clusters = float('<STR_LIT>');<EOL>dyn_phase = [];<EOL>dyn_time = [];<EOL>radius = average_neighbor_distance(self._osc_loc, number_neighbors);<EOL>increase_step = int(len(self._osc_loc) * self.__increase_persent);<EOL>if (increase_step < <NUM_LIT:1>):<EOL><INDENT>increase_step = <NUM_LIT:1>;<EOL><DEDENT>analyser = None;<EOL>while(current_number_clusters > self._number_clusters):<EOL><INDENT>self._create_connections(radius);<EOL>analyser = self.simulate_dynamic(order, solution, collect_dynamic);<EOL>if (collect_dynamic == True):<EOL><INDENT>if (len(dyn_phase) == <NUM_LIT:0>):<EOL><INDENT>self.__store_dynamic(dyn_phase, dyn_time, analyser, True);<EOL><DEDENT>self.__store_dynamic(dyn_phase, dyn_time, analyser, False);<EOL><DEDENT>clusters = analyser.allocate_sync_ensembles(<NUM_LIT>);<EOL>current_number_clusters = len(clusters);<EOL>number_neighbors += increase_step;<EOL>radius = self.__calculate_radius(number_neighbors, radius);<EOL><DEDENT>if (collect_dynamic != True):<EOL><INDENT>self.__store_dynamic(dyn_phase, dyn_time, analyser, False);<EOL><DEDENT>return syncnet_analyser(dyn_phase, dyn_time, None)<EOL>

!
        @brief Performs clustering of input data set in line with input parameters.

        @param[in] order (double): Level of local synchronization between oscillator that defines end of synchronization process, range [0..1].
        @param[in] solution (solve_type) Type of solving differential equation.
        @param[in] collect_dynamic (bool): If True - returns whole history of process synchronization otherwise - only final state (when process of clustering is over).

        @return (tuple) Returns dynamic of the network as tuple of lists on each iteration (time, oscillator_phases) that depends on collect_dynamic parameter.

        @see get_clusters()


def __calculate_radius(self, number_neighbors, radius):

if (number_neighbors >= len(self._osc_loc)):<EOL><INDENT>return radius * self.__increase_persent + radius;<EOL><DEDENT>return average_neighbor_distance(self._osc_loc, number_neighbors)<EOL>

!
        @brief Calculate new connectivity radius.

        @param[in] number_neighbors (uint): Average amount of neighbors that should be connected by new radius.
        @param[in] radius (double): Current connectivity radius.

        @return New connectivity radius.


def __store_dynamic(self, dyn_phase, dyn_time, analyser, begin_state):

if (begin_state is True):<EOL><INDENT>dyn_time.append(<NUM_LIT:0>);<EOL>dyn_phase.append(analyser.output[<NUM_LIT:0>]);<EOL><DEDENT>else:<EOL><INDENT>dyn_phase.append(analyser.output[len(analyser.output) - <NUM_LIT:1>]);<EOL>dyn_time.append(len(dyn_time));<EOL><DEDENT>

!
        @brief Store specified state of Sync network to hSync.

        @param[in] dyn_phase (list): Output dynamic of hSync where state should be stored.
        @param[in] dyn_time (list): Time points that correspond to output dynamic where new time point should be stored.
        @param[in] analyser (syncnet_analyser): Sync analyser where Sync states are stored.
        @param[in] begin_state (bool): If True the first state of Sync network is stored, otherwise the last state is stored.


def __init__(self, data, initial_centers, **kwargs):

self.__data = data<EOL>self.__clusters = []<EOL>self.__centers = initial_centers<EOL>self.__membership = []<EOL>self.__tolerance = kwargs.get('<STR_LIT>', <NUM_LIT>)<EOL>self.__itermax = kwargs.get('<STR_LIT>', <NUM_LIT:200>)<EOL>self.__m = kwargs.get('<STR_LIT:m>', <NUM_LIT:2>)<EOL>self.__degree = <NUM_LIT> / (self.__m - <NUM_LIT:1>)<EOL>self.__ccore = kwargs.get('<STR_LIT>', True)<EOL>if self.__ccore is True:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Initialize Fuzzy C-Means algorithm.

        @param[in] data (array_like): Input data that is presented as array of points (objects), each point should be represented by array_like data structure.
        @param[in] initial_centers (array_like): Initial coordinates of centers of clusters that are represented by array_like data structure: [center1, center2, ...].
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'tolerance', 'itermax', 'm').

        Keyword Args:

            - ccore (bool): Defines should be CCORE library (C++ pyclustering library) used instead of Python code or not.
            - tolerance (float): Stop condition: if maximum value of change of centers of clusters is less than tolerance then algorithm stops processing.
            - itermax (uint): Maximum number of iterations that is used for clustering process (by default: 200).
            - m (float): Hyper-parameter that controls how fuzzy the cluster will be. The higher it is, the fuzzier the cluster will be in the end.
               This parameter should be greater than 1 (by default: 2).


if self.__ccore is True:<EOL><INDENT>self.__process_by_ccore()<EOL><DEDENT>else:<EOL><INDENT>self.__process_by_python()<EOL><DEDENT>return self<EOL>

!
        @brief Performs cluster analysis in line with Fuzzy C-Means algorithm.

        @see get_clusters()
        @see get_centers()
        @see get_membership()


return self.__clusters<EOL>

!
        @brief Returns allocated clusters that consists of points that most likely (in line with membership) belong to
                these clusters.

        @remark Allocated clusters can be returned only after data processing (use method process()). Otherwise empty list is returned.

        @return (list) List of allocated clusters, each cluster contains indexes from input data.

        @see process()
        @see get_centers()
        @see get_membership()


return self.__centers<EOL>

!
        @brief Returns list of centers of allocated clusters.

        @return (array_like) Cluster centers.

        @see process()
        @see get_clusters()
        @see get_membership()


return self.__membership<EOL>

!
        @brief Returns cluster membership (probability) for each point in data.

        @return (array_like) Membership for each point in format [[Px1(c1), Px1(c2), ...], [Px2(c1), Px2(c2), ...], ...],
                 where [Px1(c1), Px1(c2), ...] membership for point x1.

        @see process()
        @see get_clusters()
        @see get_centers()


result = wrapper.fcm_algorithm(self.__data, self.__centers, self.__m, self.__tolerance, self.__itermax)<EOL>self.__clusters = result[wrapper.fcm_package_indexer.INDEX_CLUSTERS]<EOL>self.__centers = result[wrapper.fcm_package_indexer.INDEX_CENTERS]<EOL>self.__membership = result[wrapper.fcm_package_indexer.INDEX_MEMBERSHIP]<EOL>

!
        @brief Performs cluster analysis using C/C++ implementation.


self.__data = numpy.array(self.__data)<EOL>self.__centers = numpy.array(self.__centers)<EOL>self.__membership = numpy.zeros((len(self.__data), len(self.__centers)))<EOL>change = float('<STR_LIT>')<EOL>iteration = <NUM_LIT:0><EOL>while change > self.__tolerance and iteration < self.__itermax:<EOL><INDENT>self.__update_membership()<EOL>centers = self.__calculate_centers()<EOL>change = self.__calculate_changes(centers)<EOL>self.__centers = centers<EOL>iteration += <NUM_LIT:1><EOL><DEDENT>self.__extract_clusters()<EOL>

!
        @brief Performs cluster analysis using Python implementation.


dimension = self.__data.shape[<NUM_LIT:1>]<EOL>centers = numpy.zeros((len(self.__centers), dimension))<EOL>for i in range(len(self.__centers)):<EOL><INDENT>centers[i] = numpy.divide(self.__membership[:, i] @ self.__data, numpy.sum(self.__membership[:, i]))<EOL><DEDENT>return centers<EOL>

!
        @brief Calculate center using membership of each cluster.

        @return (list) Updated clusters as list of clusters. Each cluster contains indexes of objects from data.

        @return (numpy.array) Updated centers.


data_difference = numpy.zeros((len(self.__centers), len(self.__data)))<EOL>for i in range(len(self.__centers)):<EOL><INDENT>data_difference[i] = numpy.sum(numpy.square(self.__data - self.__centers[i]), axis=<NUM_LIT:1>)<EOL><DEDENT>for i in range(len(self.__data)):<EOL><INDENT>for j in range(len(self.__centers)):<EOL><INDENT>divider = sum([pow(data_difference[j][i] / data_difference[k][i], self.__degree) for k in range(len(self.__centers)) if data_difference[k][i] != <NUM_LIT:0.0>])<EOL>if divider != <NUM_LIT:0.0>:<EOL><INDENT>self.__membership[i][j] = <NUM_LIT:1.0> / divider<EOL><DEDENT>else:<EOL><INDENT>self.__membership[i][j] = <NUM_LIT:1.0><EOL><DEDENT><DEDENT><DEDENT>

!
        @brief Update membership for each point in line with current cluster centers.


changes = numpy.sum(numpy.square(self.__centers - updated_centers), axis=<NUM_LIT:1>).T<EOL>return numpy.max(changes)<EOL>

!
        @brief Calculate changes between centers.

        @return (float) Maximum change between centers.


return self._som<EOL>

!
        @brief The first layer of the oscillatory network - self-organized feature map.


return self._sync<EOL>

!
        @brief The second layer of the oscillatory network based on Kuramoto model.


def __init__(self, data, rows, cols, radius):

self._data = data;<EOL>self._radius = radius * radius;<EOL>self._som = som(rows, cols, conn_type = type_conn.grid_four, ccore = False);   <EOL>self._som_osc_table = list();<EOL>self._sync = None;       <EOL>self._struct = None;     <EOL>self._analyser = None<EOL>

!
        @brief Constructor of the double layer oscillatory network SYNC-SOM.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] rows (uint): Rows of neurons (number of neurons in column) in the input layer (self-organized feature map).
        @param[in] cols (uint): Columns of neurons (number of neurons in row) in the input later (self-organized feature map).
        @param[in] radius (double): Connectivity radius between objects that defines connection between oscillators in the second layer.


def process(self, collect_dynamic = False, order = <NUM_LIT>):

<EOL>self._som.train(self._data, <NUM_LIT:100>);<EOL>weights = list();<EOL>self._som_osc_table.clear();        <EOL>for i in range(self._som.size):<EOL><INDENT>if (self._som.awards[i] > <NUM_LIT:0>):<EOL><INDENT>weights.append(self._som.weights[i]);<EOL>self._som_osc_table.append(i);<EOL><DEDENT><DEDENT>self._sync = self.__create_sync_layer(weights);<EOL>self._analyser = self._sync.process(order, collect_dynamic = collect_dynamic);<EOL>return (self._analyser.time, self._analyser.output)<EOL>

!
        @brief Performs simulation of the oscillatory network.

        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
        @param[in] order (double): Order of process synchronization that should be considered as end of clustering, destributed 0..1.

        @return (tuple) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
                otherwise returns only last values (last step of simulation) of dynamic.

        @see get_som_clusters()
        @see get_clusters()


sync_layer = syncnet(weights, <NUM_LIT:0.0>, initial_phases = initial_type.RANDOM_GAUSSIAN, ccore = False);<EOL>for oscillator_index1 in range(<NUM_LIT:0>, len(sync_layer)):<EOL><INDENT>for oscillator_index2 in range(oscillator_index1 + <NUM_LIT:1>, len(sync_layer)):<EOL><INDENT>if (self.__has_object_connection(oscillator_index1, oscillator_index2)):<EOL><INDENT>sync_layer.set_connection(oscillator_index1, oscillator_index2);<EOL><DEDENT><DEDENT><DEDENT>return sync_layer<EOL>

!
        @brief Creates second layer of the network.

        @param[in] weights (list): List of weights of SOM neurons.

        @return (syncnet) Second layer of the network.


def __has_object_connection(self, oscillator_index1, oscillator_index2):

som_neuron_index1 = self._som_osc_table[oscillator_index1];<EOL>som_neuron_index2 = self._som_osc_table[oscillator_index2];<EOL>for index_object1 in self._som.capture_objects[som_neuron_index1]:<EOL><INDENT>for index_object2 in self._som.capture_objects[som_neuron_index2]:<EOL><INDENT>distance = euclidean_distance_square(self._data[index_object1], self._data[index_object2]);<EOL>if (distance <= self._radius):<EOL><INDENT>return True;<EOL><DEDENT><DEDENT><DEDENT>return False<EOL>

!
        @brief Searches for pair of objects that are encoded by specified neurons and that are connected in line with connectivity radius.

        @param[in] oscillator_index1 (uint): Index of the first oscillator in the second layer.
        @param[in] oscillator_index2 (uint): Index of the second oscillator in the second layer.

        @return (bool) True - if there is pair of connected objects encoded by specified oscillators.


sync_clusters = self._analyser.allocate_clusters();<EOL>som_clusters = list();<EOL>for oscillators in sync_clusters:<EOL><INDENT>cluster = list();<EOL>for index_oscillator in oscillators:<EOL><INDENT>index_neuron = self._som_osc_table[index_oscillator];<EOL>cluster.append(index_neuron);<EOL><DEDENT>som_clusters.append(cluster);<EOL><DEDENT>return som_clusters<EOL>

!
        @brief Returns clusters with SOM neurons that encode input features in line with result of synchronization in the second (Sync) layer.

        @return (list) List of clusters that are represented by lists of indexes of neurons that encode input data.

        @see process()
        @see get_clusters()


def get_clusters(self, eps = <NUM_LIT:0.1>):

sync_clusters = self._analyser.allocate_clusters(eps)       <EOL>clusters = list()<EOL>for oscillators in sync_clusters:<EOL><INDENT>cluster = list()<EOL>for index_oscillator in oscillators:<EOL><INDENT>index_neuron = self._som_osc_table[index_oscillator]<EOL>cluster += self._som.capture_objects[index_neuron]<EOL><DEDENT>clusters.append(cluster)<EOL><DEDENT>return clusters<EOL>

!
        @brief Returns clusters in line with ensembles of synchronous oscillators where each synchronous ensemble corresponds to only one cluster.

        @param[in] eps (double): Maximum error for allocation of synchronous ensemble oscillators.

        @return (list) List of grours (lists) of indexes of synchronous oscillators that corresponds to index of objects.

        @see process()
        @see get_som_clusters()


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


!
        @brief Shows visual representation of the first (SOM) layer.


!
        @brief Shows visual representation of the second (Sync) layer.


"<STR_LIT>"<EOL>"<STR_LIT>"<EOL>template_clustering(<NUM_LIT:2>, FCPS_SAMPLES.SAMPLE_ATOM, <NUM_LIT:20>, <NUM_LIT>)<EOL>

Impossible to obtain parameters that satisfy us, it seems to me that compression = 0.2 is key parameter here, because results of clustering doesn't depend on number of represented points, except 0.


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_ELONGATE)<EOL>


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT:0.5>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_LSUN)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>], [<NUM_LIT:0.0>, -<NUM_LIT>], [<NUM_LIT>, -<NUM_LIT>], [<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, -<NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TARGET)<EOL>


start_centers = [[-<NUM_LIT>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_WING_NUT)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE1, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE1, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE2, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE2, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT:0.1>], [<NUM_LIT>, <NUM_LIT:1.0>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE3, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE3, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT:0.0>, <NUM_LIT:1.0>], [<NUM_LIT:0.0>, <NUM_LIT:0.0>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE5, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_SIMPLE5, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_ELONGATE, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_ELONGATE, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT:0.5>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_LSUN, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_LSUN, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>], [<NUM_LIT:0.0>, -<NUM_LIT>], [<NUM_LIT>, -<NUM_LIT>], [<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, -<NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TARGET, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TARGET, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TWO_DIAMONDS, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TWO_DIAMONDS, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


start_centers = [[<NUM_LIT:0.0>, <NUM_LIT:0.0>, <NUM_LIT:0.0>], [<NUM_LIT>, <NUM_LIT:0.0>, <NUM_LIT:0.0>], [-<NUM_LIT>, <NUM_LIT:0.0>, <NUM_LIT:0.0>], [<NUM_LIT:0.0>, <NUM_LIT>, <NUM_LIT:0.0>], [<NUM_LIT:0.0>, -<NUM_LIT>, <NUM_LIT:0.0>], [<NUM_LIT:0.0>, <NUM_LIT:0.0>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_HEPTA, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION)<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_HEPTA, criterion = splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH)<EOL>


template_clustering(<NUM_LIT>, <NUM_LIT:7>, FCPS_SAMPLES.SAMPLE_TWO_DIAMONDS)<EOL>

It's hard to choose properly parameters, but it's OK


template_clustering(<NUM_LIT>, <NUM_LIT:2>, FCPS_SAMPLES.SAMPLE_WING_NUT)<EOL>


template_clustering(<NUM_LIT:0.5>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_GOLF_BALL)<EOL>


def experiment_execution_time(ccore = False):

template_clustering(<NUM_LIT:0.5>, <NUM_LIT:3>, SIMPLE_SAMPLES.SAMPLE_SIMPLE1, False, ccore)<EOL>template_clustering(<NUM_LIT:1>, <NUM_LIT:2>, SIMPLE_SAMPLES.SAMPLE_SIMPLE2, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:3>, SIMPLE_SAMPLES.SAMPLE_SIMPLE3, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:3>, SIMPLE_SAMPLES.SAMPLE_SIMPLE4, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:3>, SIMPLE_SAMPLES.SAMPLE_SIMPLE5, False, ccore)<EOL>template_clustering(<NUM_LIT:0.5>, <NUM_LIT:3>, SIMPLE_SAMPLES.SAMPLE_ELONGATE, False, ccore)<EOL>template_clustering(<NUM_LIT:0.5>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_LSUN, False, ccore)<EOL>template_clustering(<NUM_LIT:0.5>, <NUM_LIT:2>, FCPS_SAMPLES.SAMPLE_TARGET, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:7>, FCPS_SAMPLES.SAMPLE_TWO_DIAMONDS, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:2>, FCPS_SAMPLES.SAMPLE_WING_NUT, False, ccore)<EOL>template_clustering(<NUM_LIT:0.5>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_CHAINLINK, False, ccore)<EOL>template_clustering(<NUM_LIT:1>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_HEPTA, False, ccore)<EOL>template_clustering(<NUM_LIT>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_TETRA, False, ccore)<EOL>template_clustering(<NUM_LIT:15>, <NUM_LIT:3>, FCPS_SAMPLES.SAMPLE_ATOM, False, ccore)<EOL>


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, SIMPLE_SAMPLES.SAMPLE_ELONGATE)<EOL>


start_centers = [[<NUM_LIT:1.0>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT:0.5>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_LSUN)<EOL>


start_centers = [[<NUM_LIT>, <NUM_LIT>], [<NUM_LIT:0.0>, -<NUM_LIT>], [<NUM_LIT>, -<NUM_LIT>], [<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, <NUM_LIT>], [-<NUM_LIT>, -<NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_TARGET)<EOL>


start_centers = [[-<NUM_LIT>, <NUM_LIT>], [<NUM_LIT>, <NUM_LIT>]]<EOL>template_clustering(start_centers, FCPS_SAMPLES.SAMPLE_WING_NUT)<EOL>


def __init__(self, cluster, data, marker, markersize, color):

<EOL>self.cluster = cluster<EOL>self.data = data<EOL>self.marker = marker<EOL>self.markersize = markersize<EOL>self.color = color<EOL>self.attributes = []<EOL>

!
        @brief Constructor of cluster representation on the canvas.

        @param[in] cluster (list): Single cluster that consists of objects or indexes from data.
        @param[in] data (list): Objects that should be displayed, can be None if clusters consist of objects instead of indexes.
        @param[in] marker (string): Type of marker that is used for drawing objects.
        @param[in] markersize (uint): Size of marker that is used for drawing objects.
        @param[in] color (string): Color of the marker that is used for drawing objects.


self.__clusters = []<EOL>self.__figure = None<EOL>self.__grid_spec = None<EOL>

!
        @brief Constructs cluster visualizer for multidimensional data.
        @details The visualizer is suitable more data whose dimension is bigger than 3.


def append_cluster(self, cluster, data = None, marker = '<STR_LIT:.>', markersize = None, color = None):

if len(cluster) == <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>markersize = markersize or <NUM_LIT:5><EOL>if color is None:<EOL><INDENT>index_color = len(self.__clusters) % len(color_list.TITLES)<EOL>color = color_list.TITLES[index_color]<EOL><DEDENT>cluster_descriptor = canvas_cluster_descr(cluster, data, marker, markersize, color)<EOL>self.__clusters.append(cluster_descriptor)<EOL>

!
        @brief Appends cluster for visualization.

        @param[in] cluster (list): cluster that may consist of indexes of objects from the data or object itself.
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
        @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas.
        @param[in] markersize (uint): Size of marker.
        @param[in] color (string): Color of marker.

        @return Returns index of cluster descriptor on the canvas.


def append_clusters(self, clusters, data=None, marker='<STR_LIT:.>', markersize=None):

for cluster in clusters:<EOL><INDENT>self.append_cluster(cluster, data, marker, markersize)<EOL><DEDENT>

!
        @brief Appends list of cluster for visualization.

        @param[in] clusters (list): List of clusters where each cluster may consist of indexes of objects from the data or object itself.
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
        @param[in] marker (string): Marker that is used for displaying objects from clusters on the canvas.
        @param[in] markersize (uint): Size of marker.


def show(self, pair_filter=None, **kwargs):

if not len(self.__clusters) > <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>cluster_data = self.__clusters[<NUM_LIT:0>].data or self.__clusters[<NUM_LIT:0>].cluster<EOL>dimension = len(cluster_data[<NUM_LIT:0>])<EOL>acceptable_pairs = pair_filter or []<EOL>pairs = []<EOL>amount_axis = <NUM_LIT:1><EOL>axis_storage = []<EOL>if dimension > <NUM_LIT:1>:<EOL><INDENT>pairs = self.__create_pairs(dimension, acceptable_pairs)<EOL>amount_axis = len(pairs)<EOL><DEDENT>self.__figure = plt.figure()<EOL>self.__grid_spec = self.__create_grid_spec(amount_axis, kwargs.get('<STR_LIT>', <NUM_LIT:4>))<EOL>for index in range(amount_axis):<EOL><INDENT>ax = self.__create_canvas(dimension, pairs, index, **kwargs)<EOL>axis_storage.append(ax)<EOL><DEDENT>for cluster_descr in self.__clusters:<EOL><INDENT>self.__draw_canvas_cluster(axis_storage, cluster_descr, pairs)<EOL><DEDENT>plt.show()<EOL>

!
        @brief Shows clusters (visualize) in multi-dimensional space.

        @param[in] pair_filter (list): List of coordinate pairs that should be displayed. This argument is used as a filter.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'visible_axis' 'visible_labels', 'visible_grid', 'row_size').

        Keyword Args:

            - visible_axis (bool): Defines visibility of axes on each canvas, if True - axes are visible.
               By default axis of each canvas are not displayed.
            - visible_labels (bool): Defines visibility of labels on each canvas, if True - labels is displayed.
               By default labels of each canvas are displayed.
            - visible_grid (bool): Defines visibility of grid on each canvas, if True - grid is displayed.
               By default grid of each canvas is displayed.
            - max_row_size (uint): Maximum number of canvases on one row.


def __create_grid_spec(self, amount_axis, max_row_size):

row_size = amount_axis<EOL>if row_size > max_row_size:<EOL><INDENT>row_size = max_row_size<EOL><DEDENT>col_size = math.ceil(amount_axis / row_size)<EOL>return gridspec.GridSpec(col_size, row_size)<EOL>

!
        @brief Create grid specification for figure to place canvases.

        @param[in] amount_axis (uint): Amount of canvases that should be organized by the created grid specification.
        @param[in] max_row_size (max_row_size): Maximum number of canvases on one row.

        @return (gridspec.GridSpec) Grid specification to place canvases on figure.


def __create_pairs(self, dimension, acceptable_pairs):

if len(acceptable_pairs) > <NUM_LIT:0>:<EOL><INDENT>return acceptable_pairs<EOL><DEDENT>return list(itertools.combinations(range(dimension), <NUM_LIT:2>))<EOL>

!
        @brief Create coordinate pairs that should be displayed.

        @param[in] dimension (uint): Data-space dimension.
        @param[in] acceptable_pairs (list): List of coordinate pairs that should be displayed.

        @return (list) List of coordinate pairs that should be displayed.


def __create_canvas(self, dimension, pairs, position, **kwargs):

visible_grid = kwargs.get('<STR_LIT>', True)<EOL>visible_labels = kwargs.get('<STR_LIT>', True)<EOL>visible_axis = kwargs.get('<STR_LIT>', False)<EOL>ax = self.__figure.add_subplot(self.__grid_spec[position])<EOL>if dimension > <NUM_LIT:1>:<EOL><INDENT>if visible_labels:<EOL><INDENT>ax.set_xlabel("<STR_LIT>" % pairs[position][<NUM_LIT:0>])<EOL>ax.set_ylabel("<STR_LIT>" % pairs[position][<NUM_LIT:1>])<EOL><DEDENT><DEDENT>else:<EOL><INDENT>ax.set_ylim(-<NUM_LIT:0.5>, <NUM_LIT:0.5>)<EOL>ax.set_yticklabels([])<EOL><DEDENT>if visible_grid:<EOL><INDENT>ax.grid(True)<EOL><DEDENT>if not visible_axis:<EOL><INDENT>ax.set_yticklabels([])<EOL>ax.set_xticklabels([])<EOL><DEDENT>return ax<EOL>

!
        @brief Create new canvas with user defined parameters to display cluster or chunk of cluster on it.

        @param[in] dimension (uint): Data-space dimension.
        @param[in] pairs (list): Pair of coordinates that will be displayed on the canvas. If empty than label will not
                    be displayed on the canvas.
        @param[in] position (uint): Index position of canvas on a grid.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'visible_axis' 'visible_labels', 'visible_grid').

        Keyword Args:

            - visible_axis (bool): Defines visibility of axes on each canvas, if True - axes are visible.
               By default axis are not displayed.
            - visible_labels (bool): Defines visibility of labels on each canvas, if True - labels is displayed.
               By default labels are displayed.
            - visible_grid (bool): Defines visibility of grid on each canvas, if True - grid is displayed.
               By default grid is displayed.

        @return (matplotlib.Axis) Canvas to display cluster of chuck of cluster.


def __draw_canvas_cluster(self, axis_storage, cluster_descr, pairs):

for index_axis in range(len(axis_storage)):<EOL><INDENT>for item in cluster_descr.cluster:<EOL><INDENT>if len(pairs) > <NUM_LIT:0>:<EOL><INDENT>self.__draw_cluster_item_multi_dimension(axis_storage[index_axis], pairs[index_axis], item, cluster_descr)<EOL><DEDENT>else:<EOL><INDENT>self.__draw_cluster_item_one_dimension(axis_storage[index_axis], item, cluster_descr)<EOL><DEDENT><DEDENT><DEDENT>

!
        @brief Draw clusters.

        @param[in] axis_storage (list): List of matplotlib axis where cluster dimensional chunks are displayed.
        @param[in] cluster_descr (canvas_cluster_descr): Canvas cluster descriptor that should be displayed.
        @param[in] pairs (list): List of coordinates that should be displayed.


def __draw_cluster_item_multi_dimension(self, ax, pair, item, cluster_descr):

index_dimension1 = pair[<NUM_LIT:0>]<EOL>index_dimension2 = pair[<NUM_LIT:1>]<EOL>if cluster_descr.data is None:<EOL><INDENT>ax.plot(item[index_dimension1], item[index_dimension2],<EOL>color=cluster_descr.color, marker=cluster_descr.marker, markersize=cluster_descr.markersize)<EOL><DEDENT>else:<EOL><INDENT>ax.plot(cluster_descr.data[item][index_dimension1], cluster_descr.data[item][index_dimension2],<EOL>color=cluster_descr.color, marker=cluster_descr.marker, markersize=cluster_descr.markersize)<EOL><DEDENT>

!
        @brief Draw cluster chunk defined by pair coordinates in data space with dimension greater than 1.

        @param[in] ax (axis): Matplotlib axis that is used to display chunk of cluster point.
        @param[in] pair (list): Coordinate of the point that should be displayed.
        @param[in] item (list): Data point or index of data point.
        @param[in] cluster_descr (canvas_cluster_descr): Cluster description whose point is visualized.


def __draw_cluster_item_one_dimension(self, ax, item, cluster_descr):

if cluster_descr.data is None:<EOL><INDENT>ax.plot(item[<NUM_LIT:0>], <NUM_LIT:0.0>,<EOL>color=cluster_descr.color, marker=cluster_descr.marker, markersize=cluster_descr.markersize)<EOL><DEDENT>else:<EOL><INDENT>ax.plot(cluster_descr.data[item][<NUM_LIT:0>], <NUM_LIT:0.0>,<EOL>color=cluster_descr.color, marker=cluster_descr.marker, markersize=cluster_descr.markersize)<EOL><DEDENT>

!
        @brief Draw cluster point in one dimensional data space..

        @param[in] ax (axis): Matplotlib axis that is used to display chunk of cluster point.
        @param[in] item (list): Data point or index of data point.
        @param[in] cluster_descr (canvas_cluster_descr): Cluster description whose point is visualized.


def __init__(self, number_canvases = <NUM_LIT:1>, size_row = <NUM_LIT:1>, titles = None):

self.__number_canvases = number_canvases<EOL>self.__size_row = size_row<EOL>self.__canvas_clusters = [ [] for _ in range(number_canvases) ]<EOL>self.__canvas_dimensions = [ None for _ in range(number_canvases) ]<EOL>self.__canvas_titles = [ None for _ in range(number_canvases) ]<EOL>if titles is not None:<EOL><INDENT>self.__canvas_titles = titles<EOL><DEDENT>self.__default_2d_marker_size = <NUM_LIT:5><EOL>self.__default_3d_marker_size = <NUM_LIT:30><EOL>

!
        @brief Constructor of cluster visualizer.

        @param[in] number_canvases (uint): Number of canvases that is used for visualization.
        @param[in] size_row (uint): Amount of canvases that can be placed in one row.
        @param[in] titles (list): List of canvas's titles.

        Example:
        @code
            # load 2D data sample
            sample_2d = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE1);

            # load 3D data sample
            sample_3d = read_sample(FCPS_SAMPLES.SAMPLE_HEPTA);

            # extract clusters from the first sample using DBSCAN algorithm
            dbscan_instance = dbscan(sample_2d, 0.4, 2, False);
            dbscan_instance.process();
            clusters_sample_2d = dbscan_instance.get_clusters();

            # extract clusters from the second sample using DBSCAN algorithm
            dbscan_instance = dbscan(sample_3d, 1, 3, True);
            dbscan_instance.process();
            clusters_sample_3d = dbscan_instance.get_clusters();

            # create plot with two canvases where each row contains 2 canvases.
            size = 2;
            row_size = 2;
            visualizer = cluster_visualizer(size, row_size);

            # place clustering result of sample_2d to the first canvas
            visualizer.append_clusters(clusters_sample_2d, sample_2d, 0, markersize = 5);

            # place clustering result of sample_3d to the second canvas
            visualizer.append_clusters(clusters_sample_3d, sample_3d, 1, markersize = 30);

            # show plot
            visualizer.show();
        @endcode


def append_cluster(self, cluster, data=None, canvas=<NUM_LIT:0>, marker='<STR_LIT:.>', markersize=None, color=None):

if len(cluster) == <NUM_LIT:0>:<EOL><INDENT>return<EOL><DEDENT>if canvas > self.__number_canvases or canvas < <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" % self.__number_canvases or canvas)<EOL><DEDENT>if color is None:<EOL><INDENT>index_color = len(self.__canvas_clusters[canvas]) % len(color_list.TITLES)<EOL>color = color_list.TITLES[index_color]<EOL><DEDENT>added_canvas_descriptor = canvas_cluster_descr(cluster, data, marker, markersize, color)<EOL>self.__canvas_clusters[canvas].append( added_canvas_descriptor )<EOL>if data is None:<EOL><INDENT>dimension = len(cluster[<NUM_LIT:0>])<EOL>if self.__canvas_dimensions[canvas] is None:<EOL><INDENT>self.__canvas_dimensions[canvas] = dimension<EOL><DEDENT>elif self.__canvas_dimensions[canvas] != dimension:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT><DEDENT>else:<EOL><INDENT>dimension = len(data[<NUM_LIT:0>])<EOL>if self.__canvas_dimensions[canvas] is None:<EOL><INDENT>self.__canvas_dimensions[canvas] = dimension<EOL><DEDENT>elif self.__canvas_dimensions[canvas] != dimension:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT><DEDENT>if (dimension < <NUM_LIT:1>) or (dimension > <NUM_LIT:3>):<EOL><INDENT>raise ValueError("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>if markersize is None:<EOL><INDENT>if (dimension == <NUM_LIT:1>) or (dimension == <NUM_LIT:2>):<EOL><INDENT>added_canvas_descriptor.markersize = self.__default_2d_marker_size<EOL><DEDENT>elif dimension == <NUM_LIT:3>:<EOL><INDENT>added_canvas_descriptor.markersize = self.__default_3d_marker_size<EOL><DEDENT><DEDENT>return len(self.__canvas_clusters[canvas]) - <NUM_LIT:1><EOL>

!
        @brief Appends cluster to canvas for drawing.

        @param[in] cluster (list): cluster that may consist of indexes of objects from the data or object itself.
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
        @param[in] canvas (uint): Number of canvas that should be used for displaying cluster.
        @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas.
        @param[in] markersize (uint): Size of marker.
        @param[in] color (string): Color of marker.

        @return Returns index of cluster descriptor on the canvas.


def append_cluster_attribute(self, index_canvas, index_cluster, data, marker = None, markersize = None):

cluster_descr = self.__canvas_clusters[index_canvas][index_cluster]<EOL>attribute_marker = marker<EOL>if attribute_marker is None:<EOL><INDENT>attribute_marker = cluster_descr.marker<EOL><DEDENT>attribure_markersize = markersize<EOL>if attribure_markersize is None:<EOL><INDENT>attribure_markersize = cluster_descr.markersize<EOL><DEDENT>attribute_color = cluster_descr.color<EOL>added_attribute_cluster_descriptor = canvas_cluster_descr(data, None, attribute_marker, attribure_markersize, attribute_color)<EOL>self.__canvas_clusters[index_canvas][index_cluster].attributes.append(added_attribute_cluster_descriptor)<EOL>

!
        @brief Append cluster attribure for cluster on specific canvas.
        @details Attribute it is data that is visualized for specific cluster using its color, marker and markersize if last two is not specified.

        @param[in] index_canvas (uint): Index canvas where cluster is located.
        @param[in] index_cluster (uint): Index cluster whose attribute should be added.
        @param[in] data (list): List of points (data) that represents attribute.
        @param[in] marker (string): Marker that is used for displaying objects from cluster on the canvas.
        @param[in] markersize (uint): Size of marker.


def append_clusters(self, clusters, data = None, canvas = <NUM_LIT:0>, marker = '<STR_LIT:.>', markersize = None):

for cluster in clusters:<EOL><INDENT>self.append_cluster(cluster, data, canvas, marker, markersize)<EOL><DEDENT>

!
        @brief Appends list of cluster to canvas for drawing.

        @param[in] clusters (list): List of clusters where each cluster may consist of indexes of objects from the data or object itself.
        @param[in] data (list): If defines that each element of cluster is considered as a index of object from the data.
        @param[in] canvas (uint): Number of canvas that should be used for displaying clusters.
        @param[in] marker (string): Marker that is used for displaying objects from clusters on the canvas.
        @param[in] markersize (uint): Size of marker.


def set_canvas_title(self, text, canvas = <NUM_LIT:0>):

if canvas > self.__number_canvases:<EOL><INDENT>raise NameError('<STR_LIT>' + canvas + '<STR_LIT>')<EOL><DEDENT>self.__canvas_titles[canvas] = text<EOL>

!
        @brief Set title for specified canvas.

        @param[in] text (string): Title for canvas.
        @param[in] canvas (uint): Index of canvas where title should be displayed.


def get_cluster_color(self, index_cluster, index_canvas):

return self.__canvas_clusters[index_canvas][index_cluster].color<EOL>

!
        @brief Returns cluster color on specified canvas.


def show(self, figure=None, invisible_axis=True, visible_grid=True, display=True, shift=None):

canvas_shift = shift<EOL>if canvas_shift is None:<EOL><INDENT>if figure is not None:<EOL><INDENT>canvas_shift = len(figure.get_axes())<EOL><DEDENT>else:<EOL><INDENT>canvas_shift = <NUM_LIT:0><EOL><DEDENT><DEDENT>if figure is not None:<EOL><INDENT>cluster_figure = figure<EOL><DEDENT>else:<EOL><INDENT>cluster_figure = plt.figure()<EOL><DEDENT>maximum_cols = self.__size_row<EOL>maximum_rows = math.ceil( (self.__number_canvases + canvas_shift) / maximum_cols)<EOL>grid_spec = gridspec.GridSpec(maximum_rows, maximum_cols)<EOL>for index_canvas in range(len(self.__canvas_clusters)):<EOL><INDENT>canvas_data = self.__canvas_clusters[index_canvas]<EOL>if len(canvas_data) == <NUM_LIT:0>:<EOL><INDENT>continue<EOL><DEDENT>dimension = self.__canvas_dimensions[index_canvas]<EOL>if (dimension == <NUM_LIT:1>) or (dimension == <NUM_LIT:2>):<EOL><INDENT>ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift])<EOL><DEDENT>else:<EOL><INDENT>ax = cluster_figure.add_subplot(grid_spec[index_canvas + canvas_shift], projection='<STR_LIT>')<EOL><DEDENT>if len(canvas_data) == <NUM_LIT:0>:<EOL><INDENT>plt.setp(ax, visible=False)<EOL><DEDENT>for cluster_descr in canvas_data:<EOL><INDENT>self.__draw_canvas_cluster(ax, dimension, cluster_descr)<EOL>for attribute_descr in cluster_descr.attributes:<EOL><INDENT>self.__draw_canvas_cluster(ax, dimension, attribute_descr)<EOL><DEDENT><DEDENT>if invisible_axis is True:<EOL><INDENT>ax.xaxis.set_ticklabels([])<EOL>ax.yaxis.set_ticklabels([])<EOL>if (dimension == <NUM_LIT:3>):<EOL><INDENT>ax.zaxis.set_ticklabels([])<EOL><DEDENT><DEDENT>if self.__canvas_titles[index_canvas] is not None:<EOL><INDENT>ax.set_title(self.__canvas_titles[index_canvas])<EOL><DEDENT>ax.grid(visible_grid)<EOL><DEDENT>if display is True:<EOL><INDENT>plt.show()<EOL><DEDENT>return cluster_figure<EOL>

!
        @brief Shows clusters (visualize).

        @param[in] figure (fig): Defines requirement to use specified figure, if None - new figure is created for drawing clusters.
        @param[in] invisible_axis (bool): Defines visibility of axes on each canvas, if True - axes are invisible.
        @param[in] visible_grid (bool): Defines visibility of grid on each canvas, if True - grid is displayed.
        @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."
        @param[in] shift (uint): Force canvas shift value - defines canvas index from which custers should be visualized.

        @return (fig) Figure where clusters are shown.


def __draw_canvas_cluster(self, ax, dimension, cluster_descr):

cluster = cluster_descr.cluster<EOL>data = cluster_descr.data<EOL>marker = cluster_descr.marker<EOL>markersize = cluster_descr.markersize<EOL>color = cluster_descr.color<EOL>for item in cluster:<EOL><INDENT>if dimension == <NUM_LIT:1>:<EOL><INDENT>if data is None:<EOL><INDENT>ax.plot(item[<NUM_LIT:0>], <NUM_LIT:0.0>, color = color, marker = marker, markersize = markersize)<EOL><DEDENT>else:<EOL><INDENT>ax.plot(data[item][<NUM_LIT:0>], <NUM_LIT:0.0>, color = color, marker = marker, markersize = markersize)<EOL><DEDENT><DEDENT>elif dimension == <NUM_LIT:2>:<EOL><INDENT>if data is None:<EOL><INDENT>ax.plot(item[<NUM_LIT:0>], item[<NUM_LIT:1>], color = color, marker = marker, markersize = markersize)<EOL><DEDENT>else:<EOL><INDENT>ax.plot(data[item][<NUM_LIT:0>], data[item][<NUM_LIT:1>], color = color, marker = marker, markersize = markersize)<EOL><DEDENT><DEDENT>elif dimension == <NUM_LIT:3>:<EOL><INDENT>if data is None:<EOL><INDENT>ax.scatter(item[<NUM_LIT:0>], item[<NUM_LIT:1>], item[<NUM_LIT:2>], c = color, marker = marker, s = markersize)<EOL><DEDENT>else:<EOL><INDENT>ax.scatter(data[item][<NUM_LIT:0>], data[item][<NUM_LIT:1>], data[item][<NUM_LIT:2>], c = color, marker = marker, s = markersize)<EOL><DEDENT><DEDENT><DEDENT>

!
        @brief Draw canvas cluster descriptor.

        @param[in] ax (Axis): Axis of the canvas where canvas cluster descriptor should be displayed.
        @param[in] dimension (uint): Canvas dimension.
        @param[in] cluster_descr (canvas_cluster_descr): Canvas cluster descriptor that should be displayed.

        @return (fig) Figure where clusters are shown.


def __init__(self, data, number_clusters, link = None, ccore = True):

self.__pointer_data = data<EOL>self.__number_clusters = number_clusters<EOL>self.__similarity = link<EOL>if self.__similarity is None:<EOL><INDENT>self.__similarity = type_link.CENTROID_LINK<EOL><DEDENT>self.__clusters = []<EOL>self.__ccore = ccore<EOL>if (self.__ccore):<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>if (self.__similarity == type_link.CENTROID_LINK):<EOL><INDENT>self.__centers = self.__pointer_data.copy()<EOL><DEDENT>

!
        @brief Constructor of agglomerative hierarchical algorithm.

        @param[in] data (list): Input data that is presented as a list of points (objects), each point should be represented by list, for example
                    [[0.1, 0.2], [0.4, 0.5], [1.3, 0.9]].
        @param[in] number_clusters (uint): Number of clusters that should be allocated.
        @param[in] link (type_link): Link type that is used for calculation similarity between objects and clusters, if it is not specified centroid link will be used by default.
        @param[in] ccore (bool): Defines should be CCORE (C++ pyclustering library) used instead of Python code or not (by default it is 'False').


if (self.__ccore is True):<EOL><INDENT>self.__clusters = wrapper.agglomerative_algorithm(self.__pointer_data, self.__number_clusters, self.__similarity);<EOL><DEDENT>else:<EOL><INDENT>self.__clusters = [[index] for index in range(<NUM_LIT:0>, len(self.__pointer_data))];<EOL>current_number_clusters = len(self.__clusters);<EOL>while (current_number_clusters > self.__number_clusters):<EOL><INDENT>self.__merge_similar_clusters();<EOL>current_number_clusters = len(self.__clusters);<EOL><DEDENT><DEDENT>

!
        @brief Performs cluster analysis in line with rules of agglomerative algorithm and similarity.

        @see get_clusters()


return self.__clusters<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects in list of data.

        @remark Results of clustering can be obtained using corresponding gets methods.

        @return (list) List of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


if (self.__similarity == type_link.AVERAGE_LINK):<EOL><INDENT>self.__merge_by_average_link();<EOL><DEDENT>elif (self.__similarity == type_link.CENTROID_LINK):<EOL><INDENT>self.__merge_by_centroid_link();<EOL><DEDENT>elif (self.__similarity == type_link.COMPLETE_LINK):<EOL><INDENT>self.__merge_by_complete_link();<EOL><DEDENT>elif (self.__similarity == type_link.SINGLE_LINK):<EOL><INDENT>self.__merge_by_signle_link();<EOL><DEDENT>else:<EOL><INDENT>raise NameError('<STR_LIT>');<EOL><DEDENT>

!
        @brief Merges the most similar clusters in line with link type.


minimum_average_distance = float('<STR_LIT>');<EOL>for index_cluster1 in range(<NUM_LIT:0>, len(self.__clusters)):<EOL><INDENT>for index_cluster2 in range(index_cluster1 + <NUM_LIT:1>, len(self.__clusters)):<EOL><INDENT>candidate_average_distance = <NUM_LIT:0.0>;<EOL>for index_object1 in self.__clusters[index_cluster1]:<EOL><INDENT>for index_object2 in self.__clusters[index_cluster2]:<EOL><INDENT>candidate_average_distance += euclidean_distance_square(self.__pointer_data[index_object1], self.__pointer_data[index_object2]);<EOL><DEDENT><DEDENT>candidate_average_distance /= (len(self.__clusters[index_cluster1]) + len(self.__clusters[index_cluster2]));<EOL>if (candidate_average_distance < minimum_average_distance):<EOL><INDENT>minimum_average_distance = candidate_average_distance;<EOL>indexes = [index_cluster1, index_cluster2];<EOL><DEDENT><DEDENT><DEDENT>self.__clusters[indexes[<NUM_LIT:0>]] += self.__clusters[indexes[<NUM_LIT:1>]];  <EOL>self.__clusters.pop(indexes[<NUM_LIT:1>])<EOL>

!
        @brief Merges the most similar clusters in line with average link type.


minimum_centroid_distance = float('<STR_LIT>');<EOL>indexes = None;<EOL>for index1 in range(<NUM_LIT:0>, len(self.__centers)):<EOL><INDENT>for index2 in range(index1 + <NUM_LIT:1>, len(self.__centers)):<EOL><INDENT>distance = euclidean_distance_square(self.__centers[index1], self.__centers[index2]);<EOL>if (distance < minimum_centroid_distance):<EOL><INDENT>minimum_centroid_distance = distance;<EOL>indexes = [index1, index2];<EOL><DEDENT><DEDENT><DEDENT>self.__clusters[indexes[<NUM_LIT:0>]] += self.__clusters[indexes[<NUM_LIT:1>]];<EOL>self.__centers[indexes[<NUM_LIT:0>]] = self.__calculate_center(self.__clusters[indexes[<NUM_LIT:0>]]);<EOL>self.__clusters.pop(indexes[<NUM_LIT:1>]);   <EOL>self.__centers.pop(indexes[<NUM_LIT:1>])<EOL>

!
        @brief Merges the most similar clusters in line with centroid link type.


minimum_complete_distance = float('<STR_LIT>');<EOL>indexes = None;<EOL>for index_cluster1 in range(<NUM_LIT:0>, len(self.__clusters)):<EOL><INDENT>for index_cluster2 in range(index_cluster1 + <NUM_LIT:1>, len(self.__clusters)):<EOL><INDENT>candidate_maximum_distance = self.__calculate_farthest_distance(index_cluster1, index_cluster2);<EOL>if (candidate_maximum_distance < minimum_complete_distance):<EOL><INDENT>minimum_complete_distance = candidate_maximum_distance;<EOL>indexes = [index_cluster1, index_cluster2];<EOL><DEDENT><DEDENT><DEDENT>self.__clusters[indexes[<NUM_LIT:0>]] += self.__clusters[indexes[<NUM_LIT:1>]];  <EOL>self.__clusters.pop(indexes[<NUM_LIT:1>])<EOL>

!
        @brief Merges the most similar clusters in line with complete link type.


def __calculate_farthest_distance(self, index_cluster1, index_cluster2):

candidate_maximum_distance = <NUM_LIT:0.0>;<EOL>for index_object1 in self.__clusters[index_cluster1]:<EOL><INDENT>for index_object2 in self.__clusters[index_cluster2]:<EOL><INDENT>distance = euclidean_distance_square(self.__pointer_data[index_object1], self.__pointer_data[index_object2]);<EOL>if (distance > candidate_maximum_distance):<EOL><INDENT>candidate_maximum_distance = distance;<EOL><DEDENT><DEDENT><DEDENT>return candidate_maximum_distance<EOL>

!
        @brief Finds two farthest objects in two specified clusters in terms and returns distance between them.

        @param[in] (uint) Index of the first cluster.
        @param[in] (uint) Index of the second cluster.

        @return The farthest euclidean distance between two clusters.


minimum_single_distance = float('<STR_LIT>');<EOL>indexes = None;<EOL>for index_cluster1 in range(<NUM_LIT:0>, len(self.__clusters)):<EOL><INDENT>for index_cluster2 in range(index_cluster1 + <NUM_LIT:1>, len(self.__clusters)):<EOL><INDENT>candidate_minimum_distance = self.__calculate_nearest_distance(index_cluster1, index_cluster2);<EOL>if (candidate_minimum_distance < minimum_single_distance):<EOL><INDENT>minimum_single_distance = candidate_minimum_distance;<EOL>indexes = [index_cluster1, index_cluster2];<EOL><DEDENT><DEDENT><DEDENT>self.__clusters[indexes[<NUM_LIT:0>]] += self.__clusters[indexes[<NUM_LIT:1>]];  <EOL>self.__clusters.pop(indexes[<NUM_LIT:1>])<EOL>

!
        @brief Merges the most similar clusters in line with single link type.


def __calculate_nearest_distance(self, index_cluster1, index_cluster2):

candidate_minimum_distance = float('<STR_LIT>');<EOL>for index_object1 in self.__clusters[index_cluster1]:<EOL><INDENT>for index_object2 in self.__clusters[index_cluster2]:<EOL><INDENT>distance = euclidean_distance_square(self.__pointer_data[index_object1], self.__pointer_data[index_object2]);<EOL>if (distance < candidate_minimum_distance):<EOL><INDENT>candidate_minimum_distance = distance;<EOL><DEDENT><DEDENT><DEDENT>return candidate_minimum_distance<EOL>

!
        @brief Finds two nearest objects in two specified clusters and returns distance between them.

        @param[in] (uint) Index of the first cluster.
        @param[in] (uint) Index of the second cluster.

        @return The nearest euclidean distance between two clusters.


dimension = len(self.__pointer_data[cluster[<NUM_LIT:0>]]);<EOL>center = [<NUM_LIT:0>] * dimension;<EOL>for index_point in cluster:<EOL><INDENT>for index_dimension in range(<NUM_LIT:0>, dimension):<EOL><INDENT>center[index_dimension] += self.__pointer_data[index_point][index_dimension];<EOL><DEDENT><DEDENT>for index_dimension in range(<NUM_LIT:0>, dimension):<EOL><INDENT>center[index_dimension] /= len(cluster);<EOL><DEDENT>return center<EOL>

!
        @brief Calculates new center.

        @return (list) New value of the center of the specified cluster.


def __init__(self, data, initial_centers, tolerance=<NUM_LIT>, ccore=True, **kwargs):

self.__pointer_data = data<EOL>self.__clusters = []<EOL>self.__medians = initial_centers[:]<EOL>self.__tolerance = tolerance<EOL>self.__itermax = kwargs.get('<STR_LIT>', <NUM_LIT:100>)<EOL>self.__metric = kwargs.get('<STR_LIT>', distance_metric(type_metric.EUCLIDEAN_SQUARE))<EOL>if self.__metric is None:<EOL><INDENT>self.__metric = distance_metric(type_metric.EUCLIDEAN_SQUARE)<EOL><DEDENT>self.__ccore = ccore and self.__metric.get_type() != type_metric.USER_DEFINED<EOL>if self.__ccore:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Constructor of clustering algorithm K-Medians.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] initial_centers (list): Initial coordinates of medians of clusters that are represented by list: [center1, center2, ...].
        @param[in] tolerance (double): Stop condition: if maximum value of change of centers of clusters is less than tolerance than algorithm will stop processing
        @param[in] ccore (bool): Defines should be CCORE library (C++ pyclustering library) used instead of Python code or not.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric', 'itermax').

        Keyword Args:

            - metric (distance_metric): Metric that is used for distance calculation between two points.
            - itermax (uint): Maximum number of iterations for cluster analysis.


if self.__ccore is True:<EOL><INDENT>ccore_metric = metric_wrapper.create_instance(self.__metric)<EOL>self.__clusters, self.__medians = wrapper.kmedians(self.__pointer_data, self.__medians, self.__tolerance, self.__itermax, ccore_metric.get_pointer())<EOL><DEDENT>else:<EOL><INDENT>changes = float('<STR_LIT>')<EOL>if len(self.__pointer_data[<NUM_LIT:0>]) != len(self.__medians[<NUM_LIT:0>]):<EOL><INDENT>raise NameError('<STR_LIT>')<EOL><DEDENT>iterations = <NUM_LIT:0><EOL>while changes > self.__tolerance and iterations < self.__itermax:<EOL><INDENT>self.__clusters = self.__update_clusters()<EOL>updated_centers = self.__update_medians()<EOL>changes = max([self.__metric(self.__medians[index], updated_centers[index]) for index in range(len(updated_centers))])<EOL>self.__medians = updated_centers<EOL>iterations += <NUM_LIT:1><EOL><DEDENT><DEDENT>return self<EOL>

!
        @brief Performs cluster analysis in line with rules of K-Medians algorithm.

        @return (kmedians) Returns itself (K-Medians instance).

        @remark Results of clustering can be obtained using corresponding get methods.

        @see get_clusters()
        @see get_medians()


return self.__clusters<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()
        @see get_medians()


return self.__medians<EOL>

!
        @brief Returns list of centers of allocated clusters.

        @see process()
        @see get_clusters()


clusters = [[] for i in range(len(self.__medians))]<EOL>for index_point in range(len(self.__pointer_data)):<EOL><INDENT>index_optim = -<NUM_LIT:1><EOL>dist_optim = <NUM_LIT:0.0><EOL>for index in range(len(self.__medians)):<EOL><INDENT>dist = self.__metric(self.__pointer_data[index_point], self.__medians[index])<EOL>if (dist < dist_optim) or (index == <NUM_LIT:0>):<EOL><INDENT>index_optim = index<EOL>dist_optim = dist<EOL><DEDENT><DEDENT>clusters[index_optim].append(index_point)<EOL><DEDENT>clusters = [cluster for cluster in clusters if len(cluster) > <NUM_LIT:0>]<EOL>return clusters<EOL>

!
        @brief Calculate Manhattan distance to each point from the each cluster.
        @details Nearest points are captured by according clusters and as a result clusters are updated.

        @return (list) updated clusters as list of clusters where each cluster contains indexes of objects from data.


medians = [[] for i in range(len(self.__clusters))]<EOL>for index in range(len(self.__clusters)):<EOL><INDENT>medians[index] = [<NUM_LIT:0.0> for i in range(len(self.__pointer_data[<NUM_LIT:0>]))]<EOL>length_cluster = len(self.__clusters[index])<EOL>for index_dimension in range(len(self.__pointer_data[<NUM_LIT:0>])):<EOL><INDENT>sorted_cluster = sorted(self.__clusters[index], key=lambda x: self.__pointer_data[x][index_dimension])<EOL>relative_index_median = int(math.floor((length_cluster - <NUM_LIT:1>) / <NUM_LIT:2>))<EOL>index_median = sorted_cluster[relative_index_median]<EOL>if (length_cluster % <NUM_LIT:2>) == <NUM_LIT:0>:<EOL><INDENT>index_median_second = sorted_cluster[relative_index_median + <NUM_LIT:1>]<EOL>medians[index][index_dimension] = (self.__pointer_data[index_median][index_dimension] + self.__pointer_data[index_median_second][index_dimension]) / <NUM_LIT><EOL><DEDENT>else:<EOL><INDENT>medians[index][index_dimension] = self.__pointer_data[index_median][index_dimension]<EOL><DEDENT><DEDENT><DEDENT>return medians<EOL>

!
        @brief Calculate medians of clusters in line with contained objects.

        @return (list) list of medians for current number of clusters.


def __init__(self, data, threshold1, threshold2, ccore=True, **kwargs):

self._threshold2 = threshold2;<EOL>self._amount_skipped_objects = len(data);<EOL>self._skipped_objects = [ True ] * len(data);<EOL>super().__init__(data, len(data), threshold1, ccore, **kwargs)<EOL>

!
        @brief Creates TTSAS algorithm.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] threshold1: Dissimilarity level (distance) between point and its closest cluster, if the distance is
                    less than 'threshold1' value then point is assigned to the cluster.
        @param[in] threshold2: Dissimilarity level (distance) between point and its closest cluster, if the distance is
                    greater than 'threshold2' value then point is considered as a new cluster.
        @param[in] ccore (bool): If True than DLL CCORE (C++ solution) will be used for solving.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric').

        Keyword Args:

            - metric (distance_metric): Metric that is used for distance calculation between two points.


if self._ccore is True:<EOL><INDENT>self.__process_by_ccore();<EOL><DEDENT>else:<EOL><INDENT>self.__prcess_by_python();<EOL><DEDENT>

!
        @brief Performs cluster analysis in line with rules of BSAS algorithm.

        @remark Results of clustering can be obtained using corresponding get methods.

        @see get_clusters()
        @see get_representatives()


dimension = len(directory.get_data()[<NUM_LIT:0>])<EOL>amount_canvases = <NUM_LIT:1><EOL>if dimension > <NUM_LIT:1>:<EOL><INDENT>amount_canvases = int(dimension * (dimension - <NUM_LIT:1>) / <NUM_LIT:2>)<EOL><DEDENT>figure = plt.figure()<EOL>grid_spec = gridspec.GridSpec(<NUM_LIT:1>, amount_canvases)<EOL>pairs = list(itertools.combinations(range(dimension), <NUM_LIT:2>))<EOL>if len(pairs) == <NUM_LIT:0>: pairs = [(<NUM_LIT:0>, <NUM_LIT:0>)]<EOL>for index in range(amount_canvases):<EOL><INDENT>ax = figure.add_subplot(grid_spec[index])<EOL>bang_visualizer.__draw_blocks(ax, directory.get_leafs(), pairs[index])<EOL>bang_visualizer.__draw_two_dimension_data(ax, directory.get_data(), pairs[index])<EOL><DEDENT>plt.show()<EOL>

!
        @brief Show BANG-blocks (leafs only) in data space.
        @details BANG-blocks represents grid that was used for clustering process.

        @param[in] directory (bang_directory): Directory that was created by BANG algorithm during clustering process.


plt.figure()<EOL>axis = plt.subplot(<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>)<EOL>current_position = <NUM_LIT:0><EOL>for index_cluster in range(len(dendrogram)):<EOL><INDENT>densities = [ block.get_density() for block in dendrogram[index_cluster] ]<EOL>xrange = range(current_position, current_position + len(densities))<EOL>axis.bar(xrange, densities, <NUM_LIT:1.0>, linewidth=<NUM_LIT:0.0>, color=color_list.get_color(index_cluster))<EOL>current_position += len(densities)<EOL><DEDENT>axis.set_ylabel("<STR_LIT>")<EOL>axis.set_xlabel("<STR_LIT>")<EOL>axis.xaxis.set_ticklabels([])<EOL>plt.xlim([-<NUM_LIT:0.5>, current_position - <NUM_LIT:0.5>])<EOL>plt.show()<EOL>

!
        @brief Display dendrogram of BANG-blocks.

        @param[in] dendrogram (list): List representation of dendrogram of BANG-blocks.

        @see bang.get_dendrogram()


@staticmethod<EOL><INDENT>def show_clusters(data, clusters, noise=None):<DEDENT>

visualizer = cluster_visualizer()<EOL>visualizer.append_clusters(clusters, data)<EOL>visualizer.append_cluster(noise or [], data, marker='<STR_LIT:x>')<EOL>visualizer.show()<EOL>

!
        @brief Display BANG clustering results.

        @param[in] data (list): Dataset that was used for clustering.
        @param[in] clusters (array_like): Clusters that were allocated by the algorithm.
        @param[in] noise (array_like): Noise that were allocated by the algorithm.


@staticmethod<EOL><INDENT>def __draw_two_dimension_data(ax, data, pair):<DEDENT>

ax.set_xlabel("<STR_LIT>" % pair[<NUM_LIT:0>])<EOL>ax.set_ylabel("<STR_LIT>" % pair[<NUM_LIT:1>])<EOL>for point in data:<EOL><INDENT>if len(data[<NUM_LIT:0>]) > <NUM_LIT:1>:<EOL><INDENT>ax.plot(point[pair[<NUM_LIT:0>]], point[pair[<NUM_LIT:1>]], color='<STR_LIT>', marker='<STR_LIT:.>')<EOL><DEDENT>else:<EOL><INDENT>ax.plot(point[pair[<NUM_LIT:0>]], <NUM_LIT:0>, color='<STR_LIT>', marker='<STR_LIT:.>')<EOL>ax.yaxis.set_ticklabels([])<EOL><DEDENT><DEDENT>

!
        @brief Display data in two-dimensional canvas.

        @param[in] ax (Axis): Canvas where data should be displayed.
        @param[in] data (list): Data points that should be displayed.
        @param[in] pair (tuple): Pair of dimension indexes.


@staticmethod<EOL><INDENT>def __draw_blocks(ax, blocks, pair):<DEDENT>

ax.grid(False)<EOL>density_scale = blocks[-<NUM_LIT:1>].get_density()<EOL>for block in blocks:<EOL><INDENT>bang_visualizer.__draw_block(ax, pair, block, density_scale)<EOL><DEDENT>

!
        @brief Display BANG-blocks on specified figure.

        @param[in] ax (Axis): Axis where bang-blocks should be displayed.
        @param[in] blocks (list): List of blocks that should be displyed.
        @param[in] pair (tuple): Pair of coordinate index that should be displayed.


@staticmethod<EOL><INDENT>def __draw_block(ax, pair, block, density_scale):<DEDENT>

max_corner, min_corner = bang_visualizer.__get_rectangle_description(block, pair)<EOL>belong_cluster = block.get_cluster() is not None<EOL>if density_scale != <NUM_LIT:0.0>:<EOL><INDENT>density_scale = bang_visualizer.__maximum_density_alpha * block.get_density() / density_scale<EOL><DEDENT>face_color = matplotlib.colors.to_rgba('<STR_LIT>', alpha=density_scale)<EOL>edge_color = matplotlib.colors.to_rgba('<STR_LIT>', alpha=<NUM_LIT:1.0>)<EOL>rect = patches.Rectangle(min_corner, max_corner[<NUM_LIT:0>] - min_corner[<NUM_LIT:0>], max_corner[<NUM_LIT:1>] - min_corner[<NUM_LIT:1>],<EOL>fill=belong_cluster,<EOL>facecolor=face_color,<EOL>edgecolor=edge_color,<EOL>linewidth=<NUM_LIT:0.5>)<EOL>ax.add_patch(rect)<EOL>

!
        @brief Display BANG-block on the specified ax.

        @param[in] ax (Axis): Axis where block should be displayed.
        @param[in] pair (tuple): Pair of coordinate index that should be displayed.
        @param[in] block (bang_block): BANG-block that should be displayed.
        @param[in] density_scale (double): Max density to display density of the block by appropriate tone.


max_corner, min_corner = block.get_spatial_block().get_corners()<EOL>max_corner = [max_corner[pair[<NUM_LIT:0>]], max_corner[pair[<NUM_LIT:1>]]]<EOL>min_corner = [min_corner[pair[<NUM_LIT:0>]], min_corner[pair[<NUM_LIT:1>]]]<EOL>if pair == (<NUM_LIT:0>, <NUM_LIT:0>):<EOL><INDENT>max_corner[<NUM_LIT:1>], min_corner[<NUM_LIT:1>] = <NUM_LIT:1.0>, -<NUM_LIT:1.0><EOL><DEDENT>return max_corner, min_corner<EOL>

!
        @brief Create rectangle description for block in specific dimension.

        @param[in] pair (tuple): Pair of coordinate index that should be displayed.
        @param[in] block (bang_block): BANG-block that should be displayed

        @return (tuple) Pair of corners that describes rectangle.


def __init__(self, directory, clusters):

self.__directory = directory<EOL>self.__clusters = clusters<EOL>self.__noise = []<EOL>self.__current_block = <NUM_LIT:0><EOL>self.__current_level = <NUM_LIT:0><EOL>self.__level_blocks = directory.get_level(<NUM_LIT:0>)<EOL>self.__figure = plt.figure()<EOL>self.__ax = self.__figure.add_subplot(<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>)<EOL>self.__special_frame = <NUM_LIT:0><EOL>self.__validate_arguments()<EOL>

!
        @brief Creates BANG animator instance.

        @param[in] directory (bang_directory): BANG directory that was formed during BANG clustering process.
        @param[in] clusters (list): Allocated clusters during BANG clustering process.


if len(self.__directory.get_data()[<NUM_LIT:0>]) != <NUM_LIT:2>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>

!
        @brief Check correctness of input arguments and throw exception if incorrect is found.


self.__current_block += <NUM_LIT:1><EOL>if self.__current_block >= len(self.__level_blocks):<EOL><INDENT>self.__current_block = <NUM_LIT:0><EOL>self.__current_level += <NUM_LIT:1><EOL>if self.__current_level < self.__directory.get_height():<EOL><INDENT>self.__level_blocks = self.__directory.get_level(self.__current_level)<EOL><DEDENT><DEDENT>

!
        @brief Increment BANG block safely by updating block index, level and level block.


def __draw_block(self, block, block_alpha=<NUM_LIT:0.0>):

max_corner, min_corner = block.get_spatial_block().get_corners()<EOL>face_color = matplotlib.colors.to_rgba('<STR_LIT>', alpha=block_alpha)<EOL>edge_color = matplotlib.colors.to_rgba('<STR_LIT>', alpha=<NUM_LIT:1.0>)<EOL>rect = patches.Rectangle(min_corner, max_corner[<NUM_LIT:0>] - min_corner[<NUM_LIT:0>], max_corner[<NUM_LIT:1>] - min_corner[<NUM_LIT:1>],<EOL>fill=True,<EOL>facecolor=face_color,<EOL>edgecolor=edge_color,<EOL>linewidth=<NUM_LIT:0.5>)<EOL>self.__ax.add_patch(rect)<EOL>

!
        @brief Display single BANG block on axis.

        @param[in] block (bang_block): BANG block that should be displayed.
        @param[in] block_alpha (double): Transparency level - value of alpha.


leafs = self.__directory.get_leafs()<EOL>density_scale = leafs[-<NUM_LIT:1>].get_density()<EOL>if density_scale == <NUM_LIT:0.0>: density_scale = <NUM_LIT:1.0><EOL>for block in leafs:<EOL><INDENT>alpha = <NUM_LIT> * block.get_density() / density_scale<EOL>self.__draw_block(block, alpha)<EOL><DEDENT>

!
        @brief Display densities by filling blocks by appropriate colors.


data = self.__directory.get_data()<EOL>for index_cluster in range(len(self.__clusters)):<EOL><INDENT>color = color_list.get_color(index_cluster)<EOL>self.__draw_cluster(data, self.__clusters[index_cluster], color, '<STR_LIT:.>')<EOL><DEDENT>self.__draw_cluster(self.__directory.get_data(), self.__noise, '<STR_LIT>', '<STR_LIT:x>')<EOL>

!
        @brief Display clusters and outliers using different colors.


def __draw_cluster(self, data, cluster, color, marker):

for item in cluster:<EOL><INDENT>self.__ax.plot(data[item][<NUM_LIT:0>], data[item][<NUM_LIT:1>], color=color, marker=marker)<EOL><DEDENT>

!
        @brief Draw 2-D single cluster on axis using specified color and marker.


def animate(self, animation_velocity=<NUM_LIT>, movie_fps=<NUM_LIT>, movie_filename=None):

def init_frame():<EOL><INDENT>self.__figure.clf()<EOL>self.__ax = self.__figure.add_subplot(<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>)<EOL>self.__figure.suptitle("<STR_LIT>", fontsize=<NUM_LIT>, fontweight='<STR_LIT>')<EOL>for point in self.__directory.get_data():<EOL><INDENT>self.__ax.plot(point[<NUM_LIT:0>], point[<NUM_LIT:1>], color='<STR_LIT>', marker='<STR_LIT:.>')<EOL><DEDENT>return frame_generation(<NUM_LIT:0>)<EOL><DEDENT>def frame_generation(index_iteration):<EOL><INDENT>if self.__current_level < self.__directory.get_height():<EOL><INDENT>block = self.__level_blocks[self.__current_block]<EOL>self.__draw_block(block)<EOL>self.__increment_block()<EOL><DEDENT>else:<EOL><INDENT>if self.__special_frame == <NUM_LIT:0>:<EOL><INDENT>self.__draw_leaf_density()<EOL><DEDENT>elif self.__special_frame == <NUM_LIT:15>:<EOL><INDENT>self.__draw_clusters()<EOL><DEDENT>elif self.__special_frame == <NUM_LIT:30>:<EOL><INDENT>self.__figure.clf()<EOL>self.__ax = self.__figure.add_subplot(<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>)<EOL>self.__figure.suptitle("<STR_LIT>", fontsize=<NUM_LIT>, fontweight='<STR_LIT>')<EOL>self.__draw_clusters()<EOL><DEDENT>self.__special_frame += <NUM_LIT:1><EOL><DEDENT><DEDENT>iterations = len(self.__directory) + <NUM_LIT><EOL>cluster_animation = animation.FuncAnimation(self.__figure, frame_generation, iterations,<EOL>interval=animation_velocity,<EOL>init_func=init_frame,<EOL>repeat_delay=<NUM_LIT>)<EOL>if movie_filename is not None:<EOL><INDENT>cluster_animation.save(movie_filename, writer = '<STR_LIT>', fps = movie_fps, bitrate = <NUM_LIT>)<EOL><DEDENT>else:<EOL><INDENT>plt.show()<EOL><DEDENT>

!
        @brief Animates clustering process that is performed by BANG algorithm.

        @param[in] animation_velocity (uint): Interval between frames in milliseconds (for run-time animation only).
        @param[in] movie_fps (uint): Defines frames per second (for rendering movie only).
        @param[in] movie_filename (string): If it is specified then animation will be stored to file that is specified in this parameter.


def __init__(self, data, levels, **kwargs):

self.__data = data<EOL>self.__levels = levels<EOL>self.__density_threshold = kwargs.get('<STR_LIT>', <NUM_LIT:0.0>)<EOL>self.__amount_density = kwargs.get('<STR_LIT>', <NUM_LIT:0>)<EOL>self.__leafs = []<EOL>self.__root = None<EOL>self.__level_blocks = []<EOL>self.__size = <NUM_LIT:0><EOL>self.__observe = kwargs.get('<STR_LIT>', True)<EOL>self.__create_directory()<EOL>

!
        @brief Create BANG directory - basically tree structure with direct access to leafs.

        @param[in] data (list): Input data that is clustered.
        @param[in] levels (uint): Height of the blocks tree.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'observe').

        Keyword Args:

            - observe (bool): If 'True' then blocks on each level are stored.
            - density_threshold (double): The lowest level of density when contained data in bang-block is
               considered as a noise and there is no need to split it till the last level. Be aware that this
               parameter is used with 'amount_threshold' parameter.
            - amount_threshold (uint): Amount of points in the block when it contained data in bang-block is
               considered as a noise and there is no need to split it till the last level.


return self.__size<EOL>

!
        @brief Returns amount of blocks that is stored in the directory

        @return (uint) Amount of blocks in the BANG directory.


return self.__data<EOL>

!
        @brief Return data that is stored in the directory.

        @return (list) List of points that represents stored data.


return self.__leafs<EOL>

!
        @brief Return leafs - the smallest blocks.
        @details Some leafs can be bigger than others because splitting is not performed for blocks whose density is
                  less than threshold.

        @return (list) List of blocks that are leafs of BANG directory.


return self.__level_blocks[level]<EOL>

!
        @brief Returns BANG blocks on the specific level.

        @param[in] level (uint): Level of tree where BANG blocks are located.

        @return (list) List of BANG blocks on the specific level.


return len(self.__level_blocks)<EOL>

!
        @brief Returns height of BANG tree where blocks are stored.

        @return (uint) Height of BANG tree.


min_corner, max_corner = data_corners(self.__data)<EOL>data_block = spatial_block(max_corner, min_corner)<EOL>cache_require = (self.__levels == <NUM_LIT:1>)<EOL>self.__root = bang_block(self.__data, <NUM_LIT:0>, <NUM_LIT:0>, data_block, cache_require)<EOL>if cache_require:<EOL><INDENT>self.__leafs.append(self.__root)<EOL>self.__store_level_blocks([self.__root])<EOL><DEDENT>else:<EOL><INDENT>self.__build_directory_levels()<EOL><DEDENT>

!
        @brief Create BANG directory as a tree with separate storage for leafs.


self.__size += len(level_blocks)<EOL>if self.__observe is True:<EOL><INDENT>self.__level_blocks.append(level_blocks)<EOL><DEDENT>

!
        @brief Store level blocks if observing is enabled.

        @param[in] level_blocks (list): Created blocks on a new level.


previous_level_blocks = [ self.__root ]<EOL>for level in range(<NUM_LIT:1>, self.__levels):<EOL><INDENT>previous_level_blocks = self.__build_level(previous_level_blocks, level)<EOL>self.__store_level_blocks(previous_level_blocks)<EOL><DEDENT>self.__leafs = sorted(self.__leafs, key=lambda block: block.get_density())<EOL>

!
        @brief Build levels of direction if amount of level is greater than one.


def __build_level(self, previous_level_blocks, level):

current_level_blocks = []<EOL>split_dimension = level % len(self.__data[<NUM_LIT:0>])<EOL>cache_require = (level == self.__levels - <NUM_LIT:1>)<EOL>for block in previous_level_blocks:<EOL><INDENT>self.__split_block(block, split_dimension, cache_require, current_level_blocks)<EOL><DEDENT>if cache_require:<EOL><INDENT>self.__leafs += current_level_blocks<EOL><DEDENT>return current_level_blocks<EOL>

!
        @brief Build new level of directory.

        @param[in] previous_level_blocks (list): BANG-blocks on the previous level.
        @param[in] level (uint): Level number that should be built.

        @return (list) New block on the specified level.


def __split_block(self, block, split_dimension, cache_require, current_level_blocks):

if block.get_density() <= self.__density_threshold or len(block) <= self.__amount_density:<EOL><INDENT>self.__leafs.append(block)<EOL><DEDENT>else:<EOL><INDENT>left, right = block.split(split_dimension, cache_require)<EOL>current_level_blocks.append(left)<EOL>current_level_blocks.append(right)<EOL><DEDENT>

!
        @brief Split specific block in specified dimension.
        @details Split is not performed for block whose density is lower than threshold value, such blocks are putted to
                  leafs.

        @param[in] block (bang_block): BANG-block that should be split.
        @param[in] split_dimension (uint): Dimension at which splitting should be performed.
        @param[in] cache_require (bool): Defines when points in cache should be stored during density calculation.
        @param[in|out] current_level_blocks (list): Block storage at the current level where new blocks should be added.


def __init__(self, max_corner, min_corner):

self.__max_corner = max_corner<EOL>self.__min_corner = min_corner<EOL>self.__volume = self.__calculate_volume()<EOL>

!
        @brief Creates spatial block in data space.

        @param[in] max_corner (array_like): Maximum corner coordinates of the block.
        @param[in] min_corner (array_like): Minimal corner coordinates of the block.


return "<STR_LIT>" % (self.__max_corner, self.__min_corner)<EOL>

!
        @brief Returns string block description.

        @return String representation of the block.


for i in range(len(point)):<EOL><INDENT>if point[i] < self.__min_corner[i] or point[i] > self.__max_corner[i]:<EOL><INDENT>return False<EOL><DEDENT><DEDENT>return True<EOL>

!
        @brief Point is considered as contained if it lies in block (belong to it).

        @return (bool) True if point is in block, otherwise False.


return self.__max_corner, self.__min_corner<EOL>

!
        @brief Return spatial description of current block.

        @return (tuple) Pair of maximum and minimum corners (max_corner, min_corner).


return self.__volume<EOL>

!
        @brief Returns volume of current block.
        @details Volume block has uncommon mining here: for 1D is length of a line, for 2D is square of rectangle,
                  for 3D is volume of 3D figure, and for ND is volume of ND figure.

        @return (double) Volume of current block.


first_max_corner = self.__max_corner[:]<EOL>second_min_corner = self.__min_corner[:]<EOL>split_border = (self.__max_corner[dimension] + self.__min_corner[dimension]) / <NUM_LIT><EOL>first_max_corner[dimension] = split_border<EOL>second_min_corner[dimension] = split_border<EOL>return spatial_block(first_max_corner, self.__min_corner), spatial_block(self.__max_corner, second_min_corner)<EOL>

!
        @brief Split current block into two spatial blocks in specified dimension.

        @param[in] dimension (uint): Dimension where current block should be split.

        @return (tuple) Pair of new split blocks from current block.


if block is not self:<EOL><INDENT>block_max_corner, _ = block.get_corners()<EOL>dimension = len(block_max_corner)<EOL>neighborhood_score = self.__calculate_neighborhood(block_max_corner)<EOL>if neighborhood_score == dimension:<EOL><INDENT>return True<EOL><DEDENT><DEDENT>return False<EOL>

!
        @brief Performs calculation to identify whether specified block is neighbor of current block.
        @details It also considers diagonal blocks as neighbors.

        @param[in] block (spatial_block): Another block that is check whether it is neighbor.

        @return (bool) True is blocks are neighbors, False otherwise.


dimension = len(block_max_corner)<EOL>length_edges = [self.__max_corner[i] - self.__min_corner[i] for i in range(dimension)]<EOL>neighborhood_score = <NUM_LIT:0><EOL>for i in range(dimension):<EOL><INDENT>diff = abs(block_max_corner[i] - self.__max_corner[i])<EOL>if diff <= length_edges[i] + length_edges[i] * <NUM_LIT>:<EOL><INDENT>neighborhood_score += <NUM_LIT:1><EOL><DEDENT><DEDENT>return neighborhood_score<EOL>

!
        @brief Calculates neighborhood score that defined whether blocks are neighbors.

        @param[in] block_max_corner (list): Maximum coordinates of other block.

        @return (uint) Neighborhood score.


volume = <NUM_LIT:0.0><EOL>for i in range(<NUM_LIT:0>, len(self.__max_corner)):<EOL><INDENT>side_length = self.__max_corner[i] - self.__min_corner[i]<EOL>if side_length != <NUM_LIT:0.0>:<EOL><INDENT>if volume == <NUM_LIT:0.0>: volume = side_length<EOL>else: volume *= side_length<EOL><DEDENT><DEDENT>return volume<EOL>

!
        @brief Calculates volume of current spatial block.
        @details If empty dimension is detected (where all points has the same value) then such dimension is ignored
                  during calculation of volume.

        @return (double) Volume of current spatial block.


def __init__(self, data, region, level, space_block, cache_points=False):

self.__data = data<EOL>self.__region_number = region<EOL>self.__level = level<EOL>self.__spatial_block = space_block<EOL>self.__cache_points = cache_points<EOL>self.__cluster = None<EOL>self.__points = None<EOL>self.__amount_points = self.__get_amount_points()<EOL>self.__density = self.__calculate_density(self.__amount_points)<EOL>

!
        @brief Create BANG-block.

        @param[in] data (list): List of points that are processed.
        @param[in] region (uint): Region number - unique value on a level.
        @param[in] level (uint): Level number where block is created.
        @param[in] space_block (spatial_block): Spatial block description in data space.
        @param[in] cache_points (bool): if True then points are stored in memory (used for leaf blocks).


return "<STR_LIT:(>" + str(self.__region_number) + "<STR_LIT:U+002CU+0020>" + str(self.__level) + "<STR_LIT:)>"<EOL>

!
        @brief Returns string representation of BANG-block using region number and level where block is located.


return self.__amount_points<EOL>

!
        @brief Returns block size defined by amount of points that are contained by this block.


return self.__region_number<EOL>

!
        @brief Returns region number of BANG-block.
        @details Region number is unique on among region numbers on a directory level. Pair of region number and level
                  is unique for all directory.

        @return (uint) Region number.


return self.__density<EOL>

!
        @brief Returns density of the BANG-block.

        @return (double) BANG-block density.


return self.__cluster<EOL>

!
        @brief Return index of cluster to which the BANG-block belongs to.
        @details Index of cluster may have None value if the block was not assigned to any cluster.

        @return (uint) Index of cluster or None if the block does not belong to any cluster.


return self.__spatial_block<EOL>

!
        @brief Return spatial block - BANG-block description in data space.

        @return (spatial_block) Spatial block of the BANG-block.


if self.__points is None:<EOL><INDENT>self.__cache_covered_data()<EOL><DEDENT>return self.__points<EOL>

!
        @brief Return points that covers by the BANG-block.

        @return (list) List of point indexes that are covered by the block.


self.__cluster = index<EOL>

!
        @brief Assign cluster to the BANG-block by index.

        @param[in] index (uint): Index cluster that is assigned to BANG-block.


return self.get_spatial_block().is_neighbor(block.get_spatial_block())<EOL>

!
        @brief Performs calculation to check whether specified block is neighbor to the current.

        @param[in] block (bang_block): Other BANG-block that should be checked for neighborhood.

        @return (bool) True if blocks are neighbors, False if blocks are not neighbors.


def split(self, split_dimension, cache_points):

left_region_number = self.__region_number<EOL>right_region_number = self.__region_number + <NUM_LIT:2> ** self.__level<EOL>first_spatial_block, second_spatial_block = self.__spatial_block.split(split_dimension)<EOL>left = bang_block(self.__data, left_region_number, self.__level + <NUM_LIT:1>, first_spatial_block, cache_points)<EOL>right = bang_block(self.__data, right_region_number, self.__level + <NUM_LIT:1>, second_spatial_block, cache_points)<EOL>return left, right<EOL>

!
        @brief Split BANG-block into two new blocks in specified dimension.

        @param[in] split_dimension (uint): Dimension where block should be split.
        @param[in] cache_points (bool): If True then covered points are cached. Used for leaf blocks.

        @return (tuple) Pair of BANG-block that were formed from the current.


volume = self.__spatial_block.get_volume()<EOL>if volume != <NUM_LIT:0.0>:<EOL><INDENT>return amount_points / volume<EOL><DEDENT>return <NUM_LIT:0.0><EOL>

!
        @brief Calculates BANG-block density.

        @param[in] amount_points (uint): Amount of points in block.

        @return (double) BANG-block density.


amount = <NUM_LIT:0><EOL>for index in range(len(self.__data)):<EOL><INDENT>if self.__data[index] in self.__spatial_block:<EOL><INDENT>self.__cache_point(index)<EOL>amount += <NUM_LIT:1><EOL><DEDENT><DEDENT>return amount<EOL>

!
        @brief Count covered points by the BANG-block and if cache is enable then covered points are stored.

        @return (uint) Amount of covered points.


self.__cache_points = True<EOL>self.__points = []<EOL>for index_point in range(len(self.__data)):<EOL><INDENT>if self.__data[index_point] in self.__spatial_block:<EOL><INDENT>self.__cache_point(index_point)<EOL><DEDENT><DEDENT>


if self.__cache_points:<EOL><INDENT>if self.__points is None:<EOL><INDENT>self.__points = []<EOL><DEDENT>self.__points.append(index)<EOL><DEDENT>

!
        @brief Store index points.

        @param[in] index (uint): Index point that should be stored.


def __init__(self, data, levels, ccore=False, **kwargs):

self.__data = data<EOL>self.__levels = levels<EOL>self.__directory = None<EOL>self.__clusters = []<EOL>self.__noise = []<EOL>self.__cluster_blocks = []<EOL>self.__dendrogram = []<EOL>self.__density_threshold = kwargs.get('<STR_LIT>', <NUM_LIT:0.0>)<EOL>self.__amount_threshold = kwargs.get('<STR_LIT>', <NUM_LIT:0>)<EOL>self.__ccore = ccore<EOL>self.__validate_arguments()<EOL>

!
        @brief Create BANG clustering algorithm.

        @param[in] data (list): Input data (list of points) that should be clustered.
        @param[in] levels (uint): Amount of levels in tree that is used for splitting (how many times block should be
                    split). For example, if amount of levels is two then surface will be divided into two blocks and
                    each obtained block will be divided into blocks also.
        @param[in] ccore (bool): Reserved positional argument - not used yet.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'observe').

        Keyword Args:

            - density_threshold (double): If block density is smaller than this value then contained data by this
               block is considered as a noise and its points as outliers. Block density is defined by amount of
               points in block divided by block volume: amount_block_points/block_volume. By default
               it is 0.0 - means than only empty blocks are considered as noise. Be aware that this parameter is used
               with parameter 'amount_threshold' - the maximum threshold is considered during processing.
            - amount_threshold (uint): Amount of points in the block when it contained data in bang-block is
               considered as a noise and there is no need to split it till the last level. Be aware that this parameter
               is used with parameter 'density_threshold' - the maximum threshold is considered during processing.


self.__directory = bang_directory(self.__data, self.__levels,<EOL>density_threshold=self.__density_threshold,<EOL>amount_threshold=self.__amount_threshold)<EOL>self.__allocate_clusters()<EOL>return self<EOL>

!
        @brief Performs clustering process in line with rules of BANG clustering algorithm.

        @return (bang) Returns itself (BANG instance).

        @see get_clusters()
        @see get_noise()
        @see get_directory()
        @see get_dendrogram()


return self.__clusters<EOL>

!
        @brief Returns allocated clusters.

        @remark Allocated clusters are returned only after data processing (method process()). Otherwise empty list is returned.

        @return (list) List of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()
        @see get_noise()


return self.__noise<EOL>

!
        @brief Returns allocated noise.

        @remark Allocated noise is returned only after data processing (method process()). Otherwise empty list is returned.

        @return (list) List of indexes that are marked as a noise.

        @see process()
        @see get_clusters()


return self.__directory<EOL>

!
        @brief Returns grid directory that describes grid of the processed data.

        @remark Grid directory is returned only after data processing (method process()). Otherwise None value is returned.

        @return (bang_directory) BANG directory that describes grid of process data.

        @see process()


return self.__dendrogram<EOL>

!
        @brief Returns dendrogram of clusters.
        @details Dendrogram is created in following way: the density indices of all regions are calculated and sorted
                  in decreasing order for each cluster during clustering process.

        @remark Dendrogram is returned only after data processing (method process()). Otherwise empty list is returned.


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


if self.__levels <= <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" % self.__levels)<EOL><DEDENT>if len(self.__data) == <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>if self.__density_threshold < <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" % self.__density_threshold)<EOL><DEDENT>

!
        @brief Check input arguments of BANG algorithm and if one of them is not correct then appropriate exception
                is thrown.


leaf_blocks = self.__directory.get_leafs()<EOL>unhandled_block_indexes = set([i for i in range(len(leaf_blocks)) if leaf_blocks[i].get_density() > self.__density_threshold])<EOL>current_block = self.__find_block_center(leaf_blocks, unhandled_block_indexes)<EOL>cluster_index = <NUM_LIT:0><EOL>while current_block is not None:<EOL><INDENT>if current_block.get_density() <= self.__density_threshold or len(current_block) <= self.__amount_threshold:<EOL><INDENT>break<EOL><DEDENT>self.__expand_cluster_block(current_block, cluster_index, leaf_blocks, unhandled_block_indexes)<EOL>current_block = self.__find_block_center(leaf_blocks, unhandled_block_indexes)<EOL>cluster_index += <NUM_LIT:1><EOL><DEDENT>self.__store_clustering_results(cluster_index, leaf_blocks)<EOL>

!
        @brief Performs cluster allocation using leafs of tree in BANG directory (the smallest cells).


def __expand_cluster_block(self, block, cluster_index, leaf_blocks, unhandled_block_indexes):

block.set_cluster(cluster_index)<EOL>self.__update_cluster_dendrogram(cluster_index, [block])<EOL>neighbors = self.__find_block_neighbors(block, leaf_blocks, unhandled_block_indexes)<EOL>self.__update_cluster_dendrogram(cluster_index, neighbors)<EOL>for neighbor in neighbors:<EOL><INDENT>neighbor.set_cluster(cluster_index)<EOL>neighbor_neighbors = self.__find_block_neighbors(neighbor, leaf_blocks, unhandled_block_indexes)<EOL>self.__update_cluster_dendrogram(cluster_index, neighbor_neighbors)<EOL>neighbors += neighbor_neighbors<EOL><DEDENT>

!
        @brief Expand cluster from specific block that is considered as a central block.

        @param[in] block (bang_block): Block that is considered as a central block for cluster.
        @param[in] cluster_index (uint): Index of cluster that is assigned to blocks that forms new cluster.
        @param[in] leaf_blocks (list): Leaf BANG-blocks that are considered during cluster formation.
        @param[in] unhandled_block_indexes (set): Set of candidates (BANG block indexes) to become a cluster member. The
                    parameter helps to reduce traversing among BANG-block providing only restricted set of block that
                    should be considered.


def __store_clustering_results(self, amount_clusters, leaf_blocks):

self.__clusters = [[] for _ in range(amount_clusters)]<EOL>for block in leaf_blocks:<EOL><INDENT>index = block.get_cluster()<EOL>if index is not None:<EOL><INDENT>self.__clusters[index] += block.get_points()<EOL><DEDENT>else:<EOL><INDENT>self.__noise += block.get_points()<EOL><DEDENT><DEDENT>self.__clusters = [ list(set(cluster)) for cluster in self.__clusters ]<EOL>self.__noise = list(set(self.__noise))<EOL>

!
        @brief Stores clustering results in a convenient way.

        @param[in] amount_clusters (uint): Amount of cluster that was allocated during processing.
        @param[in] leaf_blocks (list): Leaf BANG-blocks (the smallest cells).


def __find_block_center(self, level_blocks, unhandled_block_indexes):

for i in reversed(range(len(level_blocks))):<EOL><INDENT>if level_blocks[i].get_density() <= self.__density_threshold:<EOL><INDENT>return None<EOL><DEDENT>if level_blocks[i].get_cluster() is None:<EOL><INDENT>unhandled_block_indexes.remove(i)<EOL>return level_blocks[i]<EOL><DEDENT><DEDENT>return None<EOL>

!
        @brief Search block that is cluster center for new cluster.

        @return (bang_block) Central block for new cluster, if cluster is not found then None value is returned.


def __find_block_neighbors(self, block, level_blocks, unhandled_block_indexes):

neighbors = []<EOL>handled_block_indexes = []<EOL>for unhandled_index in unhandled_block_indexes:<EOL><INDENT>if block.is_neighbor(level_blocks[unhandled_index]):<EOL><INDENT>handled_block_indexes.append(unhandled_index)<EOL>neighbors.append(level_blocks[unhandled_index])<EOL>if len(neighbors) == <NUM_LIT:8>:<EOL><INDENT>break<EOL><DEDENT><DEDENT><DEDENT>for handled_index in handled_block_indexes:<EOL><INDENT>unhandled_block_indexes.remove(handled_index)<EOL><DEDENT>return neighbors<EOL>

!
        @brief Search block neighbors that are parts of new clusters (density is greater than threshold and that are
                not cluster members yet), other neighbors are ignored.

        @param[in] block (bang_block): BANG-block for which neighbors should be found (which can be part of cluster).
        @param[in] level_blocks (list): BANG-blocks on specific level.
        @param[in] unhandled_block_indexes (set): Blocks that have not been processed yet.

        @return (list) Block neighbors that can become part of cluster.