11"""ROCKAD anomaly detector."""
22
3+ __maintainer__ = []
34__all__ = ["ROCKAD" ]
45
56import warnings
1718
1819class ROCKAD (BaseSeriesAnomalyDetector ):
1920 """
20- ROCKET-based Anomaly Detector (ROCKAD).
21+ ROCKET-based Semi-Supervised Anomaly Detector (ROCKAD).
2122
23+ Adapted ROCKAD [1]_ version to detect anomalies on time-points.
2224 ROCKAD leverages the ROCKET transformation for feature extraction from
2325 time series data and applies the scikit learn k-nearest neighbors (k-NN)
2426 approach with bootstrap aggregation for robust anomaly detection.
2527 After windowing, the data gets transformed into the ROCKET feature space.
2628 Then the windows are compared based on the feature space by
27- finding the nearest neighbours.
29+ finding the nearest neighbours. Whole-series based ROCKAD as proposed in
30+ [1]_ can be found at aeon/anomaly_detection/collection/_rockad.py
2831
2932 This class supports both univariate and multivariate time series and
3033 provides options for normalizing features, applying power transformations,
@@ -61,6 +64,31 @@ class ROCKAD(BaseSeriesAnomalyDetector):
6164 List containing k-NN estimators used for anomaly scoring, set after fitting.
6265 power_transformer_ : PowerTransformer
6366 Transformer used to apply power transformation to the features.
67+
68+ References
69+ ----------
70+ .. [1] Theissler, A., Wengert, M., Gerschner, F. (2023).
71+ ROCKAD: Transferring ROCKET to Whole Time Series Anomaly Detection.
72+ In: Crémilleux, B., Hess, S., Nijssen, S. (eds) Advances in Intelligent
73+ Data Analysis XXI. IDA 2023. Lecture Notes in Computer Science,
74+ vol 13876. Springer, Cham. https://doi.org/10.1007/978-3-031-30047-9_33
75+
76+ Examples
77+ --------
78+ >>> import numpy as np
79+ >>> from aeon.anomaly_detection.series.distance_based import ROCKAD
80+ >>> rng = np.random.default_rng(seed=42)
81+ >>> X_train = rng.normal(loc=0.0, scale=1.0, size=(1000,))
82+ >>> X_test = rng.normal(loc=0.0, scale=1.0, size=(20,))
83+ >>> X_test[15:20] -= 5
84+ >>> detector = ROCKAD(window_size=15,n_estimators=10,n_kernels=10,n_neighbors=3)
85+ >>> detector.fit(X_train)
86+ ROCKAD(...)
87+ >>> detector.predict(X_test)
88+ array([0. , 0.00554713, 0.0699094 , 0.22881059, 0.32382585,
89+ 0.43652154, 0.43652154, 0.43652154, 0.43652154, 0.43652154,
90+ 0.43652154, 0.43652154, 0.43652154, 0.43652154, 0.43652154,
91+ 0.52382585, 0.65200875, 0.80313368, 0.85194345, 1. ])
6492 """
6593
6694 _tags = {
@@ -86,7 +114,6 @@ def __init__(
86114 n_jobs = 1 ,
87115 random_state = 42 ,
88116 ):
89-
90117 self .n_estimators = n_estimators
91118 self .n_kernels = n_kernels
92119 self .normalise = normalise
@@ -136,7 +163,6 @@ def _check_params(self, X: np.ndarray) -> None:
136163 )
137164
138165 def _inner_fit (self , X : np .ndarray ) -> None :
139-
140166 self .rocket_transformer_ = Rocket (
141167 n_kernels = self .n_kernels ,
142168 normalise = self .normalise ,
@@ -189,7 +215,6 @@ def _inner_fit(self, X: np.ndarray) -> None:
189215 self .list_baggers_ .append (estimator )
190216
191217 def _predict (self , X ) -> np .ndarray :
192-
193218 _X , padding = sliding_windows (
194219 X , window_size = self .window_size , stride = self .stride , axis = 0
195220 )
@@ -209,22 +234,8 @@ def _fit_predict(self, X: np.ndarray, y: Optional[np.ndarray] = None) -> np.ndar
209234 return point_anomaly_scores
210235
211236 def _inner_predict (self , X : np .ndarray , padding : int ) -> np .ndarray :
212-
213- anomaly_scores = self ._predict_proba (X )
214-
215- point_anomaly_scores = reverse_windowing (
216- anomaly_scores , self .window_size , np .nanmean , self .stride , padding
217- )
218-
219- point_anomaly_scores = (point_anomaly_scores - point_anomaly_scores .min ()) / (
220- point_anomaly_scores .max () - point_anomaly_scores .min ()
221- )
222-
223- return point_anomaly_scores
224-
225- def _predict_proba (self , X ):
226237 """
227- Predicts the probability of anomalies for the input data.
238+ Predict the anomaly score for each time-point in the input data.
228239
229240 Parameters
230241 ----------
@@ -259,6 +270,14 @@ def _predict_proba(self, X):
259270 y_scores [:, idx ] = scores
260271
261272 # Average the scores to get the final score for each time series
262- y_scores = y_scores .mean (axis = 1 )
273+ anomaly_scores = y_scores .mean (axis = 1 )
263274
264- return y_scores
275+ point_anomaly_scores = reverse_windowing (
276+ anomaly_scores , self .window_size , np .nanmean , self .stride , padding
277+ )
278+
279+ point_anomaly_scores = (point_anomaly_scores - point_anomaly_scores .min ()) / (
280+ point_anomaly_scores .max () - point_anomaly_scores .min ()
281+ )
282+
283+ return point_anomaly_scores
0 commit comments