Completed
Push — 0.8.dev ( a85bb3...fcf0da )
by Andrei
02:28
created

bang_block.get_density()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
"""!
2
3
@brief Cluster analysis algorithm: BANG.
4
@details Implementation based on paper @cite inproceedings::bang::1.
5
6
@authors Andrei Novikov ([email protected])
7
@date 2014-2018
8
@copyright GNU Public License
9
10
@cond GNU_PUBLIC_LICENSE
11
    PyClustering is free software: you can redistribute it and/or modify
12
    it under the terms of the GNU General Public License as published by
13
    the Free Software Foundation, either version 3 of the License, or
14
    (at your option) any later version.
15
16
    PyClustering is distributed in the hope that it will be useful,
17
    but WITHOUT ANY WARRANTY; without even the implied warranty of
18
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
    GNU General Public License for more details.
20
21
    You should have received a copy of the GNU General Public License
22
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
@endcond
24
25
"""
26
27
28
from pyclustering.utils import data_corners
29
30
31
class bang_block:
32
    def __init__(self, data, dimension, region, level, max_corner, min_corner):
33
        self.__region_number = region
34
        self.__level = level
35
        self.__data = data
36
        self.__dimension = dimension
37
        self.__max_corner = max_corner
38
        self.__min_corner = min_corner
39
        self.__density = self.__calculate_density()
40
41
42
    def get_region(self):
43
        return self.__region_number
44
45
46
    def get_level(self):
47
        return self.__level
48
49
50
    def get_density(self):
51
        return self.__density
52
53
54
    def split(self):
55
        left_region_number = self.__region_number
56
        right_region_number = self.__region_number + 2 ** self.__level
57
58
        dimension = self.__dimension + 1
59
        if dimension > len(self.__data[0]):
60
            dimension = 0
61
62
        left_min_corner = self.__min_corner[:]
63
        right_max_corner = self.__max_corner[:]
64
65
        left_min_corner[dimension] += (self.__max_corner[dimension] - self.__min_corner[dimension]) / 2.0
66
        right_max_corner[dimension] = left_min_corner[dimension]
67
68
        left = bang_block(self.__data, dimension, left_region_number, self.__level + 1, self.__max_corner, left_min_corner)
69
        right = bang_block(self.__data, dimension, right_region_number, self.__level + 1, right_max_corner, self.__min_corner)
70
71
        return left, right
72
73
74
    def contained(self, point):
75
        for i in range(len(point)):
76
            if point[i] <= self.__min_corner[i] or point[i] > self.__max_corner[i]:
77
                return False
78
79
80
    def __calculate_density(self):
81
        volume = self.__max_corner[0] - self.__min_corner[0]
82
        for i in range(1, len(self.__max_corner)):
83
            volume *= self.__max_corner[i] - self.__min_corner[i]
84
85
        amount = self.__get_amount_points()
86
        return amount / volume
87
88
89
    def __get_amount_points(self):
90
        amount = 0
91
        for point in self.__data:
92
            if self.contained(point):
93
                amount += 1
94
95
        return amount
96
97
98
99
class bang:
100
    def __init__(self, data, levels, density_threshold = 0.0):
101
        self.__data = data
102
        self.__levels = levels
103
        self.__blocks = []
104
        self.__density_threshold = density_threshold
105
106
107
    def process(self):
108
        self.__build_blocks()
109
        self.__blocks = sorted(self.__blocks, key=lambda block: block.get_density())
110
111
112
    def get_blocks(self):
113
        return self.__blocks
114
115
116
    def __build_blocks(self):
117
        max_corner, min_corner = data_corners(self.__data)
118
        root_block = bang_block(self.__data, 0, 0, 0, max_corner, min_corner)
119
120
        self.__blocks.append(root_block)
121
        level_blocks = [root_block]
122
123
        for level in range(self.__levels):
0 ignored issues
show
Unused Code introduced by
The variable level seems to be unused.
Loading history...
124
            level_blocks = self.__build_next_level_blocks(level_blocks)
125
126
127
    def __build_next_level_blocks(self, level_blocks):
128
        next_level_blocks = []
129
        for block in level_blocks:
130
            left, right = block.split()
131
132
            next_level_blocks.append(left)
133
            next_level_blocks.append(right)
134
135
        return next_level_blocks