PySDKit 0.4.35__tar.gz → 0.4.37__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 (177) hide show
  1. {pysdkit-0.4.35 → pysdkit-0.4.37}/PKG-INFO +3 -3
  2. {pysdkit-0.4.35 → pysdkit-0.4.37}/PySDKit.egg-info/PKG-INFO +3 -3
  3. {pysdkit-0.4.35 → pysdkit-0.4.37}/PySDKit.egg-info/SOURCES.txt +4 -0
  4. {pysdkit-0.4.35 → pysdkit-0.4.37}/README.md +2 -2
  5. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/__init__.py +16 -2
  6. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/__init__.py +2 -0
  7. pysdkit-0.4.37/pysdkit/_emd/semd.py +344 -0
  8. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd2d/__init__.py +2 -0
  9. pysdkit-0.4.37/pysdkit/_emd2d/bmemd.py +413 -0
  10. pysdkit-0.4.37/pysdkit/_faemd/faemd.py +200 -0
  11. pysdkit-0.4.37/pysdkit/_faemd/faemd2d.py +151 -0
  12. pysdkit-0.4.37/pysdkit/_faemd/faemd3d.py +148 -0
  13. pysdkit-0.4.37/pysdkit/_faemd/filter.py +318 -0
  14. pysdkit-0.4.37/pysdkit/tests/test_bmemd.py +102 -0
  15. pysdkit-0.4.37/pysdkit/tests/test_ceemdan.py +202 -0
  16. pysdkit-0.4.37/pysdkit/tests/test_eemd.py +175 -0
  17. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_faemd.py +14 -0
  18. pysdkit-0.4.37/pysdkit/tests/test_faemd2d.py +68 -0
  19. pysdkit-0.4.37/pysdkit/tests/test_faemd3d.py +60 -0
  20. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_mstl.py +8 -4
  21. pysdkit-0.4.37/pysdkit/tests/test_semd.py +143 -0
  22. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tsa/_mstl.py +3 -1
  23. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tsa/_stl.py +11 -4
  24. pysdkit-0.4.35/pysdkit/_emd2d/bmemd.py +0 -24
  25. pysdkit-0.4.35/pysdkit/_faemd/faemd.py +0 -461
  26. pysdkit-0.4.35/pysdkit/_faemd/faemd2d.py +0 -36
  27. pysdkit-0.4.35/pysdkit/_faemd/faemd3d.py +0 -33
  28. pysdkit-0.4.35/pysdkit/_faemd/filter.py +0 -46
  29. pysdkit-0.4.35/pysdkit/tests/test_ceemdan.py +0 -286
  30. pysdkit-0.4.35/pysdkit/tests/test_faemd2d.py +0 -11
  31. pysdkit-0.4.35/pysdkit/tests/test_faemd3d.py +0 -11
  32. {pysdkit-0.4.35 → pysdkit-0.4.37}/LICENSE +0 -0
  33. {pysdkit-0.4.35 → pysdkit-0.4.37}/PySDKit.egg-info/dependency_links.txt +0 -0
  34. {pysdkit-0.4.35 → pysdkit-0.4.37}/PySDKit.egg-info/requires.txt +0 -0
  35. {pysdkit-0.4.35 → pysdkit-0.4.37}/PySDKit.egg-info/top_level.txt +0 -0
  36. {pysdkit-0.4.35 → pysdkit-0.4.37}/pyproject.toml +0 -0
  37. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_alif/__init__.py +0 -0
  38. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_alif/alif.py +0 -0
  39. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_alif/data/__init__.py +0 -0
  40. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_alif/data/prefixed_double_filter.npy +0 -0
  41. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_alif/iterative_filtering.py +0 -0
  42. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_apmd/__init__.py +0 -0
  43. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_apmd/apmd.py +0 -0
  44. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/_find_extrema.py +0 -0
  45. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/_prepare_points.py +0 -0
  46. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/_splines.py +0 -0
  47. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/ceemdan.py +0 -0
  48. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/eemd.py +0 -0
  49. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/efd.py +0 -0
  50. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/emd.py +0 -0
  51. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/hht/__init__.py +0 -0
  52. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/hht/frequency.py +0 -0
  53. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/hht/hht.py +0 -0
  54. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/memd.py +0 -0
  55. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/remd.py +0 -0
  56. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd/tvf_emd.py +0 -0
  57. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd2d/bemd.py +0 -0
  58. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_emd2d/emd2d.py +0 -0
  59. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_esmd/__init__.py +0 -0
  60. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_esmd/esmd.py +0 -0
  61. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_ewt/__init__.py +0 -0
  62. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_ewt/ewt.py +0 -0
  63. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_ewt/ewt2d.py +0 -0
  64. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_faemd/__init__.py +0 -0
  65. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_faemd/extrema.py +0 -0
  66. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_fmd/__init__.py +0 -0
  67. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_fmd/fmd.py +0 -0
  68. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_fmd/nfmd.py +0 -0
  69. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_gdmd/__init__.py +0 -0
  70. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_gdmd/gdmd.py +0 -0
  71. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_hvd/__init__.py +0 -0
  72. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_hvd/hvd.py +0 -0
  73. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_itd/__init__.py +0 -0
  74. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_itd/itd.py +0 -0
  75. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_jmd/__init__.py +0 -0
  76. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_jmd/jmd.py +0 -0
  77. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_jmd/mjmd.py +0 -0
  78. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_jmd/sjmd.py +0 -0
  79. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_lmd/__init__.py +0 -0
  80. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_lmd/lmd.py +0 -0
  81. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_lmd/rlmd.py +0 -0
  82. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_osd/__init__.py +0 -0
  83. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_osd/swd.py +0 -0
  84. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_ssa/__init__.py +0 -0
  85. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_ssa/ssa.py +0 -0
  86. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/__init__.py +0 -0
  87. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/acmd.py +0 -0
  88. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/avmd.py +0 -0
  89. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/ba_acmd.py +0 -0
  90. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/base.py +0 -0
  91. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/mvmd.py +0 -0
  92. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/ovmd.py +0 -0
  93. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/stvmd.py +0 -0
  94. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/svmd.py +0 -0
  95. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/vmd_c.py +0 -0
  96. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/vmd_f.py +0 -0
  97. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd/vme.py +0 -0
  98. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd2d/__init__.py +0 -0
  99. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd2d/cvmd2d.py +0 -0
  100. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vmd2d/vmd2d.py +0 -0
  101. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vncmd/__init__.py +0 -0
  102. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vncmd/avncmd.py +0 -0
  103. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vncmd/incmd.py +0 -0
  104. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/_vncmd/vncmd.py +0 -0
  105. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/__init__.py +0 -0
  106. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_add_noise.py +0 -0
  107. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_cube.py +0 -0
  108. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_generator.py +0 -0
  109. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_image.py +0 -0
  110. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_models.py +0 -0
  111. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_test_univariate.py +0 -0
  112. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/_time_series.py +0 -0
  113. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/data/texture.txt +0 -0
  114. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/entropy/__init__.py +0 -0
  115. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
  116. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/entropy/_permutation_entropy.py +0 -0
  117. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/entropy/_sample_entropy.py +0 -0
  118. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/models/__init__.py +0 -0
  119. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/models/_base.py +0 -0
  120. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/models/_kmeans.py +0 -0
  121. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/models/_knn.py +0 -0
  122. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/models/_pca.py +0 -0
  123. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/__init__.py +0 -0
  124. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/_fourier_spectra.py +0 -0
  125. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/_functions.py +0 -0
  126. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/_plot_images.py +0 -0
  127. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/_plot_imfs.py +0 -0
  128. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/plot/_plot_signal.py +0 -0
  129. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/__init__.py +0 -0
  130. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/data/__init__.py +0 -0
  131. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/data/test_generator_cube.py +0 -0
  132. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/data/test_generator_image.py +0 -0
  133. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/data/test_generator_signal.py +0 -0
  134. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
  135. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/models/__init__.py +0 -0
  136. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/models/test_knn.py +0 -0
  137. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/models/test_pca.py +0 -0
  138. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/run_all.py +0 -0
  139. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_alif.py +0 -0
  140. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_apmd.py +0 -0
  141. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_avncmd.py +0 -0
  142. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_emd.py +0 -0
  143. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_ewt.py +0 -0
  144. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_ewt2d.py +0 -0
  145. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_gdmd.py +0 -0
  146. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_itd.py +0 -0
  147. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_jmd.py +0 -0
  148. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_lmd.py +0 -0
  149. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_moving_decomp.py +0 -0
  150. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_ovmd.py +0 -0
  151. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_rlmd.py +0 -0
  152. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_sjmd.py +0 -0
  153. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_ssa.py +0 -0
  154. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_stl.py +0 -0
  155. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_stvmd.py +0 -0
  156. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_svmd.py +0 -0
  157. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_swd.py +0 -0
  158. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_vmd.py +0 -0
  159. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_vmd2d.py +0 -0
  160. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tests/test_vncmd.py +0 -0
  161. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tsa/__init__.py +0 -0
  162. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tsa/_dtw.py +0 -0
  163. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/tsa/_moving_decomp.py +0 -0
  164. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/__init__.py +0 -0
  165. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_cite.py +0 -0
  166. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_correlation.py +0 -0
  167. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_diagnalization.py +0 -0
  168. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_differ.py +0 -0
  169. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_fft.py +0 -0
  170. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_function.py +0 -0
  171. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_hilbert.py +0 -0
  172. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_instantaneous.py +0 -0
  173. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_kernel_matrix.py +0 -0
  174. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_mirror.py +0 -0
  175. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_process.py +0 -0
  176. {pysdkit-0.4.35 → pysdkit-0.4.37}/pysdkit/utils/_smooth1d.py +0 -0
  177. {pysdkit-0.4.35 → pysdkit-0.4.37}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.35
3
+ Version: 0.4.37
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
6
6
  Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
@@ -155,8 +155,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
155
155
  | [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
156
156
  | [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
157
157
  | [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
158
- | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
159
- | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
158
+ | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
159
+ | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
160
160
  | [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
161
161
  | [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
162
162
  | [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.35
3
+ Version: 0.4.37
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
6
6
  Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
@@ -155,8 +155,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
155
155
  | [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
156
156
  | [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
157
157
  | [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
158
- | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
159
- | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
158
+ | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
159
+ | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
160
160
  | [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
161
161
  | [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
162
162
  | [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
@@ -24,6 +24,7 @@ pysdkit/_emd/efd.py
24
24
  pysdkit/_emd/emd.py
25
25
  pysdkit/_emd/memd.py
26
26
  pysdkit/_emd/remd.py
27
+ pysdkit/_emd/semd.py
27
28
  pysdkit/_emd/tvf_emd.py
28
29
  pysdkit/_emd/hht/__init__.py
29
30
  pysdkit/_emd/hht/frequency.py
@@ -111,7 +112,9 @@ pysdkit/tests/run_all.py
111
112
  pysdkit/tests/test_alif.py
112
113
  pysdkit/tests/test_apmd.py
113
114
  pysdkit/tests/test_avncmd.py
115
+ pysdkit/tests/test_bmemd.py
114
116
  pysdkit/tests/test_ceemdan.py
117
+ pysdkit/tests/test_eemd.py
115
118
  pysdkit/tests/test_emd.py
116
119
  pysdkit/tests/test_ewt.py
117
120
  pysdkit/tests/test_ewt2d.py
@@ -126,6 +129,7 @@ pysdkit/tests/test_moving_decomp.py
126
129
  pysdkit/tests/test_mstl.py
127
130
  pysdkit/tests/test_ovmd.py
128
131
  pysdkit/tests/test_rlmd.py
132
+ pysdkit/tests/test_semd.py
129
133
  pysdkit/tests/test_sjmd.py
130
134
  pysdkit/tests/test_ssa.py
131
135
  pysdkit/tests/test_stl.py
@@ -101,8 +101,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
101
101
  | [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
102
102
  | [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
103
103
  | [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
104
- | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
105
- | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✖️ |
104
+ | [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
105
+ | [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
106
106
  | [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
107
107
  | [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
108
108
  | [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
@@ -2,7 +2,7 @@
2
2
  A Python library for signal decomposition algorithms.
3
3
  """
4
4
 
5
- __version__ = "0.4.35"
5
+ __version__ = "0.4.37"
6
6
 
7
7
  # Empirical Mode Decomposition
8
8
  from ._emd import EMD
@@ -19,6 +19,9 @@ from ._emd import REMD
19
19
  # Multivariate Empirical Mode Decomposition
20
20
  from ._emd import MEMD
21
21
 
22
+ # Serial Empirical Mode Decomposition
23
+ from ._emd import SEMD
24
+
22
25
  # Time Varying Filter based Empirical Mode Decomposition
23
26
  from ._emd import TVF_EMD
24
27
 
@@ -26,11 +29,14 @@ from ._emd import TVF_EMD
26
29
  from ._emd import EFD
27
30
 
28
31
  # Fast and Adaptive Empirical Mode Decomposition
29
- from ._faemd import FAEMD
32
+ from ._faemd import FAEMD, FAEMD2D, FAEMD3D
30
33
 
31
34
  # Empirical Mode Decomposition 2D for images
32
35
  from ._emd2d import EMD2D
33
36
 
37
+ # Bidimensional Multivariate Empirical Mode Decomposition
38
+ from ._emd2d import BMEMD
39
+
34
40
  # Hilbert Vibration Decomposition
35
41
  from ._hvd import HVD
36
42
 
@@ -153,10 +159,14 @@ Ensemble Empirical Mode Decomposition | EEMD
153
159
  Complete Ensemble EMD with Adaptive Noise | CEEMDAN
154
160
  Robust Empirical Mode Decomposition | REMD
155
161
  Multivariate Empirical Mode Decomposition | MEMD
162
+ Serial Empirical Mode Decomposition | SEMD
156
163
  Time Varying Filter based EMD | TVF_EMD
157
164
  Empirical Fourier Decomposition | EFD
158
165
  Fast and Adaptive Empirical Mode Decomposition | FAEMD
166
+ Bidimensional FAEMD | FAEMD2D
167
+ Tridimensional FAEMD | FAEMD3D
159
168
  Empirical Mode Decomposition 2D for images | EMD2D
169
+ Bidimensional Multivariate EMD | BMEMD
160
170
  Hilbert Vibration Decomposition | HVD
161
171
  Intrinsic Time-Scale Decomposition | ITD
162
172
  Local Mean Decomposition | LMD
@@ -198,10 +208,14 @@ __all__ = [
198
208
  "CEEMDAN",
199
209
  "REMD",
200
210
  "MEMD",
211
+ "SEMD",
201
212
  "TVF_EMD",
202
213
  "EFD",
203
214
  "FAEMD",
215
+ "FAEMD2D",
216
+ "FAEMD3D",
204
217
  "EMD2D",
218
+ "BMEMD",
205
219
  "HVD",
206
220
  "ITD",
207
221
  "LMD",
@@ -26,3 +26,5 @@ from .tvf_emd import TVF_EMD
26
26
  from .efd import EFD
27
27
 
28
28
  from .hht import HHT
29
+
30
+ from .semd import SEMD, concatenate_signals, deconcatenate_imfs, transition_bridge
@@ -0,0 +1,344 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on 2026/08/01
4
+ @author: Whenxuan Wang
5
+ @email: wwhenxuan@gmail.com
6
+
7
+ Serial-EMD (SEMD): fast multi-signal EMD via 1-D serialization.
8
+
9
+ Zhang, J., Feng, F., Marti-Puig, P., Caiafa, C. F., Sun, Z., Duan, F.,
10
+ and Solé-Casals, J. (2021).
11
+ Serial-EMD: Fast Empirical Mode Decomposition Method for Multi-dimensional
12
+ Signals Based on Serialization. Information Sciences.
13
+ https://doi.org/10.1016/j.ins.2021.09.033
14
+
15
+ Reference code: https://github.com/ffbear1993/serial-emd
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import Optional, Tuple, Union
21
+
22
+ import numpy as np
23
+
24
+ from .emd import EMD
25
+
26
+
27
+ def concatenate_signals(matrix_x: np.ndarray, num_interval: int) -> np.ndarray:
28
+ """
29
+ Serialize multi-channel signals with smooth transition bridges.
30
+
31
+ Follows Zhang et al. (2021) / the official ``serial-emd`` Python reference.
32
+
33
+ :param matrix_x: Array of shape ``(M, N)`` — ``M`` samples (rows),
34
+ ``N`` channels (columns).
35
+ :param num_interval: Transition length ``D`` (``1 <= D < M``).
36
+ :return: 1-D serialized signal of length ``M*N + D*(N-1)``.
37
+ """
38
+ matrix_x = np.asarray(matrix_x, dtype=float)
39
+ if matrix_x.ndim != 2:
40
+ raise ValueError(
41
+ "matrix_x must be a 2-D array of shape (n_samples, n_channels)"
42
+ )
43
+
44
+ n_length, n_signal = matrix_x.shape
45
+ if n_signal < 1:
46
+ raise ValueError("matrix_x must contain at least one channel")
47
+ if n_signal == 1:
48
+ return matrix_x[:, 0].copy()
49
+
50
+ d = int(num_interval)
51
+ if d < 1 or d >= n_length:
52
+ raise ValueError(
53
+ f"num_interval must satisfy 1 <= D < M; got D={d}, M={n_length}"
54
+ )
55
+
56
+ # Heads of channels 2..N and tails of channels 1..N-1
57
+ matrix_a = matrix_x[:d, 1:]
58
+ matrix_b = matrix_x[-d:, :-1]
59
+
60
+ # Ramp weights a_i = i / (D+1), i = 1..D (endpoints 0 and 1 excluded)
61
+ vector_a = np.linspace(0.0, 1.0, d + 2)[1:-1].reshape(-1, 1)
62
+ vector_u = np.ones((n_signal - 1, 1))
63
+
64
+ # Transition: flip(head_{i+1}) ⊙ a + flip(tail_i) ⊙ flip(a)
65
+ matrix_t_a = np.flipud(matrix_a) * (vector_a @ vector_u.T)
66
+ matrix_t_b = np.flipud(matrix_b) * (np.flipud(vector_a) @ vector_u.T)
67
+ matrix_t = matrix_t_a + matrix_t_b
68
+
69
+ # Append a dummy zero column so Fortran flattening yields the desired layout
70
+ matrix_z = np.zeros((d, 1))
71
+ matrix_t = np.concatenate([matrix_t, matrix_z], axis=1)
72
+
73
+ # Stack transitions under the original block, then column-major vectorize
74
+ matrix_r = np.concatenate([matrix_x, matrix_t], axis=0)
75
+ matrix_r = matrix_r.flatten(order="F")
76
+ return matrix_r[:-d]
77
+
78
+
79
+ def deconcatenate_imfs(
80
+ matrix_r: np.ndarray,
81
+ num_interval: int,
82
+ num_signal: int,
83
+ num_length: Optional[int] = None,
84
+ ) -> np.ndarray:
85
+ """
86
+ Split serialized IMFs back into per-channel IMF tensors.
87
+
88
+ :param matrix_r: Serialized IMFs of shape ``(L, K)`` (or ``(L,)`` for one IMF).
89
+ :param num_interval: Transition length ``D`` used during concatenation.
90
+ :param num_signal: Number of original channels ``N``.
91
+ :param num_length: Original per-channel length ``M``. Inferred from ``L``
92
+ when omitted via ``L = M*N + D*(N-1)``.
93
+ :return: Array of shape ``(M, K, N)``.
94
+ """
95
+ matrix_r = np.asarray(matrix_r, dtype=float)
96
+ if matrix_r.ndim == 1:
97
+ matrix_r = matrix_r.reshape(-1, 1)
98
+ if matrix_r.ndim != 2:
99
+ raise ValueError("matrix_r must have shape (L, K)")
100
+
101
+ d = int(num_interval)
102
+ n_signal = int(num_signal)
103
+ if n_signal < 1:
104
+ raise ValueError("num_signal must be >= 1")
105
+
106
+ length_ser, num_mode = matrix_r.shape
107
+ if n_signal == 1:
108
+ m = length_ser if num_length is None else int(num_length)
109
+ if m != length_ser:
110
+ raise ValueError("num_length does not match serialized length for N=1")
111
+ return matrix_r.reshape(m, num_mode, 1)
112
+
113
+ if d < 1:
114
+ raise ValueError("num_interval must be >= 1")
115
+
116
+ if num_length is None:
117
+ # L = M*N + D*(N-1) ⇒ M = (L - D*(N-1)) / N
118
+ numer = length_ser - d * (n_signal - 1)
119
+ if numer % n_signal != 0:
120
+ raise ValueError(
121
+ "Cannot infer num_length from serialized IMF length; "
122
+ "please pass num_length explicitly"
123
+ )
124
+ m = numer // n_signal
125
+ else:
126
+ m = int(num_length)
127
+
128
+ expected = m * n_signal + d * (n_signal - 1)
129
+ if length_ser != expected:
130
+ raise ValueError(
131
+ f"Serialized length {length_ser} incompatible with "
132
+ f"M={m}, N={n_signal}, D={d} (expected {expected})"
133
+ )
134
+
135
+ # Pad D zeros, reshape Fortran-order to (M+D, N, K), drop transitions
136
+ matrix_z = np.zeros((d, num_mode))
137
+ matrix_pad = np.concatenate([matrix_r, matrix_z], axis=0)
138
+ matrix_imf = matrix_pad.reshape([-1, n_signal, num_mode], order="F")
139
+ matrix_imf = matrix_imf[:-d, :, :]
140
+ return matrix_imf.transpose((0, 2, 1))
141
+
142
+
143
+ def transition_bridge(
144
+ tail: np.ndarray, head: np.ndarray, num_interval: Optional[int] = None
145
+ ) -> np.ndarray:
146
+ """
147
+ Build the linear cross-fade bridge between two adjacent channels.
148
+
149
+ Useful for visualizing how SEMD constructs the transition that keeps the
150
+ mean envelope continuous across channel joins.
151
+
152
+ :param tail: Last ``D`` samples of channel ``i`` (or a longer suffix).
153
+ :param head: First ``D`` samples of channel ``i+1`` (or a longer prefix).
154
+ :param num_interval: Bridge length; defaults to ``min(len(tail), len(head))``.
155
+ :return: Bridge segment of length ``D``.
156
+ """
157
+ tail = np.asarray(tail, dtype=float).ravel()
158
+ head = np.asarray(head, dtype=float).ravel()
159
+ d = int(num_interval) if num_interval is not None else min(len(tail), len(head))
160
+ if d < 1 or d > len(tail) or d > len(head):
161
+ raise ValueError("num_interval exceeds available head/tail length")
162
+
163
+ a = np.linspace(0.0, 1.0, d + 2)[1:-1]
164
+ return np.flipud(head[:d]) * a + np.flipud(tail[-d:]) * np.flipud(a)
165
+
166
+
167
+ class SEMD(object):
168
+ """
169
+ Serial Empirical Mode Decomposition (Serial-EMD / SEMD)
170
+
171
+ Zhang et al., Information Sciences, 2021.
172
+
173
+ SEMD concatenates multi-channel signals into one long 1-D series with
174
+ smooth transition bridges, runs a standard 1-D EMD (or a compatible
175
+ variant), then splits the IMFs back to each original channel. This
176
+ avoids expensive multivariate envelope interpolation (MEMD / BEMD)
177
+ while reusing any existing univariate EMD backend.
178
+
179
+ Input layout (PySDKit convention)
180
+ ---------------------------------
181
+ - univariate: ``(seq_len,)``
182
+ - multivariate: ``(n_channels, seq_len)``
183
+
184
+ Output layout
185
+ -------------
186
+ - univariate: ``(K, seq_len)``
187
+ - multivariate: ``(K, seq_len, n_channels)``
188
+ """
189
+
190
+ def __init__(
191
+ self,
192
+ num_interval: Optional[int] = None,
193
+ interval_ratio: float = 0.2,
194
+ max_imfs: int = -1,
195
+ emd: Optional[EMD] = None,
196
+ **emd_kwargs,
197
+ ) -> None:
198
+ """
199
+ :param num_interval: Transition length ``D``. If ``None``, uses
200
+ ``max(1, round(interval_ratio * seq_len))``.
201
+ :param interval_ratio: Fraction of each channel length used for ``D``
202
+ when ``num_interval`` is not given (paper default ≈ 0.2).
203
+ :param max_imfs: Maximum number of IMFs forwarded to the EMD backend
204
+ (``-1`` means no hard limit).
205
+ :param emd: Optional pre-configured univariate decomposer. Must
206
+ expose ``fit_transform(signal, max_imfs=...)`` returning
207
+ ``(K, L)``. Defaults to :class:`pysdkit._emd.emd.EMD`.
208
+ :param emd_kwargs: Extra keyword arguments used when constructing the
209
+ default :class:`EMD` instance.
210
+ """
211
+ if num_interval is not None and int(num_interval) < 1:
212
+ raise ValueError("num_interval must be a positive integer or None")
213
+ if not (0.0 < float(interval_ratio) <= 1.0):
214
+ raise ValueError("interval_ratio must lie in (0, 1]")
215
+
216
+ self.num_interval = None if num_interval is None else int(num_interval)
217
+ self.interval_ratio = float(interval_ratio)
218
+ self.max_imfs = int(max_imfs)
219
+ self.emd = emd if emd is not None else EMD(max_imfs=self.max_imfs, **emd_kwargs)
220
+
221
+ # Cached intermediates for inspection / plotting
222
+ self.serialized_signal: Optional[np.ndarray] = None
223
+ self.serialized_imfs: Optional[np.ndarray] = None
224
+ self.imfs: Optional[np.ndarray] = None
225
+ self._last_shape: Optional[Tuple[int, int]] = None # (N, M)
226
+ self._last_D: Optional[int] = None
227
+
228
+ def __str__(self) -> str:
229
+ return "Serial Empirical Mode Decomposition (SEMD)"
230
+
231
+ def __call__(
232
+ self, signal: np.ndarray, max_imfs: Optional[int] = None
233
+ ) -> np.ndarray:
234
+ return self.fit_transform(signal=signal, max_imfs=max_imfs)
235
+
236
+ def resolve_interval(self, seq_len: int) -> int:
237
+ """Resolve the transition length ``D`` for a given channel length."""
238
+ if self.num_interval is not None:
239
+ d = self.num_interval
240
+ else:
241
+ d = max(1, int(round(self.interval_ratio * seq_len)))
242
+ if d >= seq_len:
243
+ d = max(1, seq_len - 1)
244
+ return d
245
+
246
+ def serialize(self, signal: np.ndarray) -> Tuple[np.ndarray, int]:
247
+ """
248
+ Convert a PySDKit multivariate array into the serialized 1-D series.
249
+
250
+ :param signal: ``(n_channels, seq_len)`` or ``(seq_len,)``
251
+ :return: ``(serialized_1d, D)``
252
+ """
253
+ x = np.asarray(signal, dtype=float)
254
+ if x.ndim == 1:
255
+ return x.copy(), 0
256
+ if x.ndim != 2:
257
+ raise ValueError(
258
+ "signal must be 1-D (seq_len,) or 2-D (n_channels, seq_len)"
259
+ )
260
+
261
+ n_channels, seq_len = x.shape
262
+ d = self.resolve_interval(seq_len)
263
+ # Paper / reference layout: time in rows, channels in columns
264
+ serialized = concatenate_signals(x.T, d)
265
+ return serialized, d
266
+
267
+ def fit_transform(
268
+ self, signal: np.ndarray, max_imfs: Optional[int] = None
269
+ ) -> np.ndarray:
270
+ """
271
+ Decompose uni-/multi-channel signals with Serial-EMD.
272
+
273
+ :param signal: ``(seq_len,)`` or ``(n_channels, seq_len)``
274
+ :param max_imfs: Optional override for the maximum number of IMFs
275
+ :return: IMFs with shape ``(K, seq_len)`` or ``(K, seq_len, n_channels)``
276
+ """
277
+ x = np.asarray(signal, dtype=float)
278
+ if max_imfs is None:
279
+ max_imfs = self.max_imfs
280
+ # Only forward a hard cap when it is positive; otherwise let the backend decide
281
+ emd_kwargs = (
282
+ {}
283
+ if (max_imfs is None or int(max_imfs) < 0)
284
+ else {"max_imfs": int(max_imfs)}
285
+ )
286
+
287
+ if x.ndim == 1:
288
+ imfs = self.emd.fit_transform(x, **emd_kwargs)
289
+ self.serialized_signal = x.copy()
290
+ self.serialized_imfs = np.asarray(imfs).T # (L, K)
291
+ self.imfs = np.asarray(imfs)
292
+ self._last_shape = (1, x.size)
293
+ self._last_D = 0
294
+ return self.imfs
295
+
296
+ if x.ndim != 2:
297
+ raise ValueError(
298
+ "signal must be 1-D (seq_len,) or 2-D (n_channels, seq_len)"
299
+ )
300
+
301
+ n_channels, seq_len = x.shape
302
+ if n_channels < 1 or seq_len < 2:
303
+ raise ValueError("Invalid multivariate signal shape")
304
+
305
+ d = self.resolve_interval(seq_len)
306
+ serialized = concatenate_signals(x.T, d)
307
+ self.serialized_signal = serialized
308
+ self._last_shape = (n_channels, seq_len)
309
+ self._last_D = d
310
+
311
+ # Univariate EMD on the long series → (K, L)
312
+ ser_imfs = np.asarray(self.emd.fit_transform(serialized, **emd_kwargs))
313
+ if ser_imfs.ndim != 2:
314
+ raise RuntimeError("EMD backend must return a 2-D IMF array (K, L)")
315
+ self.serialized_imfs = ser_imfs.T # (L, K)
316
+
317
+ # (M, K, N)
318
+ imfs_mkn = deconcatenate_imfs(
319
+ self.serialized_imfs,
320
+ num_interval=d,
321
+ num_signal=n_channels,
322
+ num_length=seq_len,
323
+ )
324
+ # PySDKit multivariate layout: (K, seq_len, n_channels)
325
+ self.imfs = np.transpose(imfs_mkn, (1, 0, 2))
326
+ return self.imfs
327
+
328
+ def reconstruct(self, imfs: Optional[np.ndarray] = None) -> np.ndarray:
329
+ """
330
+ Sum IMFs to reconstruct the original channel(s).
331
+
332
+ :param imfs: Optional IMF tensor; defaults to the last ``fit_transform`` result.
333
+ :return: ``(seq_len,)`` or ``(n_channels, seq_len)``
334
+ """
335
+ if imfs is None:
336
+ if self.imfs is None:
337
+ raise RuntimeError("Call fit_transform before reconstruct()")
338
+ imfs = self.imfs
339
+ imfs = np.asarray(imfs)
340
+ if imfs.ndim == 2:
341
+ return np.sum(imfs, axis=0)
342
+ if imfs.ndim == 3:
343
+ return np.sum(imfs, axis=0).T # (N, M)
344
+ raise ValueError("imfs must be 2-D or 3-D")
@@ -6,3 +6,5 @@ Created on 2025/02/03 18:36:49
6
6
  """
7
7
 
8
8
  from .emd2d import EMD2D
9
+
10
+ from .bmemd import BMEMD, local_var_img, fuse_images