Skip to content

Commit 8049fe8

Browse files
author
Jaime Céspedes Sisniega
authored
Merge pull request #255 from IFCA/feature-doc-api-detectors-examples
Add detectors examples in documentation API
2 parents e2caf27 + e8bac09 commit 8049fe8

File tree

33 files changed

+972
-419
lines changed

33 files changed

+972
-419
lines changed

frouros/detectors/concept_drift/streaming/change_detection/base.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,19 @@ def _update(self, value: Union[int, float], **kwargs) -> None:
2626

2727

2828
class BaseCUSUMConfig(BaseChangeDetectionConfig):
29-
"""Class representing a CUSUM based configuration class."""
29+
"""Class representing a CUSUM based configuration class.
3030
31-
def __init__(
31+
:param lambda_: lambda value, defaults to 50.0
32+
:type lambda_: float
33+
:param min_num_instances: minimum numbers of instances to start looking for changes, defaults to 30
34+
:type min_num_instances: int
35+
""" # noqa: E501 # pylint: disable=line-too-long
36+
37+
def __init__( # noqa: D107
3238
self,
3339
lambda_: float = 50.0,
3440
min_num_instances: int = 30,
3541
) -> 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-
"""
4442
super().__init__(min_num_instances=min_num_instances)
4543
self.lambda_ = lambda_
4644

@@ -139,25 +137,23 @@ def alpha(self, value: float) -> None:
139137

140138

141139
class BaseCUSUM(BaseChangeDetection):
142-
"""CUSUM based algorithm class."""
140+
"""CUSUM based algorithm class.
141+
142+
:param config: configuration parameters, defaults to None
143+
:type config: Optional[BaseCUSUMConfig]
144+
:param callbacks: callbacks, defaults to None
145+
:type callbacks: Optional[Union[BaseCallbackStreaming, List[BaseCallbackStreaming]]]
146+
""" # noqa: E501
143147

144148
config_type = BaseCUSUMConfig
145149

146-
def __init__(
150+
def __init__( # noqa: D107
147151
self,
148152
config: Optional[BaseCUSUMConfig] = None,
149153
callbacks: Optional[
150154
Union[BaseCallbackStreaming, List[BaseCallbackStreaming]]
151155
] = None,
152156
) -> 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-
"""
161157
super().__init__(
162158
config=config,
163159
callbacks=callbacks,

frouros/detectors/concept_drift/streaming/change_detection/bocd.py

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
import abc
44
import copy
5-
from typing import Union, Optional
5+
from typing import List, Union, Optional
66

77
import numpy as np # type: ignore
88
from scipy.special import logsumexp # type: ignore
99
from scipy.stats import norm # type: ignore
1010

11+
from frouros.callbacks.streaming.base import BaseCallbackStreaming
1112
from 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:
4142
class 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):
121123
class 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

178182
class 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,

frouros/detectors/concept_drift/streaming/change_detection/cusum.py

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""CUSUM module."""
22

3+
from typing import Optional, Union, List
4+
35
import numpy as np # type: ignore
46

7+
from frouros.callbacks.streaming.base import BaseCallbackStreaming
58
from frouros.detectors.concept_drift.streaming.change_detection.base import (
69
BaseCUSUM,
710
BaseCUSUMConfig,
@@ -12,29 +15,26 @@
1215
class CUSUMConfig(BaseCUSUMConfig, DeltaConfig):
1316
"""CUSUM [page1954continuous]_ configuration.
1417
18+
:param delta: delta value, defaults to 0.005
19+
:type delta: float
20+
:param lambda_: delta value, defaults to 50.0
21+
:type lambda_: float
22+
:param min_num_instances: minimum numbers of instances to start looking for changes, defaults to 30
23+
:type min_num_instances: int
24+
1525
:References:
1626
1727
.. [page1954continuous] Page, Ewan S.
1828
"Continuous inspection schemes."
1929
Biometrika 41.1/2 (1954): 100-115.
20-
"""
30+
""" # noqa: E501 # pylint: disable=line-too-long
2131

22-
def __init__(
32+
def __init__( # noqa: D107
2333
self,
2434
delta: float = 0.005,
2535
lambda_: float = 50.0,
2636
min_num_instances: int = 30,
2737
) -> None:
28-
"""Init method.
29-
30-
:param delta: delta value
31-
:type delta: float
32-
:param lambda_: delta value
33-
:type lambda_: float
34-
:param min_num_instances: minimum numbers of instances
35-
to start looking for changes
36-
:type min_num_instances: int
37-
"""
3838
BaseCUSUMConfig.__init__(
3939
self, lambda_=lambda_, min_num_instances=min_num_instances
4040
)
@@ -44,15 +44,48 @@ def __init__(
4444
class CUSUM(BaseCUSUM):
4545
"""CUSUM [page1954continuous]_ detector.
4646
47+
:param config: configuration object of the detector, defaults to None. If None, the default configuration of :class:`CUSUMConfig` is used.
48+
:type config: Optional[CUSUMConfig]
49+
:param callbacks: callbacks, defaults to None
50+
:type callbacks: Optional[Union[BaseCallbackStreaming, List[BaseCallbackStreaming]]]
51+
4752
:References:
4853
4954
.. [page1954continuous] Page, Ewan S.
5055
"Continuous inspection schemes."
5156
Biometrika 41.1/2 (1954): 100-115.
52-
"""
57+
58+
:Example:
59+
60+
>>> from frouros.detectors.concept_drift import CUSUM
61+
>>> import numpy as np
62+
>>> np.random.seed(seed=31)
63+
>>> dist_a = np.random.normal(loc=0.2, scale=0.01, size=1000)
64+
>>> dist_b = np.random.normal(loc=0.8, scale=0.04, size=1000)
65+
>>> stream = np.concatenate((dist_a, dist_b))
66+
>>> detector = CUSUM()
67+
>>> for i, value in enumerate(stream):
68+
... _ = detector.update(value=value)
69+
... if detector.drift:
70+
... print(f"Change detected at step {i}")
71+
... break
72+
Change detected at step 1086
73+
""" # noqa: E501 # pylint: disable=line-too-long
5374

5475
config_type = CUSUMConfig # type: ignore
5576

77+
def __init__( # noqa: D107
78+
self,
79+
config: Optional[CUSUMConfig] = None,
80+
callbacks: Optional[
81+
Union[BaseCallbackStreaming, List[BaseCallbackStreaming]]
82+
] = None,
83+
) -> None:
84+
super().__init__(
85+
config=config,
86+
callbacks=callbacks,
87+
)
88+
5689
def _update_sum(self, error_rate: float) -> None:
5790
self.sum_ = np.maximum(
5891
0,

0 commit comments

Comments
 (0)