/ pyod / test / test_ts_kshape.py
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()