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.
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PKG-INFO +2 -2
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PySDKit.egg-info/PKG-INFO +2 -2
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PySDKit.egg-info/SOURCES.txt +3 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/README.md +1 -1
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/__init__.py +16 -2
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/__init__.py +2 -0
- pysdkit-0.4.36/pysdkit/_emd/semd.py +338 -0
- pysdkit-0.4.36/pysdkit/_faemd/faemd.py +204 -0
- pysdkit-0.4.36/pysdkit/_faemd/faemd2d.py +155 -0
- pysdkit-0.4.36/pysdkit/_faemd/faemd3d.py +152 -0
- pysdkit-0.4.36/pysdkit/_faemd/filter.py +326 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_faemd.py +15 -0
- pysdkit-0.4.36/pysdkit/tests/test_faemd2d.py +68 -0
- pysdkit-0.4.36/pysdkit/tests/test_faemd3d.py +60 -0
- pysdkit-0.4.36/pysdkit/tests/test_mstl.py +160 -0
- pysdkit-0.4.36/pysdkit/tests/test_semd.py +143 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_stl.py +64 -57
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tsa/__init__.py +10 -1
- pysdkit-0.4.36/pysdkit/tsa/_mstl.py +286 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tsa/_stl.py +141 -94
- pysdkit-0.4.34/pysdkit/_faemd/faemd.py +0 -461
- pysdkit-0.4.34/pysdkit/_faemd/faemd2d.py +0 -36
- pysdkit-0.4.34/pysdkit/_faemd/faemd3d.py +0 -33
- pysdkit-0.4.34/pysdkit/_faemd/filter.py +0 -46
- pysdkit-0.4.34/pysdkit/tests/test_faemd2d.py +0 -11
- pysdkit-0.4.34/pysdkit/tests/test_faemd3d.py +0 -11
- pysdkit-0.4.34/pysdkit/tsa/_mstl.py +0 -25
- {pysdkit-0.4.34 → pysdkit-0.4.36}/LICENSE +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PySDKit.egg-info/dependency_links.txt +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PySDKit.egg-info/requires.txt +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/PySDKit.egg-info/top_level.txt +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pyproject.toml +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_alif/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_alif/alif.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_alif/data/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_alif/data/prefixed_double_filter.npy +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_alif/iterative_filtering.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_apmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_apmd/apmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/_find_extrema.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/_prepare_points.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/_splines.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/ceemdan.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/eemd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/efd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/emd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/hht/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/hht/frequency.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/hht/hht.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/memd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/remd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd/tvf_emd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd2d/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd2d/bemd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd2d/bmemd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_emd2d/emd2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_esmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_esmd/esmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_ewt/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_ewt/ewt.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_ewt/ewt2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_faemd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_faemd/extrema.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_fmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_fmd/fmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_fmd/nfmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_gdmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_gdmd/gdmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_hvd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_hvd/hvd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_itd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_itd/itd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_jmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_jmd/jmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_jmd/mjmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_jmd/sjmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_lmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_lmd/lmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_lmd/rlmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_osd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_osd/swd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_ssa/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_ssa/ssa.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/acmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/avmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/ba_acmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/base.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/mvmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/ovmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/stvmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/svmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/vmd_c.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/vmd_f.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd/vme.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd2d/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd2d/cvmd2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vmd2d/vmd2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vncmd/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vncmd/avncmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vncmd/incmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/_vncmd/vncmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_add_noise.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_cube.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_generator.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_image.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_models.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_test_univariate.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/_time_series.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/data/texture.txt +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/entropy/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/entropy/_permutation_entropy.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/entropy/_sample_entropy.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/models/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/models/_base.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/models/_kmeans.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/models/_knn.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/models/_pca.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/_fourier_spectra.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/_functions.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/_plot_images.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/_plot_imfs.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/plot/_plot_signal.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/data/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/data/test_generator_cube.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/data/test_generator_image.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/data/test_generator_signal.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/models/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/models/test_knn.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/models/test_pca.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/run_all.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_alif.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_apmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_avncmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_ceemdan.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_emd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_ewt.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_ewt2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_gdmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_itd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_jmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_lmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_moving_decomp.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_ovmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_rlmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_sjmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_ssa.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_stvmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_svmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_swd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_vmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_vmd2d.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tests/test_vncmd.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tsa/_dtw.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/tsa/_moving_decomp.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/__init__.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_cite.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_correlation.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_diagnalization.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_differ.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_fft.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_function.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_hilbert.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_instantaneous.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_kernel_matrix.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_mirror.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_process.py +0 -0
- {pysdkit-0.4.34 → pysdkit-0.4.36}/pysdkit/utils/_smooth1d.py +0 -0
- {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.
|
|
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://
|
|
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.
|
|
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://
|
|
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://
|
|
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.
|
|
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",
|
|
@@ -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")
|