11"""Base concept drift ChangeDetection based module."""
22
33import abc
4- from typing import Union
4+ from typing import List , Optional , Union
55
6+ from frouros .callbacks .streaming .base import BaseCallbackStreaming
67from frouros .detectors .concept_drift .streaming .base import (
78 BaseConceptDriftStreaming ,
89 BaseConceptDriftStreamingConfig ,
910)
11+ from frouros .utils .stats import Mean
1012
1113
1214class BaseChangeDetectionConfig (BaseConceptDriftStreamingConfig ):
@@ -21,3 +23,207 @@ class BaseChangeDetection(BaseConceptDriftStreaming):
2123 @abc .abstractmethod
2224 def _update (self , value : Union [int , float ], ** kwargs ) -> None :
2325 pass
26+
27+
28+ class BaseCUSUMConfig (BaseChangeDetectionConfig ):
29+ """Class representing a CUSUM based configuration class."""
30+
31+ def __init__ (
32+ self ,
33+ lambda_ : float = 50.0 ,
34+ min_num_instances : int = 30 ,
35+ ) -> None :
36+ """Init method.
37+
38+ :param lambda_: lambda value
39+ :type lambda_: float
40+ :param min_num_instances: minimum numbers of instances
41+ to start looking for changes
42+ :type min_num_instances: int
43+ """
44+ super ().__init__ (min_num_instances = min_num_instances )
45+ self .lambda_ = lambda_
46+
47+ @property
48+ def lambda_ (self ) -> float :
49+ """Threshold property.
50+
51+ :return: lambda to use
52+ :rtype: float
53+ """
54+ return self ._lambda
55+
56+ @lambda_ .setter
57+ def lambda_ (self , value : float ) -> None :
58+ """Threshold setter.
59+
60+ :param value: value to be set
61+ :type value: float
62+ :raises ValueError: Value error exception
63+ """
64+ if value < 0 :
65+ raise ValueError ("lambda_ must be great or equal than 0." )
66+ self ._lambda = value
67+
68+
69+ class DeltaConfig :
70+ """Class representing a delta configuration class."""
71+
72+ def __init__ (
73+ self ,
74+ delta : float = 0.005 ,
75+ ) -> None :
76+ """Init method.
77+
78+ :param delta: delta value
79+ :type delta: float
80+ """
81+ self .delta = delta
82+
83+ @property
84+ def delta (self ) -> float :
85+ """Delta property.
86+
87+ :return: delta to use
88+ :rtype: float
89+ """
90+ return self ._delta
91+
92+ @delta .setter
93+ def delta (self , value : float ) -> None :
94+ """Delta setter.
95+
96+ :param value: value to be set
97+ :type value: float
98+ :raises ValueError: Value error exception
99+ """
100+ if not 0.0 <= value <= 1.0 :
101+ raise ValueError ("delta must be in the range [0, 1]." )
102+ self ._delta = value
103+
104+
105+ class AlphaConfig :
106+ """Class representing an alpha configuration class."""
107+
108+ def __init__ (
109+ self ,
110+ alpha : float = 0.9999 ,
111+ ) -> None :
112+ """Init method.
113+
114+ :param alpha: forgetting factor value
115+ :type alpha: float
116+ """
117+ self .alpha = alpha
118+
119+ @property
120+ def alpha (self ) -> float :
121+ """Forgetting factor property.
122+
123+ :return: forgetting factor value
124+ :rtype: float
125+ """
126+ return self ._alpha
127+
128+ @alpha .setter
129+ def alpha (self , value : float ) -> None :
130+ """Forgetting factor setter.
131+
132+ :param value: forgetting factor value
133+ :type value: float
134+ :raises ValueError: Value error exception
135+ """
136+ if not 0.0 <= value <= 1.0 :
137+ raise ValueError ("alpha must be in the range [0, 1]." )
138+ self ._alpha = value
139+
140+
141+ class BaseCUSUM (BaseChangeDetection ):
142+ """CUSUM based algorithm class."""
143+
144+ config_type = BaseCUSUMConfig
145+
146+ def __init__ (
147+ self ,
148+ config : Optional [BaseCUSUMConfig ] = None ,
149+ callbacks : Optional [
150+ Union [BaseCallbackStreaming , List [BaseCallbackStreaming ]]
151+ ] = None ,
152+ ) -> None :
153+ """Init method.
154+
155+ :param config: configuration parameters
156+ :type config: Optional[BaseCUSUMConfig]
157+ :param callbacks: callbacks
158+ :type callbacks: Optional[Union[BaseCallbackStreaming,
159+ List[BaseCallbackStreaming]]]
160+ """
161+ super ().__init__ (
162+ config = config ,
163+ callbacks = callbacks ,
164+ )
165+ self .additional_vars = {
166+ "mean_error_rate" : Mean (),
167+ "sum_" : 0.0 ,
168+ }
169+ self ._set_additional_vars_callback ()
170+
171+ @property
172+ def mean_error_rate (self ) -> Mean :
173+ """Mean error rate property.
174+
175+ :return: mean error rate to use
176+ :rtype: Mean
177+ """
178+ return self ._additional_vars ["mean_error_rate" ]
179+
180+ @mean_error_rate .setter
181+ def mean_error_rate (self , value : Mean ) -> None :
182+ """Mean error rate setter.
183+
184+ :param value: value to be set
185+ :type value: Mean
186+ """
187+ self ._additional_vars ["mean_error_rate" ] = value
188+
189+ @property
190+ def sum_ (self ) -> float :
191+ """Sum count property.
192+
193+ :return: sum count value
194+ :rtype: float
195+ """
196+ return self ._additional_vars ["sum_" ]
197+
198+ @sum_ .setter
199+ def sum_ (self , value : float ) -> None :
200+ """Sum count setter.
201+
202+ :param value: value to be set
203+ :type value: float
204+ """
205+ self ._additional_vars ["sum_" ] = value
206+
207+ @abc .abstractmethod
208+ def _update_sum (self , error_rate : float ) -> None :
209+ pass
210+
211+ def reset (self ) -> None :
212+ """Reset method."""
213+ super ().reset ()
214+ self .mean_error_rate = Mean ()
215+ self .sum_ = 0.0
216+
217+ def _update (self , value : Union [int , float ], ** kwargs ) -> None :
218+ self .num_instances += 1
219+
220+ self .mean_error_rate .update (value = value )
221+ self ._update_sum (error_rate = value )
222+
223+ if (
224+ self .num_instances >= self .config .min_num_instances # type: ignore
225+ and self .sum_ > self .config .lambda_ # type: ignore
226+ ):
227+ self .drift = True
228+ else :
229+ self .drift = False
0 commit comments