kneeliverse.multi_knee

The following module provides one algorithm that transfroms any single knee detection algorithm into a multi-knee one.

 1# coding: utf-8
 2
 3'''
 4The following module provides one algorithm that transfroms
 5any single knee detection algorithm into a multi-knee one.
 6'''
 7
 8__author__ = 'Mário Antunes'
 9__version__ = '1.0'
10__email__ = 'mario.antunes@ua.pt'
11__status__ = 'Development'
12__license__ = 'MIT'
13__copyright__ = '''
14Copyright (c) 2021-2023 Stony Brook University
15Copyright (c) 2021-2023 The Research Foundation of SUNY
16'''
17
18import logging
19import numpy as np
20import kneeliverse.linear_fit as lf
21import kneeliverse.metrics as metrics
22
23
24logger = logging.getLogger(__name__)
25
26
27#TODO: support all the other metrics
28def multi_knee(get_knee: callable, points: np.ndarray, t1: float = 0.001, t2: int = 3, cost: metrics.Metrics = metrics.Metrics.smape) -> np.ndarray:
29    """
30    Wrapper that convert a single knee point detection into a multi knee point detector.
31
32    It uses recursion on the left and right parts of the curve after detecting the current knee.
33
34    Args:
35        get_knee (callable): method that returns a single knee point
36        points (np.ndarray): numpy array with the points (x, y)
37        t1 (float): the coefficient of determination used as a threshold (default 0.01)
38        t2 (int): the mininum number of points used as a threshold (default 3)
39        cost (metrics.Metrics): the cost method used to evaluate a point set (default: metrics.Metrics.smape)
40
41    Returns:
42        np.ndarray: knee points on the curve
43    """
44
45    stack = [(0, len(points))]
46    knees = []
47
48    while stack:
49        left, right = stack.pop()
50        pt = points[left:right]
51        
52        if len(pt) > t2:
53            if len(pt) <= 2:
54                if cost is metrics.Metrics.rmspe:
55                    r = 0.0
56                else:
57                    r = 1.0
58            else:
59                coef = lf.linear_fit_points(pt)
60                if cost is metrics.Metrics.r2:
61                    r = lf.linear_r2_points(pt, coef)
62                else:
63                    r = lf.smape_points(pt, coef)
64
65            curved = r < t1 if cost is metrics.Metrics.r2 else r >= t1
66
67            if curved:
68                rv = get_knee(pt)
69                if rv is not None:
70                    idx = rv + left
71                    knees.append(idx)
72                    stack.append((left, idx+1))
73                    stack.append((idx+1, right))
74    knees.sort()
75    return np.array(knees)
logger = <Logger kneeliverse.multi_knee (WARNING)>
def multi_knee( get_knee: <built-in function callable>, points: numpy.ndarray, t1: float = 0.001, t2: int = 3, cost: kneeliverse.metrics.Metrics = <Metrics.smape: 'smape'>) -> numpy.ndarray:
29def multi_knee(get_knee: callable, points: np.ndarray, t1: float = 0.001, t2: int = 3, cost: metrics.Metrics = metrics.Metrics.smape) -> np.ndarray:
30    """
31    Wrapper that convert a single knee point detection into a multi knee point detector.
32
33    It uses recursion on the left and right parts of the curve after detecting the current knee.
34
35    Args:
36        get_knee (callable): method that returns a single knee point
37        points (np.ndarray): numpy array with the points (x, y)
38        t1 (float): the coefficient of determination used as a threshold (default 0.01)
39        t2 (int): the mininum number of points used as a threshold (default 3)
40        cost (metrics.Metrics): the cost method used to evaluate a point set (default: metrics.Metrics.smape)
41
42    Returns:
43        np.ndarray: knee points on the curve
44    """
45
46    stack = [(0, len(points))]
47    knees = []
48
49    while stack:
50        left, right = stack.pop()
51        pt = points[left:right]
52        
53        if len(pt) > t2:
54            if len(pt) <= 2:
55                if cost is metrics.Metrics.rmspe:
56                    r = 0.0
57                else:
58                    r = 1.0
59            else:
60                coef = lf.linear_fit_points(pt)
61                if cost is metrics.Metrics.r2:
62                    r = lf.linear_r2_points(pt, coef)
63                else:
64                    r = lf.smape_points(pt, coef)
65
66            curved = r < t1 if cost is metrics.Metrics.r2 else r >= t1
67
68            if curved:
69                rv = get_knee(pt)
70                if rv is not None:
71                    idx = rv + left
72                    knees.append(idx)
73                    stack.append((left, idx+1))
74                    stack.append((idx+1, right))
75    knees.sort()
76    return np.array(knees)

Wrapper that convert a single knee point detection into a multi knee point detector.

It uses recursion on the left and right parts of the curve after detecting the current knee.

Arguments:
  • get_knee (callable): method that returns a single knee point
  • points (np.ndarray): numpy array with the points (x, y)
  • t1 (float): the coefficient of determination used as a threshold (default 0.01)
  • t2 (int): the mininum number of points used as a threshold (default 3)
  • cost (metrics.Metrics): the cost method used to evaluate a point set (default: metrics.Metrics.smape)
Returns:

np.ndarray: knee points on the curve