Skip to content

Commit 7621454

Browse files
committed
Add anomaly detector based on standard deviation range from mean
1 parent 6041f5b commit 7621454

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

src/adtk/detector/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
PersistAD,
1919
QuantileAD,
2020
SeasonalAD,
21+
VolatilityRangeAD,
2122
ThresholdAD,
2223
VolatilityShiftAD,
2324
)
@@ -56,6 +57,7 @@ def print_all_models() -> None:
5657
"ThresholdAD",
5758
"QuantileAD",
5859
"InterQuartileRangeAD",
60+
"VolatilityRangeAD",
5961
"GeneralizedESDTestAD",
6062
"PersistAD",
6163
"LevelShiftAD",

src/adtk/detector/_detector_1d.py

+59
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,65 @@ def _predict_core(self, s: pd.Series) -> pd.Series:
271271
return predicted
272272

273273

274+
class VolatilityRangeAD(_TrainableUnivariateDetector):
275+
"""Anomaly detector based on standard deviation range.
276+
277+
This detector flags anomalies for values that are outside the range of [mean - c * std, mean + c * std].
278+
279+
Parameters
280+
----------
281+
c : float, optional (default=1.0)
282+
The multiplier for the standard deviation to set the range for anomaly detection.
283+
"""
284+
285+
def __init__(
286+
self,
287+
c: Union[
288+
Optional[float], Tuple[Optional[float], Optional[float]]
289+
] = 3.0,
290+
) -> None:
291+
super().__init__()
292+
self.c = c
293+
294+
@property
295+
def _param_names(self) -> Tuple[str, ...]:
296+
return ("c",)
297+
298+
def _fit_core(self, s: pd.Series) -> None:
299+
if s.count() == 0:
300+
raise RuntimeError("Valid values are not enough for training.")
301+
mean_val = s.mean()
302+
std_val = s.std()
303+
304+
self.abs_low_ = (
305+
(
306+
mean_val
307+
- std_val
308+
* (self.c if (not isinstance(self.c, tuple)) else self.c[0])
309+
)
310+
if (
311+
(self.c if (not isinstance(self.c, tuple)) else self.c[0])
312+
is not None
313+
)
314+
else -float("inf")
315+
)
316+
self.abs_high_ = (
317+
mean_val
318+
+ std_val
319+
* (self.c if (not isinstance(self.c, tuple)) else self.c[1])
320+
if (
321+
(self.c if (not isinstance(self.c, tuple)) else self.c[1])
322+
is not None
323+
)
324+
else float("inf")
325+
)
326+
327+
def _predict_core(self, s: pd.Series) -> pd.Series:
328+
predicted = (s > self.abs_high_) | (s < self.abs_low_)
329+
predicted[s.isna()] = np.nan
330+
return predicted
331+
332+
274333
class GeneralizedESDTestAD(_TrainableUnivariateDetector):
275334
"""Detector that detects anomaly based on generalized ESD test.
276335

0 commit comments

Comments
 (0)