diff --git a/aeon/anomaly_detection/_dwt_mlead.py b/aeon/anomaly_detection/_dwt_mlead.py index 47fc9b10b4..14c3d02a15 100644 --- a/aeon/anomaly_detection/_dwt_mlead.py +++ b/aeon/anomaly_detection/_dwt_mlead.py @@ -134,8 +134,8 @@ def _predict(self, X) -> np.ndarray: if self.quantile_epsilon < 0 or self.quantile_epsilon > 1: raise ValueError("quantile_epsilon must be in [0, 1]") - X, self.N_, self.M_ = _pad_series(X) - max_level = int(np.log2(self.M_)) + X, n, m = _pad_series(X) + max_level = int(np.log2(m)) if self.start_level >= max_level: raise ValueError( @@ -168,7 +168,7 @@ def _predict(self, X) -> np.ndarray: # aggregate anomaly counts (leaf counters) point_anomaly_scores = self._push_anomaly_counts_down_to_points( - coef_anomaly_counts + coef_anomaly_counts, m, n ) return point_anomaly_scores @@ -220,19 +220,20 @@ def _reverse_windowing( return np.sum(mapped, axis=1) + @staticmethod def _push_anomaly_counts_down_to_points( - self, coef_anomaly_counts: list[np.ndarray] + coef_anomaly_counts: list[np.ndarray], m: int, n: int ) -> np.ndarray: # sum up counters of detail coeffs (orig. D^l) and approx coeffs (orig. C^l) anomaly_counts = coef_anomaly_counts[0::2] + coef_anomaly_counts[1::2] # extrapolate anomaly counts to the original series' points - counter = np.zeros(self.M_) + counter = np.zeros(m) for ac in anomaly_counts: - counter += ac.repeat(self.M_ // ac.shape[0], axis=0) + counter += ac.repeat(m // ac.shape[0], axis=0) # set event counters with count < 2 to 0 counter[counter < 2] = 0 - return counter[: self.N_] + return counter[:n] @classmethod def get_test_params(cls, parameter_set="default"): diff --git a/aeon/anomaly_detection/_pyodadapter.py b/aeon/anomaly_detection/_pyodadapter.py index 454510c405..d7f560fdf7 100644 --- a/aeon/anomaly_detection/_pyodadapter.py +++ b/aeon/anomaly_detection/_pyodadapter.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any import numpy as np +from sklearn import clone from aeon.anomaly_detection.base import BaseAnomalyDetector from aeon.utils.validation._dependencies import _check_soft_dependencies @@ -114,7 +115,7 @@ def _predict(self, X: np.ndarray) -> np.ndarray: _X, padding = sliding_windows( X, window_size=self.window_size, stride=self.stride, axis=0 ) - window_anomaly_scores = self.pyod_model.decision_function(_X) + window_anomaly_scores = self.fitted_pyod_model_.decision_function(_X) point_anomaly_scores = reverse_windowing( window_anomaly_scores, self.window_size, np.nanmean, self.stride, padding ) @@ -127,7 +128,7 @@ def _fit_predict(self, X: np.ndarray, y: np.ndarray | None = None) -> np.ndarray ) self._inner_fit(_X) - window_anomaly_scores = self.pyod_model.decision_scores_ + window_anomaly_scores = self.fitted_pyod_model_.decision_scores_ point_anomaly_scores = reverse_windowing( window_anomaly_scores, self.window_size, np.nanmean, self.stride, padding ) @@ -149,10 +150,11 @@ def _check_params(self, X: np.ndarray) -> None: ) def _inner_fit(self, X: np.ndarray) -> None: - self.pyod_model.fit(X) + self.fitted_pyod_model_: BaseDetector = clone(self.pyod_model) # type: ignore + self.fitted_pyod_model_.fit(X) def _inner_predict(self, X: np.ndarray, padding: int) -> np.ndarray: - window_anomaly_scores = self.pyod_model.decision_function(X) + window_anomaly_scores = self.fitted_pyod_model_.decision_function(X) point_anomaly_scores = reverse_windowing( window_anomaly_scores, self.window_size, np.nanmean, self.stride, padding ) diff --git a/aeon/anomaly_detection/_stomp.py b/aeon/anomaly_detection/_stomp.py index ddccf38b0f..8cf441ff46 100644 --- a/aeon/anomaly_detection/_stomp.py +++ b/aeon/anomaly_detection/_stomp.py @@ -82,7 +82,6 @@ def __init__( p: float = 2.0, k: int = 1, ): - self.mp: np.ndarray | None = None self.window_size = window_size self.ignore_trivial = ignore_trivial self.normalize = normalize @@ -96,7 +95,7 @@ def _predict(self, X: np.ndarray) -> np.ndarray: import stumpy self._check_params(X) - self.mp = stumpy.stump( + mp = stumpy.stump( X[:, 0], m=self.window_size, ignore_trivial=self.ignore_trivial, @@ -104,7 +103,7 @@ def _predict(self, X: np.ndarray) -> np.ndarray: p=self.p, k=self.k, ) - point_anomaly_scores = reverse_windowing(self.mp[:, 0], self.window_size) + point_anomaly_scores = reverse_windowing(mp[:, 0], self.window_size) return point_anomaly_scores def _check_params(self, X: np.ndarray) -> None: diff --git a/aeon/testing/testing_config.py b/aeon/testing/testing_config.py index d46e299cb9..bbeb5dc505 100644 --- a/aeon/testing/testing_config.py +++ b/aeon/testing/testing_config.py @@ -58,11 +58,8 @@ "RSAST": ["check_fit_deterministic"], "AEFCNClusterer": ["check_fit_updates_state"], "AEResNetClusterer": ["check_fit_updates_state"], - "PyODAdapter": ["check_fit_updates_state"], "SFA": ["check_persistence_via_pickle", "check_fit_deterministic"], # missed in legacy testing, changes state in predict/transform - "DWT_MLEAD": ["check_non_state_changing_method"], - "STOMP": ["check_non_state_changing_method"], "FLUSSSegmenter": ["check_non_state_changing_method"], "InformationGainSegmenter": ["check_non_state_changing_method"], "GreedyGaussianSegmenter": ["check_non_state_changing_method"], @@ -84,9 +81,8 @@ # methods that should not change the state of the estimator, that is, they should # not change fitted parameters or hyper-parameters. They are also the methods that # "apply" the fitted estimator to data and useful for checking results. -# NON_STATE_CHANGING_METHODS_ARRAYLIK = -# non-state-changing methods that return an array-like output +# non-state-changing methods that return an array-like output NON_STATE_CHANGING_METHODS_ARRAYLIKE = ( "predict", "predict_var",