PySDKit 0.4.24__tar.gz → 0.4.25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. {pysdkit-0.4.24 → pysdkit-0.4.25}/PKG-INFO +1 -1
  2. {pysdkit-0.4.24 → pysdkit-0.4.25}/PySDKit.egg-info/PKG-INFO +1 -1
  3. {pysdkit-0.4.24 → pysdkit-0.4.25}/PySDKit.egg-info/SOURCES.txt +1 -0
  4. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/__init__.py +1 -1
  5. pysdkit-0.4.25/pysdkit/_itd/itd.py +187 -0
  6. pysdkit-0.4.25/pysdkit/tests/test_itd.py +116 -0
  7. {pysdkit-0.4.24 → pysdkit-0.4.25}/setup.py +1 -1
  8. pysdkit-0.4.24/pysdkit/_itd/itd.py +0 -166
  9. {pysdkit-0.4.24 → pysdkit-0.4.25}/LICENSE +0 -0
  10. {pysdkit-0.4.24 → pysdkit-0.4.25}/PySDKit.egg-info/dependency_links.txt +0 -0
  11. {pysdkit-0.4.24 → pysdkit-0.4.25}/PySDKit.egg-info/requires.txt +0 -0
  12. {pysdkit-0.4.24 → pysdkit-0.4.25}/PySDKit.egg-info/top_level.txt +0 -0
  13. {pysdkit-0.4.24 → pysdkit-0.4.25}/README.md +0 -0
  14. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/__init__.py +0 -0
  15. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/_find_extrema.py +0 -0
  16. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/_prepare_points.py +0 -0
  17. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/_splines.py +0 -0
  18. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/ceemdan.py +0 -0
  19. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/eemd.py +0 -0
  20. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/efd.py +0 -0
  21. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/emd.py +0 -0
  22. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/hht/__init__.py +0 -0
  23. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/hht/frequency.py +0 -0
  24. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/hht/hht.py +0 -0
  25. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/memd.py +0 -0
  26. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/remd.py +0 -0
  27. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd/tvf_emd.py +0 -0
  28. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd2d/__init__.py +0 -0
  29. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd2d/bemd.py +0 -0
  30. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd2d/bmemd.py +0 -0
  31. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_emd2d/emd2d.py +0 -0
  32. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_esmd/__init__.py +0 -0
  33. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_esmd/esmd.py +0 -0
  34. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ewt/__init__.py +0 -0
  35. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ewt/ewt.py +0 -0
  36. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ewt/ewt2d.py +0 -0
  37. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/__init__.py +0 -0
  38. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/extrema.py +0 -0
  39. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/faemd.py +0 -0
  40. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/faemd2d.py +0 -0
  41. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/faemd3d.py +0 -0
  42. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_faemd/filter.py +0 -0
  43. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_fmd/__init__.py +0 -0
  44. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_fmd/fmd.py +0 -0
  45. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_fmd/nfmd.py +0 -0
  46. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_hvd/__init__.py +0 -0
  47. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_hvd/hvd.py +0 -0
  48. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ifd/__init__.py +0 -0
  49. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_itd/__init__.py +0 -0
  50. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_jmd/__init__.py +0 -0
  51. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_jmd/jmd.py +0 -0
  52. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_jmd/mjmd.py +0 -0
  53. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_lmd/__init__.py +0 -0
  54. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_lmd/lmd.py +0 -0
  55. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_lmd/rlmd.py +0 -0
  56. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_osd/__init__.py +0 -0
  57. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ssa/__init__.py +0 -0
  58. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_ssa/ssa.py +0 -0
  59. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/__init__.py +0 -0
  60. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/acmd.py +0 -0
  61. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/avmd.py +0 -0
  62. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/ba_acmd.py +0 -0
  63. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/base.py +0 -0
  64. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/mvmd.py +0 -0
  65. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/svmd.py +0 -0
  66. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/vmd_c.py +0 -0
  67. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/vmd_f.py +0 -0
  68. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd/vme.py +0 -0
  69. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd2d/__init__.py +0 -0
  70. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd2d/cvmd2d.py +0 -0
  71. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vmd2d/vmd2d.py +0 -0
  72. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vncmd/__init__.py +0 -0
  73. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vncmd/avncmd.py +0 -0
  74. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vncmd/incmd.py +0 -0
  75. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vncmd/mncmd.py +0 -0
  76. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/_vncmd/vncmd.py +0 -0
  77. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/__init__.py +0 -0
  78. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_add_noise.py +0 -0
  79. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_cube.py +0 -0
  80. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_generator.py +0 -0
  81. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_image.py +0 -0
  82. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_models.py +0 -0
  83. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_test_univariate.py +0 -0
  84. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/_time_series.py +0 -0
  85. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/data/texture.txt +0 -0
  86. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/entropy/__init__.py +0 -0
  87. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
  88. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/entropy/_permutation_entropy.py +0 -0
  89. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/entropy/_sample_entropy.py +0 -0
  90. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/models/__init__.py +0 -0
  91. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/models/_base.py +0 -0
  92. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/models/_kmeans.py +0 -0
  93. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/models/_knn.py +0 -0
  94. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/models/_pca.py +0 -0
  95. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/__init__.py +0 -0
  96. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/_fourier_spectra.py +0 -0
  97. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/_functions.py +0 -0
  98. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/_plot_images.py +0 -0
  99. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/_plot_imfs.py +0 -0
  100. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/plot/_plot_signal.py +0 -0
  101. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/__init__.py +0 -0
  102. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/data/__init__.py +0 -0
  103. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/data/test_generator_cube.py +0 -0
  104. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/data/test_generator_image.py +0 -0
  105. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/data/test_generator_signal.py +0 -0
  106. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
  107. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/models/__init__.py +0 -0
  108. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/models/test_knn.py +0 -0
  109. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/models/test_pca.py +0 -0
  110. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_all.py +0 -0
  111. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_ceemdan.py +0 -0
  112. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_emd.py +0 -0
  113. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_ewt.py +0 -0
  114. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_faemd.py +0 -0
  115. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_faemd2d.py +0 -0
  116. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_faemd3d.py +0 -0
  117. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_lmd.py +0 -0
  118. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_moving_decomp.py +0 -0
  119. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_rlmd.py +0 -0
  120. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_stl.py +0 -0
  121. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_vmd.py +0 -0
  122. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tests/test_vmd2d.py +0 -0
  123. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tsa/__init__.py +0 -0
  124. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tsa/_dtw.py +0 -0
  125. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tsa/_moving_decomp.py +0 -0
  126. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tsa/_mstl.py +0 -0
  127. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/tsa/_stl.py +0 -0
  128. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/__init__.py +0 -0
  129. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_cite.py +0 -0
  130. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_correlation.py +0 -0
  131. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_diagnalization.py +0 -0
  132. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_differ.py +0 -0
  133. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_fft.py +0 -0
  134. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_function.py +0 -0
  135. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_hilbert.py +0 -0
  136. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_instantaneous.py +0 -0
  137. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_kernel_matrix.py +0 -0
  138. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_mirror.py +0 -0
  139. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_process.py +0 -0
  140. {pysdkit-0.4.24 → pysdkit-0.4.25}/pysdkit/utils/_smooth1d.py +0 -0
  141. {pysdkit-0.4.24 → pysdkit-0.4.25}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.24
3
+ Version: 0.4.25
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Home-page: https://github.com/wwhenxuan/PySDKit
6
6
  Author: whenxuan, changewam, josefinez, Yuan Feng, Wentong Zhao, JacktheFowler, Deeksha Manjunath
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.24
3
+ Version: 0.4.25
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Home-page: https://github.com/wwhenxuan/PySDKit
6
6
  Author: whenxuan, changewam, josefinez, Yuan Feng, Wentong Zhao, JacktheFowler, Deeksha Manjunath
@@ -103,6 +103,7 @@ pysdkit/tests/test_ewt.py
103
103
  pysdkit/tests/test_faemd.py
104
104
  pysdkit/tests/test_faemd2d.py
105
105
  pysdkit/tests/test_faemd3d.py
106
+ pysdkit/tests/test_itd.py
106
107
  pysdkit/tests/test_lmd.py
107
108
  pysdkit/tests/test_moving_decomp.py
108
109
  pysdkit/tests/test_rlmd.py
@@ -2,7 +2,7 @@
2
2
  A Python library for signal decomposition algorithms.
3
3
  """
4
4
 
5
- __version__ = "0.4.24"
5
+ __version__ = "0.4.25"
6
6
 
7
7
  # Empirical Mode Decomposition
8
8
  from ._emd import EMD
@@ -0,0 +1,187 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on 2025/01/12 12:24:54
4
+ @author: Whenxuan Wang
5
+ @email: wwhenxuan@gmail.com
6
+ """
7
+ import numpy as np
8
+
9
+ from scipy.signal import find_peaks
10
+ from scipy.interpolate import interp1d
11
+
12
+
13
+ class ITD(object):
14
+ """
15
+ ITD: Intrinsic Time-Scale Decomposition
16
+
17
+ H=_itd(x); returns the returns proper rotation components(PRC) and residual signal corresponding to the ITD of X
18
+ Frei, M. G., & Osorio, I. (2007, February).
19
+ Intrinsic time-scale decomposition: time-frequency-energy analysis and real-time filtering of non-stationary signals.
20
+ In Proceedings of the Royal Society of London A: Mathematical, Physical and Engineering Sciences
21
+ (Vol. 463, No. 2078, pp. 321-342). The Royal Society.
22
+ MATLAB Link: https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd
23
+ """
24
+
25
+ def __init__(self, N_max: int = 10):
26
+ self.N_max = N_max
27
+
28
+ def __call__(self, signal: np.ndarray) -> np.ndarray:
29
+ """allow instances to be called like functions"""
30
+ return self.fit_transform(signal=signal)
31
+
32
+ def __str__(self) -> str:
33
+ """Get the full name and abbreviation of the algorithm"""
34
+ return "Intrinsic Time-Scale Decomposition (ITD)"
35
+
36
+ def stop_iter(self, x, counter, E_x):
37
+ """Determine whether the ITD iteration should stop.
38
+
39
+ :param x: current residual signal
40
+ :param counter: number of iterations completed
41
+ :param E_x: energy of the original input signal
42
+ :return: True if decomposition should stop
43
+ """
44
+ if counter > self.N_max:
45
+ return True
46
+
47
+ Exx = np.sum(x**2)
48
+ if Exx <= 0.01 * E_x:
49
+ return True
50
+
51
+ pks1, _ = find_peaks(x)
52
+ pks2, _ = find_peaks(-x)
53
+ pks = np.union1d(pks1, pks2)
54
+
55
+ if len(pks) <= 7:
56
+ return True
57
+
58
+ return False
59
+
60
+ @staticmethod
61
+ def itd_baseline_extract(x):
62
+ """
63
+ Calculate the baseline L of the signal x using the ITD algorithm.
64
+
65
+ The baseline is constructed by computing control points (LK) at each
66
+ extremum as a weighted average (alpha=0.5) of the signal value and the
67
+ interpolated opposite envelope, then piecewise-linearly interpolating
68
+ between consecutive extrema using signal-value-based slopes.
69
+
70
+ :param x: input 1D signal array
71
+ :return: tuple (L, H) where L is the baseline and H = x - L is the
72
+ proper rotation component
73
+ """
74
+ length = len(x)
75
+ t = np.arange(length)
76
+ alpha = 0.5
77
+
78
+ idx_max, _ = find_peaks(x)
79
+ val_max = x[idx_max]
80
+
81
+ idx_min, _ = find_peaks(-x)
82
+ val_min = x[idx_min]
83
+
84
+ if len(idx_max) == 0 or len(idx_min) == 0:
85
+ return x.copy(), np.zeros_like(x)
86
+
87
+ idx_cb = np.union1d(idx_max, idx_min)
88
+
89
+ # Pad boundary so that idx_max and idx_min span the same index range.
90
+ # When the first (or last) extremum on one side has no counterpart,
91
+ # mirror the opposite extremum's position with the signal value there.
92
+ if idx_max[0] < idx_min[0]:
93
+ idx_min = np.concatenate(([idx_max[0]], idx_min))
94
+ val_min = np.concatenate(([x[idx_max[0]]], val_min))
95
+ elif idx_min[0] < idx_max[0]:
96
+ idx_max = np.concatenate(([idx_min[0]], idx_max))
97
+ val_max = np.concatenate(([x[idx_min[0]]], val_max))
98
+
99
+ if idx_max[-1] > idx_min[-1]:
100
+ idx_min = np.concatenate((idx_min, [idx_max[-1]]))
101
+ val_min = np.concatenate((val_min, [x[idx_max[-1]]]))
102
+ elif idx_min[-1] > idx_max[-1]:
103
+ idx_max = np.concatenate((idx_max, [idx_min[-1]]))
104
+ val_max = np.concatenate((val_max, [x[idx_min[-1]]]))
105
+
106
+ Max_line_interp = interp1d(
107
+ idx_max, val_max, kind="linear", fill_value="extrapolate"
108
+ )
109
+ Min_line_interp = interp1d(
110
+ idx_min, val_min, kind="linear", fill_value="extrapolate"
111
+ )
112
+
113
+ Max_line = Max_line_interp(t)
114
+ Min_line = Min_line_interp(t)
115
+
116
+ # LK control points: weighted average at each extremum
117
+ LK1_vals = alpha * Max_line[idx_min] + (1 - alpha) * val_min
118
+ LK2_vals = alpha * Min_line[idx_max] + (1 - alpha) * val_max
119
+
120
+ # Build (position, value) pairs and merge
121
+ LK1_pts = np.column_stack((idx_min, LK1_vals))
122
+ LK2_pts = np.column_stack((idx_max, LK2_vals))
123
+ LK = np.vstack((LK1_pts, LK2_pts))
124
+
125
+ # Sort by position and remove duplicates at the boundaries
126
+ order = np.argsort(LK[:, 0])
127
+ LK = LK[order]
128
+
129
+ # Remove the padded boundary duplicates (first and last are mirrors)
130
+ if len(LK) > 2:
131
+ LK = LK[1:-1]
132
+
133
+ # Clamp to signal boundaries
134
+ LK = np.vstack((
135
+ [0, LK[0, 1]],
136
+ LK,
137
+ [length - 1, LK[-1, 1]],
138
+ ))
139
+
140
+ # Compute baseline by piecewise-linear interpolation between extrema
141
+ idx_Xk = np.concatenate(([0], idx_cb, [length - 1])).astype(int)
142
+
143
+ L = np.zeros(length)
144
+ for i in range(len(idx_Xk) - 1):
145
+ denom = x[idx_Xk[i + 1]] - x[idx_Xk[i]]
146
+ kij = (LK[i + 1, 1] - LK[i, 1]) / denom if denom != 0 else 0.0
147
+ for j in range(idx_Xk[i], idx_Xk[i + 1]):
148
+ L[j] = LK[i, 1] + kij * (x[j] - x[idx_Xk[i]])
149
+
150
+ L[length - 1] = LK[-1, 1]
151
+
152
+ H = x - L
153
+ return L, H
154
+
155
+ def fit_transform(self, signal: np.ndarray) -> np.ndarray:
156
+ """Input x is a 1D numpy signal and return the decomposition results."""
157
+ H = []
158
+ E_x = np.sum(signal**2)
159
+ counter = 0
160
+
161
+ while True:
162
+ counter = counter + 1
163
+ L1, H1 = self.itd_baseline_extract(signal)
164
+ H.append(H1)
165
+ STOP = self.stop_iter(signal, counter, E_x)
166
+ if STOP is True:
167
+ H.append(L1)
168
+ break
169
+ signal = L1
170
+
171
+ H = np.vstack(H)
172
+
173
+ return H
174
+
175
+
176
+ if __name__ == "__main__":
177
+ from pysdkit.data import test_univariate_signal
178
+ from pysdkit.plot import plot_IMFs
179
+
180
+ time, signal = test_univariate_signal()
181
+
182
+ print(signal.shape)
183
+
184
+ itd = ITD()
185
+ imfs = itd.fit_transform(signal=signal)
186
+
187
+ plot_IMFs(signal, IMFs=imfs)
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Tests for the Intrinsic Time-Scale Decomposition (ITD) algorithm.
4
+ """
5
+ import unittest
6
+ import numpy as np
7
+
8
+ from pysdkit import ITD
9
+ from pysdkit.data import test_emd, test_univariate_signal
10
+
11
+
12
+ class ITDTest(unittest.TestCase):
13
+ """Test that ITD runs correctly on various signal types."""
14
+
15
+ def test_fit_transform(self) -> None:
16
+ """Verify signal decomposition on built-in test signals."""
17
+ itd = ITD(N_max=5)
18
+ for index in range(1, 4):
19
+ time, signal = test_univariate_signal(case=index)
20
+ IMFs = itd.fit_transform(signal)
21
+ dim = len(IMFs.shape)
22
+ self.assertEqual(
23
+ first=dim,
24
+ second=2,
25
+ msg="The output shape of the decomposed signal is wrong",
26
+ )
27
+ _, length = IMFs.shape
28
+ self.assertEqual(
29
+ first=len(signal),
30
+ second=length,
31
+ msg="Wrong length of decomposed signal",
32
+ )
33
+
34
+ def test_default_call(self) -> None:
35
+ """Verify the __call__ interface works."""
36
+ time, signal = test_emd()
37
+ itd = ITD(N_max=5)
38
+ IMFs = itd(signal)
39
+
40
+ dim = len(IMFs.shape)
41
+ self.assertEqual(
42
+ first=dim,
43
+ second=2,
44
+ msg="The output shape of the decomposed signal is wrong",
45
+ )
46
+ _, length = IMFs.shape
47
+ self.assertEqual(
48
+ first=len(signal), second=length, msg="Wrong length of decomposed signal"
49
+ )
50
+
51
+ def test_reconstruction(self) -> None:
52
+ """Verify that IMFs sum back to the original signal."""
53
+ time, signal = test_univariate_signal(case=1)
54
+ itd = ITD(N_max=5)
55
+ IMFs = itd.fit_transform(signal)
56
+
57
+ reconstructed = np.sum(IMFs, axis=0)
58
+ self.assertTrue(
59
+ np.allclose(signal, reconstructed, atol=1e-10),
60
+ "IMFs should reconstruct the original signal",
61
+ )
62
+
63
+ def test_near_monotonic_signal(self) -> None:
64
+ """Verify ITD handles near-monotonic signals without crashing (issue #29)."""
65
+ np.random.seed(42)
66
+ N = 200
67
+ t = np.linspace(0, 1, N)
68
+ signal = -1100 * t + 0.01 * np.sin(50 * t) + 0.005 * np.random.randn(N)
69
+
70
+ itd = ITD(N_max=3)
71
+ IMFs = itd.fit_transform(signal)
72
+
73
+ self.assertTrue(IMFs.shape[0] >= 1, "Should produce at least one component")
74
+ self.assertEqual(IMFs.shape[1], N, "Output length should match input")
75
+ reconstructed = np.sum(IMFs, axis=0)
76
+ self.assertTrue(
77
+ np.allclose(signal, reconstructed, atol=1e-10),
78
+ "IMFs should reconstruct the original signal",
79
+ )
80
+
81
+ def test_pure_trend(self) -> None:
82
+ """Verify ITD handles a pure trend (no oscillation)."""
83
+ signal = np.linspace(0, 10, 100)
84
+ itd = ITD(N_max=3)
85
+ IMFs = itd.fit_transform(signal)
86
+
87
+ self.assertTrue(IMFs.shape[0] >= 1, "Should produce at least one component")
88
+ reconstructed = np.sum(IMFs, axis=0)
89
+ self.assertTrue(
90
+ np.allclose(signal, reconstructed, atol=1e-10),
91
+ "IMFs should reconstruct the original signal",
92
+ )
93
+
94
+ def test_asymmetric_extrema(self) -> None:
95
+ """Verify ITD works when maxima and minima counts differ."""
96
+ t = np.linspace(0, 4 * np.pi, 500)
97
+ signal = np.sin(t) + 0.5 * np.sin(3 * t) + 0.3 * np.cos(0.5 * t)
98
+
99
+ itd = ITD(N_max=5)
100
+ IMFs = itd.fit_transform(signal)
101
+
102
+ self.assertTrue(IMFs.shape[0] >= 2, "Should produce multiple components")
103
+ reconstructed = np.sum(IMFs, axis=0)
104
+ self.assertTrue(
105
+ np.allclose(signal, reconstructed, atol=1e-10),
106
+ "IMFs should reconstruct the original signal",
107
+ )
108
+
109
+ def test_str(self) -> None:
110
+ """Verify string representation."""
111
+ itd = ITD()
112
+ self.assertIn("ITD", str(itd))
113
+
114
+
115
+ if __name__ == "__main__":
116
+ unittest.main()
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
  setuptools.setup(
7
7
  name="PySDKit",
8
8
  packages=setuptools.find_packages(),
9
- version="0.4.24",
9
+ version="0.4.25",
10
10
  description="A Python library for signal decomposition algorithms with a unified interface.",
11
11
  url="https://github.com/wwhenxuan/PySDKit",
12
12
  author="whenxuan, changewam, josefinez, Yuan Feng, Wentong Zhao, JacktheFowler, Deeksha Manjunath",
@@ -1,166 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Created on 2025/01/12 12:24:54
4
- @author: Whenxuan Wang
5
- @email: wwhenxuan@gmail.com
6
- not done!!!
7
- """
8
- import numpy as np
9
-
10
- from scipy.signal import find_peaks
11
- from scipy.interpolate import interp1d
12
-
13
-
14
- class ITD(object):
15
- """
16
- ITD: Intrinsic Time-Scale Decomposition
17
-
18
- H=_itd(x); returns the returns proper rotation components(PRC) and residual signal corresponding to the ITD of X
19
- Frei, M. G., & Osorio, I. (2007, February).
20
- Intrinsic time-scale decomposition: time-frequency-energy analysis and real-time filtering of non-stationary signals.
21
- In Proceedings of the Royal Society of London A: Mathematical, Physical and Engineering Sciences
22
- (Vol. 463, No. 2078, pp. 321-342). The Royal Society.
23
- MATLAB Link: https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd
24
- """
25
-
26
- def __init__(self, N_max: int = 10):
27
- self.N_max = N_max
28
-
29
- def __call__(self, signal: np.ndarray) -> np.ndarray:
30
- """allow instances to be called like functions"""
31
- return self.fit_transform(signal=signal)
32
-
33
- def __str__(self) -> str:
34
- """Get the full name and abbreviation of the algorithm"""
35
- return "Intrinsic Time-Scale Decomposition (ITD)"
36
-
37
- def stop_iter(self, x, counter, E_x):
38
- """Stop the main iteration loop"""
39
- if counter > self.N_max:
40
- return True
41
-
42
- Exx = np.sum(x**2)
43
- if Exx <= 0.01 * E_x:
44
- return True
45
-
46
- # 获取输入信号的峰值
47
- pks1, _ = find_peaks(x) # 极大值位置
48
- pks2, _ = find_peaks(-x) # 极小值位置
49
- pks = np.union1d(pks1, pks2) # 所有的极值点
50
-
51
- if len(pks) <= 7:
52
- return True
53
-
54
- # EOF
55
- return False
56
-
57
- @staticmethod
58
- def itd_baseline_extract(x):
59
- """This function is to calculate the baseline L of the signal x."""
60
- length = len(x)
61
- t = np.arange(0, length)
62
- alpha = 0.5
63
-
64
- idx_max, _ = find_peaks(x)
65
- val_max = x[idx_max]
66
-
67
- idx_min, _ = find_peaks(-x)
68
- val_min = x[idx_min]
69
-
70
- idx_cb = np.union1d(idx_max, idx_min)
71
-
72
- # Check the boundary conditions
73
-
74
- # Left side
75
- if np.min(idx_max) < np.min(idx_min):
76
- idx_min = np.append(idx_max[0], idx_min)
77
- val_min = np.append(val_min[0], val_min)
78
- elif np.min(idx_max) > np.min(idx_min):
79
- idx_max = np.append(idx_min[0], idx_max)
80
- val_max = np.append(val_max[0], val_max)
81
-
82
- # Right side
83
- if np.max(idx_max) > np.max(idx_min):
84
- idx_min = np.append(idx_min, idx_max[-1])
85
- val_min = np.append(val_min, val_min[-1])
86
- elif np.max(idx_max) < np.max(idx_min):
87
- idx_max = np.append(idx_max, idx_min[-1])
88
- val_max = np.append(val_max, val_max[-1])
89
-
90
- # Compute the LK points
91
- # 创建插值函数对象,使用线性插值
92
- Max_line_interp = interp1d(
93
- idx_max, val_max, kind="linear", fill_value="extrapolate"
94
- )
95
- Min_line_interp = interp1d(
96
- idx_min, val_min, kind="linear", fill_value="extrapolate"
97
- )
98
-
99
- # 计算插值点的值
100
- Max_line = Max_line_interp(t)
101
- Min_line = Min_line_interp(t)
102
-
103
- LK1 = alpha * Max_line[idx_min] + val_min * (1 - alpha)
104
- LK2 = alpha * Min_line[idx_max] + val_max * (1 - alpha)
105
-
106
- LK1 = np.append(idx_min.reshape(-1, 1), LK1.reshape(-1, 1))
107
- LK2 = np.append(idx_max.reshape(-1, 1), LK2.reshape(-1, 1))
108
-
109
- print(LK1.shape, LK2.shape)
110
-
111
- LK = np.vstack((LK1, LK2)).T
112
-
113
- LK_col_2 = np.argsort(LK[:, 0])
114
- LK_sorted = LK[LK_col_2, :]
115
- LK = LK_sorted[1:-1, :]
116
-
117
- LK = np.vstack((np.array([0, LK[0, 1]]), LK, np.array([length - 1, LK[-1, 1]])))
118
-
119
- # Compute the Lt points
120
- idx_Xk = np.hstack((0, idx_cb, length - 1)).reshape([len(idx_cb) + 2])
121
-
122
- L = np.zeros(length)
123
- for i in range(0, len(idx_Xk) - 1):
124
- for j in range(idx_Xk[i], idx_Xk[i + 1]):
125
- kij = (LK[i + 1, 1] - LK[i, 1]) / (
126
- x[idx_Xk[i + 1]] - x[idx_Xk[i]]
127
- ) # compute the slope K
128
- L[j] = LK[i, 1] + kij * (x[j] - x[idx_Xk[i]])
129
-
130
- H = x - L
131
- print(H)
132
- return L, H
133
-
134
- def fit_transform(self, signal: np.ndarray) -> np.ndarray:
135
- """Input x is a 1D numpy signal and return the decomposition results."""
136
- H = []
137
- E_x = np.sum(signal**2)
138
- counter = 0
139
-
140
- while True:
141
- counter = counter + 1
142
- L1, H1 = self.itd_baseline_extract(signal)
143
- H.append(H1)
144
- STOP = self.stop_iter(signal, counter, E_x)
145
- if STOP is True:
146
- H.append(L1)
147
- break
148
- signal = L1
149
-
150
- H = np.vstack(H)
151
-
152
- return H
153
-
154
-
155
- if __name__ == "__main__":
156
- from pysdkit.data import test_univariate_signal
157
- from pysdkit.plot import plot_IMFs
158
-
159
- time, signal = test_univariate_signal()
160
-
161
- print(signal.shape)
162
-
163
- itd = ITD()
164
- imfs = itd.fit_transform(signal=signal)
165
-
166
- plot_IMFs(signal, IMFs=imfs)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes