22
33import abc
44import copy
5- from typing import Union , Optional
5+ from typing import List , Union , Optional
66
77import numpy as np # type: ignore
88from scipy .special import logsumexp # type: ignore
99from scipy .stats import norm # type: ignore
1010
11+ from frouros .callbacks .streaming .base import BaseCallbackStreaming
1112from frouros .detectors .concept_drift .streaming .change_detection .base import (
1213 BaseChangeDetection ,
1314 BaseChangeDetectionConfig ,
@@ -41,7 +42,8 @@ def update(self, value: Union[int, float], **kwargs) -> None:
4142class GaussianUnknownMean (BaseBOCDModel ):
4243 """Gaussian unknown mean model.
4344
44- (adapted from the implementation in https://github.com/gwgundersen/bocd)
45+ :Note:
46+ Adapted from the implementation in https://github.com/gwgundersen/bocd.
4547 """
4648
4749 def __init__ (
@@ -121,33 +123,32 @@ def var_params(self):
121123class BOCDConfig (BaseChangeDetectionConfig ):
122124 """BOCD (Bayesian Online Change Detection) [adams2007bayesian]_ configuration.
123125
126+ :param model: BOCD model, defaults to None. If None, :class:`frouros.detectors.concept_drift.streaming.change_detection.bocd.GaussianUnknownMean` is used.
127+ :type model: Optional[BaseBOCDModel]
128+ :param hazard: hazard value, defaults to 0.01
129+ :type hazard: float
130+ :param min_num_instances: minimum numbers of instances to start looking for changes, defaults to 30
131+ :type min_num_instances: int
132+
124133 :References:
125134
126135 .. [adams2007bayesian] Adams, Ryan Prescott, and David JC MacKay.
127136 "Bayesian online changepoint detection."
128137 arXiv preprint arXiv:0710.3742 (2007).
129- """
138+ """ # noqa: E501 pylint: disable=line-too-long
130139
131- def __init__ (
140+ model_type = GaussianUnknownMean
141+
142+ def __init__ ( # noqa: D107
132143 self ,
133- model : BaseBOCDModel = GaussianUnknownMean , # type: ignore
144+ model : Optional [ BaseBOCDModel ] = None , # type: ignore
134145 hazard : float = 0.01 ,
135146 min_num_instances : int = 30 ,
136147 ) -> None :
137- """Init method.
138-
139- :param model: BOCD model
140- :type model: BaseBOCDModel
141- :param hazard: hazard value
142- :type hazard: float
143- :param min_num_instances: minimum numbers of instances
144- to start looking for changes
145- :type min_num_instances: int
146- """
147148 super ().__init__ (
148149 min_num_instances = min_num_instances ,
149150 )
150- self .model = model
151+ self .model = model # type: ignore
151152 self .log_hazard = np .log (hazard )
152153 self .log_1_minus_hazard = np .log (1 - hazard )
153154
@@ -168,35 +169,59 @@ def model(self, model: BaseBOCDModel) -> None:
168169 :type model: BaseBOCDModel
169170 :raises TypeError: if model is not an instance of BaseModel
170171 """
171- if not isinstance (model , BaseBOCDModel ):
172- raise TypeError (
173- f"model must be an instance of BaseModel, not { type (model )} "
174- )
175- self ._model = model
172+ if model is not None :
173+ if not isinstance (model , BaseBOCDModel ):
174+ raise TypeError (
175+ f"model must be an instance of BaseModel, not { type (model )} "
176+ )
177+ self ._model = model
178+ else :
179+ self ._model = self .model_type ()
176180
177181
178182class BOCD (BaseChangeDetection ):
179183 """BOCD (Bayesian Online Change Detection) [adams2007bayesian]_ detector.
180184
181- (adapted from the implementation in https://github.com/gwgundersen/bocd)
185+ :param config: configuration object of the detector, defaults to None. If None, the default configuration of :class:`BOCDConfig` is used.
186+ :type config: Optional[BOCDConfig]
187+ :param callbacks: callbacks, defaults to None
188+ :type callbacks: Optional[Union[BaseCallbackStreaming, List[BaseCallbackStreaming]]]
189+
190+ :Note:
191+ Adapted from the implementation in https://github.com/gwgundersen/bocd.
182192
183193 :References:
184194
185195 .. [adams2007bayesian] Adams, Ryan Prescott, and David JC MacKay.
186196 "Bayesian online changepoint detection."
187197 arXiv preprint arXiv:0710.3742 (2007).
188- """
189198
190- config_type = BOCDConfig # type: ignore
199+ :Example:
200+
201+ >>> from frouros.detectors.concept_drift import BOCD
202+ >>> import numpy as np
203+ >>> np.random.seed(seed=31)
204+ >>> dist_a = np.random.normal(loc=0.2, scale=0.01, size=1000)
205+ >>> dist_b = np.random.normal(loc=0.8, scale=0.04, size=1000)
206+ >>> stream = np.concatenate((dist_a, dist_b))
207+ >>> detector = BOCD()
208+ >>> for i, value in enumerate(stream):
209+ ... _ = detector.update(value=value)
210+ ... if detector.drift:
211+ ... print(f"Change detected at step {i}")
212+ ... break
213+ Change detected at step 1031
214+ """ # noqa: E501 # pylint: disable=line-too-long
191215
192- def __init__ (self , config : BOCDConfig , callbacks : list = None ) -> None :
193- """Init method.
216+ config_type = BOCDConfig # type: ignore
194217
195- :param config: configuration object of the detector
196- :type config: BOCDConfig
197- :param callbacks: list of callbacks, defaults to None
198- :type callbacks: list, optional
199- """
218+ def __init__ ( # noqa: D107
219+ self ,
220+ config : Optional [BOCDConfig ] = None ,
221+ callbacks : Optional [
222+ Union [BaseCallbackStreaming , List [BaseCallbackStreaming ]]
223+ ] = None ,
224+ ) -> None :
200225 super ().__init__ (
201226 config = config ,
202227 callbacks = callbacks ,
0 commit comments