Modal-Decomposition 0.1.1__tar.gz → 0.1.3__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.
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/PKG-INFO +2 -26
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/README.md +1 -25
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/pyproject.toml +1 -1
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEFD.py +5 -2
- modal_decomposition-0.1.3/src/Modal_Decomposition/EEMD.py +64 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EFD.py +8 -6
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EMD.py +14 -5
- modal_decomposition-0.1.3/src/Modal_Decomposition/Error/RealizationError.py +2 -0
- modal_decomposition-0.1.3/src/Modal_Decomposition/LMD.py +243 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/MEMD.py +13 -11
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SSA.py +2 -2
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SVMD.py +9 -4
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/__init__.py +32 -19
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/PKG-INFO +2 -26
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/SOURCES.txt +1 -1
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/tests/test.py +2 -5
- modal_decomposition-0.1.1/src/Modal_Decomposition/EEMD.py +0 -130
- modal_decomposition-0.1.1/src/Modal_Decomposition/LMD.py +0 -178
- modal_decomposition-0.1.1/src/Modal_Decomposition/help_function.py +0 -64
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/LICENSE +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/setup.cfg +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMD.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMDAN.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/__init__.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/color_define.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/colorful_print.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EWT.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Error/CycleError.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/FMD.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/ICEEMDAN.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/RPSEMD.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/EnvironmentMemory.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/LazyImport.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/Monotonicity.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/NumpyNdarray_MemoryCalculator.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/OneDimArray.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/__init__.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/VMD.py +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/dependency_links.txt +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/requires.txt +0 -0
- {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Modal-Decomposition
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: a package for modal decomposition
|
|
5
5
|
Author-email: Mao_HaoChuan <2215269365@qq.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -55,7 +55,7 @@ All entrance of functions or class are stored in `Modal_Decomposition/__init__.p
|
|
|
55
55
|
| EFD | Empirical Fourier Decomposition | `Function.EFD(signal)` | [10.1016/j.ymssp.2021.108155](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355?via%3Dihub) |
|
|
56
56
|
| EMD | Empirical Mode Decomposition | `Function.EMD(signal)` | [10.1098/rspa.1998.0193](https://www.semanticscholar.org/paper/The-empirical-mode-decomposition-and-the-Hilbert-Huang-Shen/3842d81b0375dae8ae92734aa2a5d4aeed7a91d1) |
|
|
57
57
|
| EWT | Empirical Wavelet Transform | `Function.EWT(signal)` | [10.48550/arXiv.2304.06274](https://arxiv.org/abs/2304.06274) |
|
|
58
|
-
| FMD | Filtered Mode Decomposition | `Function.
|
|
58
|
+
| FMD | Filtered Mode Decomposition | `Function.FMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
|
|
59
59
|
| ICEEMDAN | Improved Complete Ensemble Empirical Mode Decomposition with Adaptive Noise | `Function.ICEEMDAN(signal)` | [10.1007/s10470-021-01901-3](https://link.springer.com/article/10.1007/s10470-021-01901-3#citeas) |
|
|
60
60
|
| LMD | Local Mean Decomposition | `Function.LMD(signal)` | [10.1098/rsif.2005.0058](https://royalsocietypublishing.org/doi/10.1098/rsif.2005.0058) |
|
|
61
61
|
| MEMD | Multivariate Empirical Mode Decomposition | `Function.MEMD(signal)` | [10.48550/arXiv.2206.00926](https://arxiv.org/abs/2206.00926) |
|
|
@@ -79,42 +79,18 @@ pip install -r requirements.txt
|
|
|
79
79
|
pip install Modal-Decomposition
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
*Well, some libs should be installed by yourself, don't use `pip install -r requirements.txt`, because `Triton` need installed by GitHub.*
|
|
83
|
-
|
|
84
|
-
*`Triton` please visite this url: https://github.com/woct0rdho/triton-windows/releases*
|
|
85
|
-
|
|
86
82
|
## Dependence
|
|
87
83
|
|
|
88
84
|
This lib's dependence are:
|
|
89
85
|
|
|
90
86
|
***Python: 3.10***
|
|
91
87
|
|
|
92
|
-
- [antropy](https://github.com/raphaelvallat/antropy)
|
|
93
|
-
- [colorama](https://github.com/tartley/colorama)
|
|
94
|
-
- [einops](https://github.com/arogozhnikov/einops)
|
|
95
88
|
- [EMD-signal](https://github.com/laszukdawid/PyEMD)
|
|
96
89
|
- [ewtpy](https://github.com/vrcarva/ewtpy)
|
|
97
|
-
- [numba](https://github.com/numba/numba)
|
|
98
|
-
- [numpy](https://github.com/numpy/numpy)
|
|
99
|
-
- [scipy](https://github.com/scipy/scipy)
|
|
100
90
|
- [vmdpy](https://github.com/vrcarva/vmdpy)
|
|
101
|
-
- [psutil](https://github.com/giampaolo/psutil)
|
|
102
91
|
|
|
103
92
|
*Other dependence please read "requirements.txt"*
|
|
104
93
|
|
|
105
|
-
## Codes Resource
|
|
106
|
-
|
|
107
|
-
All codes from :
|
|
108
|
-
|
|
109
|
-
- Github:
|
|
110
|
-
- EMD-signal -> EMD, CEEFD, CEEMDAN, EEMD
|
|
111
|
-
- ewtpy -> EWT
|
|
112
|
-
- vmdpy -> VMD
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- Myself:
|
|
116
|
-
- CEEMD, EFD, FMD, ICEEMDAN, LMD, MEMD, RPSEMD, SSA, SVMD
|
|
117
|
-
|
|
118
94
|
## Url
|
|
119
95
|
|
|
120
96
|
This lib's url is: https://github.com/a-raining-day/Modal-Decomposition
|
|
@@ -23,7 +23,7 @@ All entrance of functions or class are stored in `Modal_Decomposition/__init__.p
|
|
|
23
23
|
| EFD | Empirical Fourier Decomposition | `Function.EFD(signal)` | [10.1016/j.ymssp.2021.108155](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355?via%3Dihub) |
|
|
24
24
|
| EMD | Empirical Mode Decomposition | `Function.EMD(signal)` | [10.1098/rspa.1998.0193](https://www.semanticscholar.org/paper/The-empirical-mode-decomposition-and-the-Hilbert-Huang-Shen/3842d81b0375dae8ae92734aa2a5d4aeed7a91d1) |
|
|
25
25
|
| EWT | Empirical Wavelet Transform | `Function.EWT(signal)` | [10.48550/arXiv.2304.06274](https://arxiv.org/abs/2304.06274) |
|
|
26
|
-
| FMD | Filtered Mode Decomposition | `Function.
|
|
26
|
+
| FMD | Filtered Mode Decomposition | `Function.FMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
|
|
27
27
|
| ICEEMDAN | Improved Complete Ensemble Empirical Mode Decomposition with Adaptive Noise | `Function.ICEEMDAN(signal)` | [10.1007/s10470-021-01901-3](https://link.springer.com/article/10.1007/s10470-021-01901-3#citeas) |
|
|
28
28
|
| LMD | Local Mean Decomposition | `Function.LMD(signal)` | [10.1098/rsif.2005.0058](https://royalsocietypublishing.org/doi/10.1098/rsif.2005.0058) |
|
|
29
29
|
| MEMD | Multivariate Empirical Mode Decomposition | `Function.MEMD(signal)` | [10.48550/arXiv.2206.00926](https://arxiv.org/abs/2206.00926) |
|
|
@@ -47,42 +47,18 @@ pip install -r requirements.txt
|
|
|
47
47
|
pip install Modal-Decomposition
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
*Well, some libs should be installed by yourself, don't use `pip install -r requirements.txt`, because `Triton` need installed by GitHub.*
|
|
51
|
-
|
|
52
|
-
*`Triton` please visite this url: https://github.com/woct0rdho/triton-windows/releases*
|
|
53
|
-
|
|
54
50
|
## Dependence
|
|
55
51
|
|
|
56
52
|
This lib's dependence are:
|
|
57
53
|
|
|
58
54
|
***Python: 3.10***
|
|
59
55
|
|
|
60
|
-
- [antropy](https://github.com/raphaelvallat/antropy)
|
|
61
|
-
- [colorama](https://github.com/tartley/colorama)
|
|
62
|
-
- [einops](https://github.com/arogozhnikov/einops)
|
|
63
56
|
- [EMD-signal](https://github.com/laszukdawid/PyEMD)
|
|
64
57
|
- [ewtpy](https://github.com/vrcarva/ewtpy)
|
|
65
|
-
- [numba](https://github.com/numba/numba)
|
|
66
|
-
- [numpy](https://github.com/numpy/numpy)
|
|
67
|
-
- [scipy](https://github.com/scipy/scipy)
|
|
68
58
|
- [vmdpy](https://github.com/vrcarva/vmdpy)
|
|
69
|
-
- [psutil](https://github.com/giampaolo/psutil)
|
|
70
59
|
|
|
71
60
|
*Other dependence please read "requirements.txt"*
|
|
72
61
|
|
|
73
|
-
## Codes Resource
|
|
74
|
-
|
|
75
|
-
All codes from :
|
|
76
|
-
|
|
77
|
-
- Github:
|
|
78
|
-
- EMD-signal -> EMD, CEEFD, CEEMDAN, EEMD
|
|
79
|
-
- ewtpy -> EWT
|
|
80
|
-
- vmdpy -> VMD
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- Myself:
|
|
84
|
-
- CEEMD, EFD, FMD, ICEEMDAN, LMD, MEMD, RPSEMD, SSA, SVMD
|
|
85
|
-
|
|
86
62
|
## Url
|
|
87
63
|
|
|
88
64
|
This lib's url is: https://github.com/a-raining-day/Modal-Decomposition
|
|
@@ -14,6 +14,7 @@ Description: (if None write None)
|
|
|
14
14
|
Modify: (must)
|
|
15
15
|
2026.3.25 - Create
|
|
16
16
|
2026.3.30 - Desperate the CEEMDAN and the CEEFD, and rename the Cyclic_CEEFD as CEEFD, del the origin CEEFD.
|
|
17
|
+
2026.4.7 - Fix the problem when the freq_bins include 0 will make the idx out of list. And change _extract_imf to staticmethod.
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
20
|
import numpy as np
|
|
@@ -44,13 +45,15 @@ class ceefd:
|
|
|
44
45
|
envelope = np.maximum(envelope, mag_spectrum)
|
|
45
46
|
return envelope
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
@staticmethod
|
|
49
|
+
def _extract_imf(signal, freq_bins):
|
|
48
50
|
N = len(signal)
|
|
49
51
|
fft_signal = np.fft.fft(signal)
|
|
50
52
|
|
|
51
53
|
mask = np.zeros(N, dtype=bool)
|
|
52
54
|
mask[freq_bins] = True
|
|
53
|
-
mask[N - np.array(freq_bins)] = True
|
|
55
|
+
# mask[N - np.array(freq_bins)] = True -> \ # 2026.4.7
|
|
56
|
+
mask[(N - np.array(freq_bins)) % N] = True
|
|
54
57
|
|
|
55
58
|
imf_fft = fft_signal * mask
|
|
56
59
|
imf = np.fft.ifft(imf_fft).real
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python version: (must)
|
|
3
|
+
3.10.11
|
|
4
|
+
|
|
5
|
+
Lib and Version: (if None write None)
|
|
6
|
+
EMD-S - 1.9.0
|
|
7
|
+
|
|
8
|
+
Only accessed by: (must)
|
|
9
|
+
Only __init__.py
|
|
10
|
+
|
|
11
|
+
Description: (if None write None)
|
|
12
|
+
Realize the EEMD.
|
|
13
|
+
|
|
14
|
+
Modify: (must)
|
|
15
|
+
2026.3.25 - Create.
|
|
16
|
+
2026.3.27 - Change the EEMD class' usage. EEMD(parallel=True) (default) -> EEMD(parallel=False). Now, it will not use parallel default.
|
|
17
|
+
2026.4.2 - Finish the Optimization of the EEMD. Correct the logic.
|
|
18
|
+
2026.5.1 - Fix: use PyEMD.EEMD to ensure standard algorithm.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
from typing import Union, Tuple, Optional
|
|
23
|
+
|
|
24
|
+
def eemd(
|
|
25
|
+
S: Union[list, np.ndarray],
|
|
26
|
+
T: Optional[Union[list, np.ndarray]] = None,
|
|
27
|
+
trials: int = 100,
|
|
28
|
+
noise_width: float = 0.05,
|
|
29
|
+
max_imf: int = -1,
|
|
30
|
+
**kwargs
|
|
31
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
32
|
+
"""
|
|
33
|
+
EEMD: Ensemble Empirical Mode Decomposition
|
|
34
|
+
(Standard implementation via PyEMD)
|
|
35
|
+
|
|
36
|
+
:param S: Signal (1-dim)
|
|
37
|
+
:param T: Time axis (1-dim). Default uniform.
|
|
38
|
+
:param trials: Number of white noise realizations (ensemble size).
|
|
39
|
+
:param noise_width: Standard deviation of added white noise relative to signal std.
|
|
40
|
+
:param max_imf: Max number of IMFs to extract. -1 for all.
|
|
41
|
+
:param kwargs: Additional parameters passed to PyEMD.EEMD.
|
|
42
|
+
:return: IMFs (n_IMFs, N), Res (N,)
|
|
43
|
+
"""
|
|
44
|
+
from PyEMD import EEMD
|
|
45
|
+
|
|
46
|
+
if not isinstance(S, np.ndarray):
|
|
47
|
+
S = np.array(S, dtype=np.float64).ravel()
|
|
48
|
+
else:
|
|
49
|
+
S = S.ravel().astype(np.float64)
|
|
50
|
+
|
|
51
|
+
N = len(S)
|
|
52
|
+
|
|
53
|
+
if T is None:
|
|
54
|
+
T = np.arange(N, dtype=np.float64)
|
|
55
|
+
else:
|
|
56
|
+
T = np.asarray(T, dtype=np.float64)
|
|
57
|
+
|
|
58
|
+
decomposer = EEMD(trials=trials, noise_width=noise_width, **kwargs)
|
|
59
|
+
result = decomposer.eemd(S, T, max_imf=max_imf)
|
|
60
|
+
|
|
61
|
+
IMFs = result[:-1, :] # shape [n_imfs, N]
|
|
62
|
+
Res = result[-1, :] # shape [N,]
|
|
63
|
+
|
|
64
|
+
return IMFs, Res
|
|
@@ -17,6 +17,7 @@ Description: (if None write None)
|
|
|
17
17
|
Modify: (must)
|
|
18
18
|
2026.3.25 - Create
|
|
19
19
|
2026.4.2 - Finish the Optimization of the EFD. Del the origin efd function.
|
|
20
|
+
2026.4.7 - Correct the error of the use of the np.concentrate and the construction of the wn.
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
23
|
import numpy as np
|
|
@@ -24,13 +25,14 @@ from .COLOR import printc
|
|
|
24
25
|
from typing import Union, Tuple
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: int=-1) -> Tuple[np.ndarray, np.ndarray]:
|
|
28
|
+
def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: int=-1, verbose: bool=False) -> Tuple[np.ndarray, np.ndarray]:
|
|
28
29
|
"""
|
|
29
30
|
EFD: Empirical Fourier Decomposition
|
|
30
31
|
|
|
31
32
|
:param S: Signal (1-dim)
|
|
32
33
|
:param T: Time axis (1-dim)
|
|
33
34
|
:param max_IMFs: the num of the IMFs. -1 means return all IMFs
|
|
35
|
+
:param verbose: if print the info?
|
|
34
36
|
:return: IMFs (n_IMFs, N), Res: (N,)
|
|
35
37
|
"""
|
|
36
38
|
|
|
@@ -60,7 +62,8 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
|
|
|
60
62
|
|
|
61
63
|
if T is None: # if T is None, default generate uniform T-axis.
|
|
62
64
|
T = np.arange(N) # default fs = 1
|
|
63
|
-
|
|
65
|
+
if verbose:
|
|
66
|
+
print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
|
|
64
67
|
|
|
65
68
|
else:
|
|
66
69
|
if not isinstance(T, np.ndarray):
|
|
@@ -86,14 +89,13 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
|
|
|
86
89
|
uniform_freq = np.linspace(0, np.pi, N // 2)
|
|
87
90
|
freq_N = len(uniform_freq)
|
|
88
91
|
|
|
89
|
-
|
|
92
|
+
local_maximum_points_tuple = argrelmax(edge_magnitude)
|
|
93
|
+
local_maximum_points = local_maximum_points_tuple[0]
|
|
90
94
|
local_maximum = edge_magnitude[local_maximum_points]
|
|
91
95
|
|
|
92
96
|
if max_IMFs != -1:
|
|
93
97
|
local_maximum_zip = [(point, value) for point, value in zip(local_maximum_points, local_maximum)]
|
|
94
|
-
|
|
95
98
|
local_maximum_zip = sorted(local_maximum_zip, reverse=True, key=lambda x: x[1])
|
|
96
|
-
|
|
97
99
|
local_maximum_points = list(map(lambda x: x[0], local_maximum_zip[:max_IMFs]))
|
|
98
100
|
|
|
99
101
|
local_maximum_points = np.concatenate(([0], local_maximum_points, [freq_N - 1]))
|
|
@@ -112,7 +114,7 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
|
|
|
112
114
|
else:
|
|
113
115
|
# wn.append(np.argmin(edge_magnitude[current_point:next_point + 1]))
|
|
114
116
|
wn.append(current_point + np.argmin(edge_magnitude[current_point:next_point + 1]))
|
|
115
|
-
wn = np.concatenate(([0], wn, freq_N - 1))
|
|
117
|
+
wn = np.concatenate(([0], wn, [freq_N - 1]))
|
|
116
118
|
|
|
117
119
|
filters_arr = []
|
|
118
120
|
for edge in range(1, len(wn)):
|
|
@@ -18,11 +18,10 @@ Modify: (must)
|
|
|
18
18
|
|
|
19
19
|
from PyEMD import EMD
|
|
20
20
|
import numpy as np
|
|
21
|
-
from .COLOR.colorful_print import printc
|
|
22
21
|
from typing import Tuple, Union
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind: str = "cubic", nbsym: int = 2, max_imf=-1,
|
|
24
|
+
def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind: str = "cubic", nbsym: int = 2, max_imf=-1, verbose: bool=False)\
|
|
26
25
|
-> Tuple[np.ndarray, np.ndarray]:
|
|
27
26
|
"""
|
|
28
27
|
EMD: Empirical Mode Decomposition
|
|
@@ -32,7 +31,7 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
|
|
|
32
31
|
:param spline_kind: the kind of spline. default cubic.
|
|
33
32
|
:param nbsym:
|
|
34
33
|
:param max_imf: the max num of IMFs
|
|
35
|
-
:param
|
|
34
|
+
:param verbose: True will print info, else no.
|
|
36
35
|
:return: IMFs (2-dim), Res (1-dim)
|
|
37
36
|
"""
|
|
38
37
|
if not isinstance(S, np.ndarray):
|
|
@@ -52,7 +51,8 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
|
|
|
52
51
|
|
|
53
52
|
if T is None: # if T is None, default generate uniform T-axis.
|
|
54
53
|
T = np.arange(N) # default fs = 1
|
|
55
|
-
|
|
54
|
+
if verbose:
|
|
55
|
+
print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
|
|
56
56
|
|
|
57
57
|
else:
|
|
58
58
|
if not isinstance(T, np.ndarray):
|
|
@@ -62,4 +62,13 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
|
|
|
62
62
|
|
|
63
63
|
IMFs = EMD_cls.emd(S, T, max_imf=max_imf)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Res = IMFs[-1, :]
|
|
66
|
+
IMFs = IMFs[:-1, :]
|
|
67
|
+
|
|
68
|
+
if IMFs.ndim == 1:
|
|
69
|
+
IMFs = IMFs.reshape(1, -1)
|
|
70
|
+
|
|
71
|
+
elif IMFs.ndim == 0:
|
|
72
|
+
IMFs = np.zeros((1, Res.shape[1]))
|
|
73
|
+
|
|
74
|
+
return IMFs, Res
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python version: (must)
|
|
3
|
+
3.10.11
|
|
4
|
+
|
|
5
|
+
Lib and Version: (if None write None)
|
|
6
|
+
numpy - 2.2.6
|
|
7
|
+
scipy - 1.15.3
|
|
8
|
+
|
|
9
|
+
Only accessed by: (must)
|
|
10
|
+
Only __init__.py
|
|
11
|
+
|
|
12
|
+
Modify: (must)
|
|
13
|
+
2026.3.25
|
|
14
|
+
|
|
15
|
+
Description: (if None write None)
|
|
16
|
+
Realize the LMD
|
|
17
|
+
|
|
18
|
+
Modify:
|
|
19
|
+
2026.3.25 - Optimize the use of scipy
|
|
20
|
+
2026.4.3 - Finish the Optimization of the LMD.
|
|
21
|
+
2026.4.7 - LMD can support the decompose completely now.
|
|
22
|
+
2026.4.9 - Change some hardcode parameters to args.
|
|
23
|
+
2026.5.1 - Fix the dependence of "help_function.py".
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import numpy as np
|
|
27
|
+
from .Utils import is_monotonic
|
|
28
|
+
from typing import Union, Tuple
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def lmd(
|
|
32
|
+
S: Union[list, np.ndarray],
|
|
33
|
+
max_pf: Union[int, None] = None,
|
|
34
|
+
max_iter: int = 37,
|
|
35
|
+
eps: float = 0.05,
|
|
36
|
+
eps_stable: float = 1e-12,
|
|
37
|
+
min_amp: float = 1e-12,
|
|
38
|
+
max_amp: float = 1e12,
|
|
39
|
+
converge_mean: float = 1e-3,
|
|
40
|
+
smooth_window: int = 5
|
|
41
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
42
|
+
"""
|
|
43
|
+
LMD: Local mean decomposition
|
|
44
|
+
|
|
45
|
+
:param S: Signal (1-dim)
|
|
46
|
+
:param max_pf: the max num of the PFs. Default log2(N), -1 means decompose completely.
|
|
47
|
+
:param max_iter: max iteration for each iter of decomposing the PFs. Default 37, according the paper.
|
|
48
|
+
:param eps: Envelope convergence threshold
|
|
49
|
+
:param eps_stable:
|
|
50
|
+
:param min_amp:
|
|
51
|
+
:param max_amp:
|
|
52
|
+
:param converge_mean:
|
|
53
|
+
:param smooth_window:
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
PFs: (n_pf, N), Res: (N,)
|
|
57
|
+
"""
|
|
58
|
+
from scipy.signal import argrelextrema, savgol_filter
|
|
59
|
+
|
|
60
|
+
if S.ndim == 0:
|
|
61
|
+
raise ValueError("The dim of the S must be 1-dim, not 0")
|
|
62
|
+
|
|
63
|
+
elif S.ndim > 1:
|
|
64
|
+
if 1 in S.shape:
|
|
65
|
+
S = S.reshape(-1)
|
|
66
|
+
|
|
67
|
+
else:
|
|
68
|
+
raise ValueError(f"The dim of S must be 1-dim, not {S.ndim}")
|
|
69
|
+
|
|
70
|
+
if not isinstance(S, np.ndarray):
|
|
71
|
+
S = np.asarray(S, dtype=np.float64).ravel()
|
|
72
|
+
n_samples = S.size
|
|
73
|
+
|
|
74
|
+
if n_samples < 8:
|
|
75
|
+
raise ValueError("LMD requires signal length ≥ 8 (Smith 2005 standard)")
|
|
76
|
+
|
|
77
|
+
t = np.arange(n_samples, dtype=np.float64)
|
|
78
|
+
max_pf = int(np.log2(n_samples)) if max_pf is None else max_pf
|
|
79
|
+
residue = S.copy()
|
|
80
|
+
PFs: list[np.ndarray] = []
|
|
81
|
+
|
|
82
|
+
if max_pf != -1:
|
|
83
|
+
for _ in range(max_pf):
|
|
84
|
+
h = residue.copy()
|
|
85
|
+
a_total = np.ones(n_samples, dtype=np.float64)
|
|
86
|
+
converged = False
|
|
87
|
+
|
|
88
|
+
for __ in range(max_iter):
|
|
89
|
+
max_loc = argrelextrema(h, np.greater)[0]
|
|
90
|
+
min_loc = argrelextrema(h, np.less)[0]
|
|
91
|
+
ext_idx = np.unique(np.concatenate([max_loc, min_loc]))
|
|
92
|
+
|
|
93
|
+
if len(ext_idx) < 3:
|
|
94
|
+
break
|
|
95
|
+
|
|
96
|
+
ext_idx, ext_vals = _mirror_extend_real(h, ext_idx, n_samples)
|
|
97
|
+
ext_idx = np.sort(ext_idx)
|
|
98
|
+
|
|
99
|
+
t_mid = (ext_idx[:-1] + ext_idx[1:]) / 2.0
|
|
100
|
+
m_vals = (ext_vals[:-1] + ext_vals[1:]) / 2.0
|
|
101
|
+
a_vals = np.abs(ext_vals[:-1] - ext_vals[1:]) / 2.0
|
|
102
|
+
|
|
103
|
+
m_t = _safe_interpolate(t_mid, m_vals, t)
|
|
104
|
+
a_t = _safe_interpolate(t_mid, a_vals, t)
|
|
105
|
+
|
|
106
|
+
a_t = np.clip(a_t, min_amp, max_amp)
|
|
107
|
+
a_t = savgol_filter(a_t, smooth_window, 2)
|
|
108
|
+
|
|
109
|
+
if _check_convergence(m_t, a_t, eps, converge_mean):
|
|
110
|
+
converged = True
|
|
111
|
+
break
|
|
112
|
+
|
|
113
|
+
s_new = (h - m_t) / a_t
|
|
114
|
+
a_total = np.clip(a_total * a_t, min_amp, max_amp)
|
|
115
|
+
h = s_new
|
|
116
|
+
|
|
117
|
+
if not converged:
|
|
118
|
+
break
|
|
119
|
+
|
|
120
|
+
current_pf = np.clip(a_total * s_new, -max_amp, max_amp)
|
|
121
|
+
|
|
122
|
+
if np.any(np.isnan(current_pf)) or np.any(np.isinf(current_pf)):
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
if np.sum(current_pf ** 2) < eps_stable:
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
PFs.append(current_pf)
|
|
129
|
+
residue -= current_pf
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
is_monotonic(residue) or
|
|
133
|
+
np.sum(residue ** 2) < eps_stable or
|
|
134
|
+
len(argrelextrema(residue, np.greater)[0]) + len(argrelextrema(residue, np.less)[0]) <= 2
|
|
135
|
+
):
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
else:
|
|
139
|
+
while True:
|
|
140
|
+
h = residue.copy()
|
|
141
|
+
a_total = np.ones(n_samples, dtype=np.float64)
|
|
142
|
+
converged = False
|
|
143
|
+
|
|
144
|
+
for __ in range(max_iter):
|
|
145
|
+
max_loc = argrelextrema(h, np.greater)[0]
|
|
146
|
+
min_loc = argrelextrema(h, np.less)[0]
|
|
147
|
+
ext_idx = np.unique(np.concatenate([max_loc, min_loc]))
|
|
148
|
+
|
|
149
|
+
if len(ext_idx) < 3:
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
ext_idx, ext_vals = _mirror_extend_real(h, ext_idx, n_samples)
|
|
153
|
+
ext_idx = np.sort(ext_idx)
|
|
154
|
+
|
|
155
|
+
t_mid = (ext_idx[:-1] + ext_idx[1:]) / 2.0
|
|
156
|
+
m_vals = (ext_vals[:-1] + ext_vals[1:]) / 2.0
|
|
157
|
+
a_vals = np.abs(ext_vals[:-1] - ext_vals[1:]) / 2.0
|
|
158
|
+
|
|
159
|
+
m_t = _safe_interpolate(t_mid, m_vals, t)
|
|
160
|
+
a_t = _safe_interpolate(t_mid, a_vals, t)
|
|
161
|
+
|
|
162
|
+
a_t = np.clip(a_t, min_amp, max_amp)
|
|
163
|
+
a_t = savgol_filter(a_t, smooth_window, 2)
|
|
164
|
+
|
|
165
|
+
if _check_convergence(m_t, a_t, eps, converge_mean):
|
|
166
|
+
converged = True
|
|
167
|
+
break
|
|
168
|
+
|
|
169
|
+
s_new = (h - m_t) / a_t
|
|
170
|
+
a_total = np.clip(a_total * a_t, min_amp, max_amp)
|
|
171
|
+
h = s_new
|
|
172
|
+
|
|
173
|
+
if not converged:
|
|
174
|
+
break
|
|
175
|
+
|
|
176
|
+
current_pf = np.clip(a_total * s_new, -max_amp, max_amp)
|
|
177
|
+
|
|
178
|
+
if np.any(np.isnan(current_pf)) or np.any(np.isinf(current_pf)):
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
if np.sum(current_pf ** 2) < eps_stable:
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
PFs.append(current_pf)
|
|
185
|
+
residue -= current_pf
|
|
186
|
+
|
|
187
|
+
if (is_increasing(residue) or is_increasing(-residue) or
|
|
188
|
+
np.sum(residue ** 2) < eps_stable or
|
|
189
|
+
len(argrelextrema(residue, np.greater)[0]) + len(argrelextrema(residue, np.less)[0]) <= 2):
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
del h, a_total, t
|
|
193
|
+
return np.array(PFs, dtype=np.float64), residue
|
|
194
|
+
|
|
195
|
+
def _mirror_extend_real(signal: np.ndarray, ext_idx: np.ndarray, n_samples: int) -> Tuple[np.ndarray, np.ndarray]:
|
|
196
|
+
ext = ext_idx.copy()
|
|
197
|
+
vals = signal[ext].copy()
|
|
198
|
+
last_idx = n_samples - 1
|
|
199
|
+
|
|
200
|
+
if ext[0] > 0:
|
|
201
|
+
mirror_pos = 0
|
|
202
|
+
mirror_val = 2 * signal[0] - vals[0]
|
|
203
|
+
ext = np.insert(ext, 0, mirror_pos)
|
|
204
|
+
vals = np.insert(vals, 0, mirror_val)
|
|
205
|
+
|
|
206
|
+
if ext[-1] < last_idx:
|
|
207
|
+
mirror_pos = last_idx
|
|
208
|
+
mirror_val = 2 * signal[-1] - vals[-1]
|
|
209
|
+
ext = np.append(ext, mirror_pos)
|
|
210
|
+
vals = np.append(vals, mirror_val)
|
|
211
|
+
|
|
212
|
+
return ext, vals
|
|
213
|
+
|
|
214
|
+
def _safe_interpolate(x: np.ndarray, y: np.ndarray, x_new: np.ndarray) -> np.ndarray:
|
|
215
|
+
from scipy import interpolate
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
interp = interpolate.CubicSpline(x, y, bc_type="natural")
|
|
219
|
+
res = interp(x_new)
|
|
220
|
+
except:
|
|
221
|
+
res = np.interp(x_new, x, y)
|
|
222
|
+
|
|
223
|
+
left_mask = x_new < x[0]
|
|
224
|
+
right_mask = x_new > x[-1]
|
|
225
|
+
if np.any(left_mask):
|
|
226
|
+
slope = (y[1] - y[0]) / (x[1] - x[0])
|
|
227
|
+
res[left_mask] = y[0] + slope * (x_new[left_mask] - x[0])
|
|
228
|
+
if np.any(right_mask):
|
|
229
|
+
slope = (y[-1] - y[-2]) / (x[-1] - x[-2])
|
|
230
|
+
res[right_mask] = y[-1] + slope * (x_new[right_mask] - x[-1])
|
|
231
|
+
|
|
232
|
+
return res
|
|
233
|
+
|
|
234
|
+
def _check_convergence(m_t: np.ndarray, a_t: np.ndarray, eps: float, converge_mean) -> bool:
|
|
235
|
+
mean_ok = np.max(np.abs(m_t)) < converge_mean
|
|
236
|
+
envelope_ok = np.max(np.abs(a_t - 1.0)) < eps
|
|
237
|
+
return mean_ok and envelope_ok
|
|
238
|
+
|
|
239
|
+
def compute_envelope(signal: Union[list, np.ndarray]) -> np.ndarray:
|
|
240
|
+
from scipy.linalg import hilbert
|
|
241
|
+
|
|
242
|
+
signal = np.asarray(signal, dtype=np.float64).ravel()
|
|
243
|
+
return np.abs(hilbert(signal))
|
|
@@ -9,20 +9,21 @@ Lib and Version: (if None write None)
|
|
|
9
9
|
Only accessed by: (must)
|
|
10
10
|
Only __init__.py
|
|
11
11
|
|
|
12
|
-
Modify: (must)
|
|
13
|
-
2026.3.25
|
|
14
|
-
|
|
15
12
|
Description: (if None write None)
|
|
16
13
|
Realize the MEMD
|
|
14
|
+
|
|
15
|
+
Modify: (must)
|
|
16
|
+
2026.3.25 - Create.
|
|
17
|
+
2026.5.1 - Fix the problem when calculating projections.
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
20
|
import numpy as np
|
|
20
21
|
from typing import Union, Tuple
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2, max_iter=10) -> Tuple[np.ndarray, np.ndarray]:
|
|
24
|
+
def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2, max_iter=10, spline_kind: str="linear") -> Tuple[np.ndarray, np.ndarray]:
|
|
24
25
|
"""
|
|
25
|
-
MEMD: Multimodal
|
|
26
|
+
MEMD: Multimodal Empirical Mode Decomposition
|
|
26
27
|
|
|
27
28
|
:param S: Signal (2-dim), (d, N) | d -> channels,N -> time points
|
|
28
29
|
:param d: channels or dimensions
|
|
@@ -30,7 +31,8 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
|
|
|
30
31
|
:param max_imf: max num of IMFs
|
|
31
32
|
:param sd_thresh: therahold
|
|
32
33
|
:param max_iter: max iterations of each IMF
|
|
33
|
-
:
|
|
34
|
+
:param spline_kind: spline kind.
|
|
35
|
+
:return: IMFs (IMFs_num, d, N), Res (2-dim)
|
|
34
36
|
"""
|
|
35
37
|
|
|
36
38
|
if not isinstance(S, np.ndarray):
|
|
@@ -56,7 +58,7 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
|
|
|
56
58
|
|
|
57
59
|
vectors = generate_hammersley_points(k, d) # dhape: (d, k)
|
|
58
60
|
|
|
59
|
-
projections = np.dot(S.T, vectors) # shape: (N, k)
|
|
61
|
+
# projections = np.dot(S.T, vectors) # shape: (N, k)
|
|
60
62
|
|
|
61
63
|
imfs = []
|
|
62
64
|
residue = S.copy()
|
|
@@ -66,7 +68,7 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
|
|
|
66
68
|
|
|
67
69
|
for iter_num in range(max_iter):
|
|
68
70
|
h_old = h
|
|
69
|
-
mean_envelope = compute_local_mean(h, vectors,
|
|
71
|
+
mean_envelope = compute_local_mean(h, vectors, T, spline_kind=spline_kind)
|
|
70
72
|
|
|
71
73
|
h_new = h - mean_envelope
|
|
72
74
|
sd = np.sum((h_old - h_new) ** 2) / np.sum(h_old ** 2)
|
|
@@ -153,7 +155,7 @@ def generate_primes(n):
|
|
|
153
155
|
return primes
|
|
154
156
|
|
|
155
157
|
|
|
156
|
-
def compute_local_mean(signal, vectors, T):
|
|
158
|
+
def compute_local_mean(signal, vectors, T, spline_kind: str="linear"):
|
|
157
159
|
"""
|
|
158
160
|
S: now S (d, N)
|
|
159
161
|
vectors: directional vector (d, k)
|
|
@@ -196,7 +198,7 @@ def compute_local_mean(signal, vectors, T):
|
|
|
196
198
|
max_values = signal[ch, max_indices]
|
|
197
199
|
if len(max_indices) >= 4:
|
|
198
200
|
try:
|
|
199
|
-
max_spline = interp1d(max_indices, max_values, kind=
|
|
201
|
+
max_spline = interp1d(max_indices, max_values, kind=spline_kind,fill_value='extrapolate')
|
|
200
202
|
max_env = max_spline(T)
|
|
201
203
|
except:
|
|
202
204
|
max_env = np.interp(T, max_indices, max_values)
|
|
@@ -206,7 +208,7 @@ def compute_local_mean(signal, vectors, T):
|
|
|
206
208
|
min_values = signal[ch, min_indices]
|
|
207
209
|
if len(min_indices) >= 4:
|
|
208
210
|
try:
|
|
209
|
-
min_spline = interp1d(min_indices, min_values, kind=
|
|
211
|
+
min_spline = interp1d(min_indices, min_values, kind=spline_kind,fill_value='extrapolate')
|
|
210
212
|
min_env = min_spline(T)
|
|
211
213
|
except:
|
|
212
214
|
min_env = np.interp(T, min_indices, min_values)
|
|
@@ -15,7 +15,7 @@ Description: (if None write None)
|
|
|
15
15
|
|
|
16
16
|
Modify: (must)
|
|
17
17
|
2026.3.25 - Create.
|
|
18
|
-
2026.3.29 - Optimize the SSA.decompose function, it's
|
|
18
|
+
2026.3.29 - Optimize the SSA.decompose function, it's use_JIT now. Please use SSA() which means SSA.decompose_fast().
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import numpy as np
|
|
@@ -165,7 +165,7 @@ class SSA:
|
|
|
165
165
|
:param S: Signal (1-dim)
|
|
166
166
|
:param L:
|
|
167
167
|
:param groups: group information, such as: [[0], [1,2], [3,4]] means which components will be merged. If None, return all
|
|
168
|
-
:param faster: if you choose True, function will be
|
|
168
|
+
:param faster: if you choose True, function will be use_JIT with wasting memory.
|
|
169
169
|
:return: IMFs (2-dim)
|
|
170
170
|
"""
|
|
171
171
|
|