test_ts_kshape.py
1 # -*- coding: utf-8 -*- 2 """Tests for KShape.""" 3 4 import os 5 import sys 6 import unittest 7 8 import numpy as np 9 10 # temporary solution for relative imports in case pyod is not installed 11 # if pyod is installed, no need to use the following line 12 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 13 14 from pyod.models.ts_kshape import KShape 15 from pyod.utils.data import generate_ts_data 16 17 18 class TestKShape(unittest.TestCase): 19 def setUp(self): 20 self.X_train, self.X_test, self.y_train, self.y_test = \ 21 generate_ts_data(n_train=300, n_test=200, contamination=0.05, 22 random_state=42) 23 24 def test_fit(self): 25 clf = KShape(n_clusters=3, window_size=20, contamination=0.1) 26 clf.fit(self.X_train) 27 assert len(clf.decision_scores_) == 300 28 29 def test_decision_function(self): 30 clf = KShape(n_clusters=3, window_size=20) 31 clf.fit(self.X_train) 32 scores = clf.decision_function(self.X_test) 33 assert len(scores) == 200 34 35 def test_multivariate(self): 36 X_multi = generate_ts_data( 37 n_train=300, n_test=200, n_channels=2, contamination=0.05, 38 random_state=42)[0] 39 clf = KShape(n_clusters=3, window_size=20) 40 clf.fit(X_multi) 41 assert len(clf.decision_scores_) == 300 42 43 def test_has_labels_and_threshold(self): 44 clf = KShape(n_clusters=3, window_size=20, contamination=0.1) 45 clf.fit(self.X_train) 46 assert hasattr(clf, 'labels_') 47 assert hasattr(clf, 'threshold_') 48 assert len(clf.labels_) == 300 49 50 def test_scores_nonnegative(self): 51 clf = KShape(n_clusters=3, window_size=20) 52 clf.fit(self.X_train) 53 # SBD is in [0, 2], so scores should be non-negative 54 assert np.all(clf.decision_scores_ >= 0) 55 56 def test_short_series_raises(self): 57 with self.assertRaises(ValueError): 58 clf = KShape(window_size=50) 59 clf.fit(np.random.randn(30)) 60 61 def test_kshape_centroid_correctness(self): 62 """Identical members should produce a centroid with near-zero SBD.""" 63 from pyod.models.ts_kshape import _znormalize, _sbd, _compute_centroid 64 x = _znormalize(np.sin(np.arange(20, dtype=np.float64))) 65 members = np.vstack([x, x, x]) 66 centroid = _compute_centroid(members) 67 dist, _ = _sbd(x, centroid) 68 assert dist < 0.1, f"Centroid SBD too high: {dist}" 69 70 def test_kshape_too_few_subsequences_raises(self): 71 with self.assertRaises(ValueError): 72 clf = KShape(n_clusters=10, window_size=20) 73 clf.fit(np.random.randn(25)) 74 75 76 if __name__ == '__main__': 77 unittest.main()