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