Completed
Push — master ( 57e143...200d41 )
by Andrei
01:29
created

manhattan_distance_numpy()   A

Complexity

Conditions 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
"""!
2
3
@brief Module provides various distance metrics - abstraction of the notion of distance in a metric space.
4
5
@authors Andrei Novikov ([email protected])
6
@date 2014-2018
7
@copyright GNU Public License
8
9
@cond GNU_PUBLIC_LICENSE
10
    PyClustering is free software: you can redistribute it and/or modify
11
    it under the terms of the GNU General Public License as published by
12
    the Free Software Foundation, either version 3 of the License, or
13
    (at your option) any later version.
14
15
    PyClustering is distributed in the hope that it will be useful,
16
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
    GNU General Public License for more details.
19
20
    You should have received a copy of the GNU General Public License
21
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
@endcond
23
24
"""
25
26
27
import numpy
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
28
29
from enum import IntEnum
30
31
32
class type_metric(IntEnum):
33
    """!
34
    @brief Enumeration of supported metrics in the module for distance calculation between two points.
35
36
    """
37
38
    ## Euclidean distance, for more information see function 'euclidean_distance'.
39
    EUCLIDEAN = 0
40
41
    ## Square Euclidean distance, for more information see function 'euclidean_distance_square'.
42
    EUCLIDEAN_SQUARE = 1
43
44
    ## Manhattan distance, for more information see function 'manhattan_distance'.
45
    MANHATTAN = 2
46
47
    ## Chebyshev distance, for more information see function 'chebyshev_distance'.
48
    CHEBYSHEV = 3
49
50
    ## Minkowski distance, for more information see function 'minkowski_distance'.
51
    MINKOWSKI = 4
52
53
    ## User defined function for distance calculation between two points.
54
    USER_DEFINED = 1000
55
56
57
58
class distance_metric:
59
    """!
60
    @brief Distance metric performs distance calculation between two points in line with encapsulated function, for
61
            example, euclidean distance or chebyshev distance, or even user-defined.
62
63
    @details
64
65
    Example of Euclidean distance metric:
66
    @code
67
        metric = distance_metric(type_metric.EUCLIDEAN)
68
        distance = metric([1.0, 2.5], [-1.2, 3.4])
69
    @endcode
70
71
    Example of Chebyshev distance metric:
72
    @code
73
        metric = distance_metric(type_metric.CHEBYSHEV)
74
        distance = metric([0.0, 0.0], [2.5, 6.0])
75
    @endcode
76
77
    In following example additional argument should be specified (generally, 'degree' is a optional argument that is
78
     equal to '2' by default) that is specific for Minkowski distance:
79
    @code
80
        metric = distance_metric(type_metric.MINKOWSKI, degree=4)
81
        distance = metric([4.0, 9.2, 1.0], [3.4, 2.5, 6.2])
82
    @endcode
83
84
    User may define its own function for distance calculation:
85
    @code
86
        user_function = lambda point1, point2: point1[0] + point2[0] + 2
87
        metric = distance_metric(type_metric.USER_DEFINED, func=user_function)
88
        distance = metric([2.0, 3.0], [1.0, 3.0])
89
    @endcode
90
91
    """
92
    def __init__(self, type, **kwargs):
93
        """!
94
        @brief Creates distance metric instance for calculation distance between two points.
95
96
        @param[in] type (type_metric):
97
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'numpy_usage' 'func' and corresponding additional argument for
98
                    for specific metric types).
99
100
        <b>Keyword Args:</b><br>
101
            - func (callable): Callable object with two arguments (point #1 and point #2) or (object #1 and object #2) in case of numpy usage.
102
                                This argument is used only if metric is 'type_metric.USER_DEFINED'.
103
            - degree (numeric): Only for 'type_metric.MINKOWSKI' - degree of Minkowski equation.
104
            - numpy_usage (bool): If True then numpy is used for calculation (by default is False).
105
106
        """
107
        self.__type = type
108
        self.__args = kwargs
109
        self.__func = self.__args.get('func', None)
110
        self.__numpy = self.__args.get('numpy_usage', False)
111
112
        self.__calculator = self.__create_distance_calculator()
113
114
115
    def __call__(self, point1, point2):
116
        """!
117
        @brief Calculates distance between two points.
118
119
        @param[in] point1 (list): The first point.
120
        @param[in] point2 (list): The second point.
121
122
        @return (double) Distance between two points.
123
124
        """
125
        return self.__calculator(point1, point2)
126
127
128
    def get_type(self):
129
        """!
130
        @brief Return type of distance metric that is used.
131
132
        @return (type_metric) Type of distance metric.
133
134
        """
135
        return self.__type
136
137
138
    def get_arguments(self):
139
        """!
140
        @brief Return additional arguments that are used by distance metric.
141
142
        @return (dict) Additional arguments.
143
144
        """
145
        return self.__args
146
147
148
    def get_function(self):
149
        """!
150
        @brief Return user-defined function for calculation distance metric.
151
152
        @return (callable): User-defined distance metric function.
153
154
        """
155
        return self.__func
156
157
158
    def enable_numpy_usage(self):
159
        """!
160
        @brief Start numpy for distance calculation.
161
        @details Useful in case matrices to increase performance.
162
163
        """
164
        self.__numpy = True
165
        self.__calculator = self.__create_distance_calculator()
166
167
168
    def disable_numpy_usage(self):
169
        """!
170
        @brief Stop using numpy for distance calculation.
171
        @details Useful in case of big amount of small data portion when numpy call is longer than calculation itself.
172
173
        """
174
        self.__numpy = False
175
        self.__calculator = self.__create_distance_calculator()
176
177
178
    def __create_distance_calculator(self):
179
        if self.__numpy is True:
180
            return self.__create_distance_calculator_numpy()
181
182
        return self.__create_distance_calculator_basic()
183
184
185 View Code Duplication
    def __create_distance_calculator_basic(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
186
        """!
187
        @brief Creates distance metric calculator.
188
189
        @return (callable) Callable object of distance metric calculator.
190
191
        """
192
        if self.__type == type_metric.EUCLIDEAN:
193
            return euclidean_distance
194
195
        elif self.__type == type_metric.EUCLIDEAN_SQUARE:
196
            return euclidean_distance_square
197
198
        elif self.__type == type_metric.MANHATTAN:
199
            return manhattan_distance
200
201
        elif self.__type == type_metric.CHEBYSHEV:
202
            return chebyshev_distance
203
204
        elif self.__type == type_metric.MINKOWSKI:
205
            return lambda point1, point2: minkowski_distance(point1, point2, self.__args.get('degree', 2))
206
207
        elif self.__type == type_metric.USER_DEFINED:
208
            return self.__func
209
210
        else:
211
            raise ValueError("Unknown type of metric: '%d'", self.__type)
212
213
214 View Code Duplication
    def __create_distance_calculator_numpy(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
215
        """!
216
        @brief Creates distance metric calculator that uses numpy.
217
218
        @return (callable) Callable object of distance metric calculator.
219
220
        """
221
        if self.__type == type_metric.EUCLIDEAN:
222
            return euclidean_distance_numpy
223
224
        elif self.__type == type_metric.EUCLIDEAN_SQUARE:
225
            return euclidean_distance_square_numpy
226
227
        elif self.__type == type_metric.MANHATTAN:
228
            return manhattan_distance_numpy
229
230
        elif self.__type == type_metric.CHEBYSHEV:
231
            return chebyshev_distance_numpy
232
233
        elif self.__type == type_metric.MINKOWSKI:
234
            return lambda object1, object2: minkowski_distance_numpy(object1, object2, self.__args.get('degree', 2))
235
236
        elif self.__type == type_metric.USER_DEFINED:
237
            return self.__func
238
239
        else:
240
            raise ValueError("Unknown type of metric: '%d'", self.__type)
241
242
243
244
def euclidean_distance(point1, point2):
245
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
246
    @brief Calculate Euclidean distance between two vectors.
247
    @details The Euclidean between vectors (points) a and b is calculated by following formula:
248
249
    \f[
250
    dist(a, b) = \sqrt{ \sum_{i=0}^{N}(a_{i} - b_{i})^{2} };
251
    \f]
252
253
    Where N is a length of each vector.
254
255
    @param[in] point1 (array_like): The first vector.
256
    @param[in] point2 (array_like): The second vector.
257
258
    @return (double) Euclidean distance between two vectors.
259
260
    @see euclidean_distance_square, manhattan_distance, chebyshev_distance
261
262
    """
263
    distance = euclidean_distance_square(point1, point2)
264
    return distance ** 0.5
265
266
267
def euclidean_distance_numpy(object1, object2):
268
    """!
269
    @brief Calculate Euclidean distance between two objects using numpy.
270
271
    @param[in] object1 (array_like): The first array_like object.
272
    @param[in] object2 (array_like): The second array_like object.
273
274
    @return (double) Euclidean distance between two objects.
275
276
    """
277
    return numpy.sum(numpy.sqrt(numpy.square(object1 - object2)), axis=1).T
278
279
280
def euclidean_distance_square(point1, point2):
281
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
282
    @brief Calculate square Euclidean distance between two vectors.
283
284
    \f[
285
    dist(a, b) = \sum_{i=0}^{N}(a_{i} - b_{i})^{2};
286
    \f]
287
288
    @param[in] point1 (array_like): The first vector.
289
    @param[in] point2 (array_like): The second vector.
290
291
    @return (double) Square Euclidean distance between two vectors.
292
293
    @see euclidean_distance, manhattan_distance, chebyshev_distance
294
295
    """
296
    distance = 0.0
297
    for i in range(len(point1)):
298
        distance += (point1[i] - point2[i]) ** 2.0
299
300
    return distance
301
302
303
def euclidean_distance_square_numpy(object1, object2):
304
    """!
305
    @brief Calculate square Euclidean distance between two objects using numpy.
306
307
    @param[in] object1 (array_like): The first array_like object.
308
    @param[in] object2 (array_like): The second array_like object.
309
310
    @return (double) Square Euclidean distance between two objects.
311
312
    """
313
    return numpy.sum(numpy.square(object1 - object2), axis=1).T
314
315
316
def manhattan_distance(point1, point2):
317
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \l was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
318
    @brief Calculate Manhattan distance between between two vectors.
319
320
    \f[
321
    dist(a, b) = \sum_{i=0}^{N}\left | a_{i} - b_{i} \right |;
322
    \f]
323
324
    @param[in] point1 (array_like): The first vector.
325
    @param[in] point2 (array_like): The second vector.
326
327
    @return (double) Manhattan distance between two vectors.
328
329
    @see euclidean_distance_square, euclidean_distance, chebyshev_distance
330
331
    """
332
    distance = 0.0
333
    dimension = len(point1)
334
335
    for i in range(dimension):
336
        distance += abs(point1[i] - point2[i])
337
338
    return distance
339
340
341
def manhattan_distance_numpy(object1, object2):
342
    """!
343
    @brief Calculate Manhattan distance between two objects using numpy.
344
345
    @param[in] object1 (array_like): The first array_like object.
346
    @param[in] object2 (array_like): The second array_like object.
347
348
    @return (double) Manhattan distance between two objects.
349
350
    """
351
    return numpy.sum(numpy.absolute(object1 - object2), axis=1).T
352
353
354
def chebyshev_distance(point1, point2):
355
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \m was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \l was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
356
    @brief Calculate Chebyshev distance between between two vectors.
357
358
    \f[
359
    dist(a, b) = \max_{}i\left (\left | a_{i} - b_{i} \right |\right );
360
    \f]
361
362
    @param[in] point1 (array_like): The first vector.
363
    @param[in] point2 (array_like): The second vector.
364
365
    @return (double) Chebyshev distance between two vectors.
366
367
    @see euclidean_distance_square, euclidean_distance, minkowski_distance
368
369
    """
370
    distance = 0.0
371
    dimension = len(point1)
372
373
    for i in range(dimension):
374
        distance = max(distance, abs(point1[i] - point2[i]))
375
376
    return distance
377
378
379
def chebyshev_distance_numpy(object1, object2):
380
    """!
381
    @brief Calculate Chebyshev distance between two objects using numpy.
382
383
    @param[in] object1 (array_like): The first array_like object.
384
    @param[in] object2 (array_like): The second array_like object.
385
386
    @return (double) Chebyshev distance between two objects.
387
388
    """
389
    return numpy.max(numpy.absolute(object1 - object2), axis=1).T
390
391
392
def minkowski_distance(point1, point2, degree=2):
393
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \l was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
394
    @brief Calculate Minkowski distance between two vectors.
395
396
    \f[
397
    dist(a, b) = \sqrt[p]{ \sum_{i=0}^{N}\left(a_{i} - b_{i}\right)^{p} };
398
    \f]
399
400
    @param[in] point1 (array_like): The first vector.
401
    @param[in] point2 (array_like): The second vector.
402
    @param[in] degree (numeric): Degree of that is used for Minkowski distance.
403
404
    @return (double) Minkowski distance between two vectors.
405
406
    @see euclidean_distance
407
408
    """
409
    distance = 0.0
410
    for i in range(len(point1)):
411
        distance += (point1[i] - point2[i]) ** degree
412
413
    return distance ** (1.0 / degree)
414
415
416
def minkowski_distance_numpy(object1, object2, degree=2):
417
    """!
418
    @brief Calculate Minkowski distance between objects using numpy.
419
420
    @param[in] object1 (array_like): The first array_like object.
421
    @param[in] object2 (array_like): The second array_like object.
422
    @param[in] degree (numeric): Degree of that is used for Minkowski distance.
423
424
    @return (double) Minkowski distance between two object.
425
426
    """
427
    return numpy.sum(numpy.power(numpy.power(object1 - object2, degree), 1/degree), axis=1).T