eegdash 0.3.3.dev61__py3-none-any.whl → 0.5.0.dev180784713__py3-none-any.whl
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.
- eegdash/__init__.py +19 -6
- eegdash/api.py +336 -539
- eegdash/bids_eeg_metadata.py +495 -0
- eegdash/const.py +349 -0
- eegdash/dataset/__init__.py +28 -0
- eegdash/dataset/base.py +311 -0
- eegdash/dataset/bids_dataset.py +641 -0
- eegdash/dataset/dataset.py +692 -0
- eegdash/dataset/dataset_summary.csv +255 -0
- eegdash/dataset/registry.py +287 -0
- eegdash/downloader.py +197 -0
- eegdash/features/__init__.py +15 -13
- eegdash/features/datasets.py +329 -138
- eegdash/features/decorators.py +105 -13
- eegdash/features/extractors.py +233 -63
- eegdash/features/feature_bank/__init__.py +12 -12
- eegdash/features/feature_bank/complexity.py +22 -20
- eegdash/features/feature_bank/connectivity.py +27 -28
- eegdash/features/feature_bank/csp.py +3 -1
- eegdash/features/feature_bank/dimensionality.py +6 -6
- eegdash/features/feature_bank/signal.py +29 -30
- eegdash/features/feature_bank/spectral.py +40 -44
- eegdash/features/feature_bank/utils.py +8 -0
- eegdash/features/inspect.py +126 -15
- eegdash/features/serialization.py +58 -17
- eegdash/features/utils.py +90 -16
- eegdash/hbn/__init__.py +28 -0
- eegdash/hbn/preprocessing.py +105 -0
- eegdash/hbn/windows.py +428 -0
- eegdash/logging.py +54 -0
- eegdash/mongodb.py +55 -24
- eegdash/paths.py +52 -0
- eegdash/utils.py +29 -1
- eegdash-0.5.0.dev180784713.dist-info/METADATA +121 -0
- eegdash-0.5.0.dev180784713.dist-info/RECORD +38 -0
- eegdash-0.5.0.dev180784713.dist-info/licenses/LICENSE +29 -0
- eegdash/data_config.py +0 -34
- eegdash/data_utils.py +0 -687
- eegdash/dataset.py +0 -69
- eegdash/preprocessing.py +0 -63
- eegdash-0.3.3.dev61.dist-info/METADATA +0 -192
- eegdash-0.3.3.dev61.dist-info/RECORD +0 -28
- eegdash-0.3.3.dev61.dist-info/licenses/LICENSE +0 -23
- {eegdash-0.3.3.dev61.dist-info → eegdash-0.5.0.dev180784713.dist-info}/WHEEL +0 -0
- {eegdash-0.3.3.dev61.dist-info → eegdash-0.5.0.dev180784713.dist-info}/top_level.txt +0 -0
|
@@ -3,11 +3,10 @@ import numpy as np
|
|
|
3
3
|
from sklearn.neighbors import KDTree
|
|
4
4
|
|
|
5
5
|
from ..decorators import FeaturePredecessor, univariate_feature
|
|
6
|
-
from ..extractors import FeatureExtractor
|
|
7
6
|
from .signal import SIGNAL_PREDECESSORS
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"
|
|
9
|
+
"complexity_entropy_preprocessor",
|
|
11
10
|
"complexity_approx_entropy",
|
|
12
11
|
"complexity_sample_entropy",
|
|
13
12
|
"complexity_svd_entropy",
|
|
@@ -30,28 +29,31 @@ def _channel_app_samp_entropy_counts(x, m, r, l):
|
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
def complexity_entropy_preprocessor(x, /, m=2, r=0.2, l=1):
|
|
33
|
+
rr = r * x.std(axis=-1)
|
|
34
|
+
counts_m = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // l))
|
|
35
|
+
counts_mp1 = np.empty((*x.shape[:-1], (x.shape[-1] - m) // l))
|
|
36
|
+
for i in np.ndindex(x.shape[:-1]):
|
|
37
|
+
counts_m[i + (slice(None),)] = _channel_app_samp_entropy_counts(
|
|
38
|
+
x[i], m, rr[i], l
|
|
39
|
+
)
|
|
40
|
+
counts_mp1[i + (slice(None),)] = _channel_app_samp_entropy_counts(
|
|
41
|
+
x[i], m + 1, rr[i], l
|
|
42
|
+
)
|
|
43
|
+
return counts_m, counts_mp1
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@FeaturePredecessor(complexity_entropy_preprocessor)
|
|
45
47
|
@univariate_feature
|
|
46
|
-
def complexity_approx_entropy(counts_m, counts_mp1):
|
|
48
|
+
def complexity_approx_entropy(counts_m, counts_mp1, /):
|
|
47
49
|
phi_m = np.log(counts_m / counts_m.shape[-1]).mean(axis=-1)
|
|
48
50
|
phi_mp1 = np.log(counts_mp1 / counts_mp1.shape[-1]).mean(axis=-1)
|
|
49
51
|
return phi_m - phi_mp1
|
|
50
52
|
|
|
51
53
|
|
|
52
|
-
@FeaturePredecessor(
|
|
54
|
+
@FeaturePredecessor(complexity_entropy_preprocessor)
|
|
53
55
|
@univariate_feature
|
|
54
|
-
def complexity_sample_entropy(counts_m, counts_mp1):
|
|
56
|
+
def complexity_sample_entropy(counts_m, counts_mp1, /):
|
|
55
57
|
A = np.sum(counts_mp1 - 1, axis=-1)
|
|
56
58
|
B = np.sum(counts_m - 1, axis=-1)
|
|
57
59
|
return -np.log(A / B)
|
|
@@ -59,10 +61,10 @@ def complexity_sample_entropy(counts_m, counts_mp1):
|
|
|
59
61
|
|
|
60
62
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
61
63
|
@univariate_feature
|
|
62
|
-
def complexity_svd_entropy(x, m=10, tau=1):
|
|
64
|
+
def complexity_svd_entropy(x, /, m=10, tau=1):
|
|
63
65
|
x_emb = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // tau, m))
|
|
64
66
|
for i in np.ndindex(x.shape[:-1]):
|
|
65
|
-
x_emb[
|
|
67
|
+
x_emb[i + (slice(None), slice(None))] = _create_embedding(x[i], m, tau)
|
|
66
68
|
s = np.linalg.svdvals(x_emb)
|
|
67
69
|
s /= s.sum(axis=-1, keepdims=True)
|
|
68
70
|
return -np.sum(s * np.log(s), axis=-1)
|
|
@@ -71,7 +73,7 @@ def complexity_svd_entropy(x, m=10, tau=1):
|
|
|
71
73
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
72
74
|
@univariate_feature
|
|
73
75
|
@nb.njit(cache=True, fastmath=True)
|
|
74
|
-
def complexity_lempel_ziv(x, threshold=None, normalize=True):
|
|
76
|
+
def complexity_lempel_ziv(x, /, threshold=None, normalize=True):
|
|
75
77
|
lzc = np.empty(x.shape[:-1])
|
|
76
78
|
for i in np.ndindex(x.shape[:-1]):
|
|
77
79
|
t = np.median(x[i]) if threshold is None else threshold
|
|
@@ -4,56 +4,55 @@ import numpy as np
|
|
|
4
4
|
from scipy.signal import csd
|
|
5
5
|
|
|
6
6
|
from ..decorators import FeaturePredecessor, bivariate_feature
|
|
7
|
-
from ..extractors import BivariateFeature
|
|
7
|
+
from ..extractors import BivariateFeature
|
|
8
8
|
from . import utils
|
|
9
|
+
from .signal import SIGNAL_PREDECESSORS
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
|
-
"
|
|
12
|
+
"connectivity_coherency_preprocessor",
|
|
12
13
|
"connectivity_magnitude_square_coherence",
|
|
13
14
|
"connectivity_imaginary_coherence",
|
|
14
15
|
"connectivity_lagged_coherence",
|
|
15
16
|
]
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@FeaturePredecessor(CoherenceFeatureExtractor)
|
|
19
|
+
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
20
|
+
def connectivity_coherency_preprocessor(x, /, **kwargs):
|
|
21
|
+
f_min = kwargs.pop("f_min") if "f_min" in kwargs else None
|
|
22
|
+
f_max = kwargs.pop("f_max") if "f_max" in kwargs else None
|
|
23
|
+
assert "fs" in kwargs and "nperseg" in kwargs
|
|
24
|
+
kwargs["axis"] = -1
|
|
25
|
+
n = x.shape[1]
|
|
26
|
+
idx_x, idx_y = BivariateFeature.get_pair_iterators(n)
|
|
27
|
+
ix, iy = list(chain(range(n), idx_x)), list(chain(range(n), idx_y))
|
|
28
|
+
f, s = csd(x[:, ix], x[:, iy], **kwargs)
|
|
29
|
+
f_min, f_max = utils.get_valid_freq_band(kwargs["fs"], x.shape[-1], f_min, f_max)
|
|
30
|
+
f, s = utils.slice_freq_band(f, s, f_min=f_min, f_max=f_max)
|
|
31
|
+
p, sxy = np.split(s, [n], axis=1)
|
|
32
|
+
sxx, syy = p[:, idx_x].real, p[:, idx_y].real
|
|
33
|
+
c = sxy / np.sqrt(sxx * syy)
|
|
34
|
+
return f, c
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@FeaturePredecessor(connectivity_coherency_preprocessor)
|
|
39
38
|
@bivariate_feature
|
|
40
|
-
def connectivity_magnitude_square_coherence(f, c, bands=utils.DEFAULT_FREQ_BANDS):
|
|
39
|
+
def connectivity_magnitude_square_coherence(f, c, /, bands=utils.DEFAULT_FREQ_BANDS):
|
|
41
40
|
# https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity
|
|
42
41
|
coher = c.real**2 + c.imag**2
|
|
43
42
|
return utils.reduce_freq_bands(f, coher, bands, np.mean)
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
@FeaturePredecessor(
|
|
45
|
+
@FeaturePredecessor(connectivity_coherency_preprocessor)
|
|
47
46
|
@bivariate_feature
|
|
48
|
-
def connectivity_imaginary_coherence(f, c, bands=utils.DEFAULT_FREQ_BANDS):
|
|
47
|
+
def connectivity_imaginary_coherence(f, c, /, bands=utils.DEFAULT_FREQ_BANDS):
|
|
49
48
|
# https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity
|
|
50
49
|
coher = c.imag
|
|
51
50
|
return utils.reduce_freq_bands(f, coher, bands, np.mean)
|
|
52
51
|
|
|
53
52
|
|
|
54
|
-
@FeaturePredecessor(
|
|
53
|
+
@FeaturePredecessor(connectivity_coherency_preprocessor)
|
|
55
54
|
@bivariate_feature
|
|
56
|
-
def connectivity_lagged_coherence(f, c, bands=utils.DEFAULT_FREQ_BANDS):
|
|
55
|
+
def connectivity_lagged_coherence(f, c, /, bands=utils.DEFAULT_FREQ_BANDS):
|
|
57
56
|
# https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity
|
|
58
57
|
coher = c.imag / np.sqrt(1 - c.real)
|
|
59
58
|
return utils.reduce_freq_bands(f, coher, bands, np.mean)
|
|
@@ -3,8 +3,9 @@ import numpy as np
|
|
|
3
3
|
import scipy
|
|
4
4
|
import scipy.linalg
|
|
5
5
|
|
|
6
|
-
from ..decorators import multivariate_feature
|
|
6
|
+
from ..decorators import FeaturePredecessor, multivariate_feature
|
|
7
7
|
from ..extractors import TrainableFeature
|
|
8
|
+
from .signal import SIGNAL_PREDECESSORS
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"CommonSpatialPattern",
|
|
@@ -21,6 +22,7 @@ def _update_mean_cov(count, mean, cov, x_count, x_mean, x_cov):
|
|
|
21
22
|
cov[:] -= np.outer(mean, mean)
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
24
26
|
@multivariate_feature
|
|
25
27
|
class CommonSpatialPattern(TrainableFeature):
|
|
26
28
|
def __init__(self):
|
|
@@ -17,7 +17,7 @@ __all__ = [
|
|
|
17
17
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
18
18
|
@univariate_feature
|
|
19
19
|
@nb.njit(cache=True, fastmath=True)
|
|
20
|
-
def dimensionality_higuchi_fractal_dim(x, k_max=10, eps=1e-7):
|
|
20
|
+
def dimensionality_higuchi_fractal_dim(x, /, k_max=10, eps=1e-7):
|
|
21
21
|
N = x.shape[-1]
|
|
22
22
|
hfd = np.empty(x.shape[:-1])
|
|
23
23
|
log_k = np.vstack((-np.log(np.arange(1, k_max + 1)), np.ones(k_max))).T
|
|
@@ -26,7 +26,7 @@ def dimensionality_higuchi_fractal_dim(x, k_max=10, eps=1e-7):
|
|
|
26
26
|
for i in np.ndindex(x.shape[:-1]):
|
|
27
27
|
for k in range(1, k_max + 1):
|
|
28
28
|
for m in range(k):
|
|
29
|
-
L_km[m] = np.mean(np.abs(np.diff(x[
|
|
29
|
+
L_km[m] = np.mean(np.abs(np.diff(x[i + (slice(m, None),)], n=k)))
|
|
30
30
|
L_k[k - 1] = (N - 1) * np.sum(L_km[:k]) / (k**3)
|
|
31
31
|
L_k = np.maximum(L_k, eps)
|
|
32
32
|
hfd[i] = np.linalg.lstsq(log_k, np.log(L_k))[0][0]
|
|
@@ -35,7 +35,7 @@ def dimensionality_higuchi_fractal_dim(x, k_max=10, eps=1e-7):
|
|
|
35
35
|
|
|
36
36
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
37
37
|
@univariate_feature
|
|
38
|
-
def dimensionality_petrosian_fractal_dim(x):
|
|
38
|
+
def dimensionality_petrosian_fractal_dim(x, /):
|
|
39
39
|
nd = signal_zero_crossings(np.diff(x, axis=-1))
|
|
40
40
|
log_n = np.log(x.shape[-1])
|
|
41
41
|
return log_n / (np.log(nd) + log_n)
|
|
@@ -43,7 +43,7 @@ def dimensionality_petrosian_fractal_dim(x):
|
|
|
43
43
|
|
|
44
44
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
45
45
|
@univariate_feature
|
|
46
|
-
def dimensionality_katz_fractal_dim(x):
|
|
46
|
+
def dimensionality_katz_fractal_dim(x, /):
|
|
47
47
|
dists = np.abs(np.diff(x, axis=-1))
|
|
48
48
|
L = dists.sum(axis=-1)
|
|
49
49
|
a = dists.mean(axis=-1)
|
|
@@ -79,7 +79,7 @@ def _hurst_exp(x, ns, a, gamma_ratios, log_n):
|
|
|
79
79
|
|
|
80
80
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
81
81
|
@univariate_feature
|
|
82
|
-
def dimensionality_hurst_exp(x):
|
|
82
|
+
def dimensionality_hurst_exp(x, /):
|
|
83
83
|
ns = np.unique(np.power(2, np.arange(2, np.log2(x.shape[-1]) - 1)).astype(int))
|
|
84
84
|
idx = ns > 340
|
|
85
85
|
gamma_ratios = np.empty(ns.shape[0])
|
|
@@ -94,7 +94,7 @@ def dimensionality_hurst_exp(x):
|
|
|
94
94
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
95
95
|
@univariate_feature
|
|
96
96
|
@nb.njit(cache=True, fastmath=True)
|
|
97
|
-
def dimensionality_detrended_fluctuation_analysis(x):
|
|
97
|
+
def dimensionality_detrended_fluctuation_analysis(x, /):
|
|
98
98
|
ns = np.unique(np.floor(np.power(2, np.arange(2, np.log2(x.shape[-1]) - 1))))
|
|
99
99
|
a = np.vstack((np.arange(ns[-1]), np.ones(int(ns[-1])))).T
|
|
100
100
|
log_n = np.vstack((np.log(ns), np.ones(ns.shape[0]))).T
|
|
@@ -4,93 +4,92 @@ import numpy as np
|
|
|
4
4
|
from scipy import signal, stats
|
|
5
5
|
|
|
6
6
|
from ..decorators import FeaturePredecessor, univariate_feature
|
|
7
|
-
from ..extractors import FeatureExtractor
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
9
|
+
"signal_hilbert_preprocessor",
|
|
10
|
+
"SIGNAL_PREDECESSORS",
|
|
11
|
+
"signal_decorrelation_time",
|
|
12
|
+
"signal_hjorth_activity",
|
|
13
|
+
"signal_hjorth_complexity",
|
|
14
|
+
"signal_hjorth_mobility",
|
|
14
15
|
"signal_kurtosis",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
16
|
+
"signal_line_length",
|
|
17
|
+
"signal_mean",
|
|
17
18
|
"signal_peak_to_peak",
|
|
18
19
|
"signal_quantile",
|
|
20
|
+
"signal_root_mean_square",
|
|
21
|
+
"signal_skewness",
|
|
22
|
+
"signal_std",
|
|
23
|
+
"signal_variance",
|
|
19
24
|
"signal_zero_crossings",
|
|
20
|
-
"signal_line_length",
|
|
21
|
-
"signal_hjorth_activity",
|
|
22
|
-
"signal_hjorth_mobility",
|
|
23
|
-
"signal_hjorth_complexity",
|
|
24
|
-
"signal_decorrelation_time",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
@FeaturePredecessor(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return np.abs(signal.hilbert(x - x.mean(axis=-1, keepdims=True), axis=-1))
|
|
28
|
+
@FeaturePredecessor()
|
|
29
|
+
def signal_hilbert_preprocessor(x, /):
|
|
30
|
+
return np.abs(signal.hilbert(x - x.mean(axis=-1, keepdims=True), axis=-1))
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
SIGNAL_PREDECESSORS = [
|
|
33
|
+
SIGNAL_PREDECESSORS = [None, signal_hilbert_preprocessor]
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
38
37
|
@univariate_feature
|
|
39
|
-
def signal_mean(x):
|
|
38
|
+
def signal_mean(x, /):
|
|
40
39
|
return x.mean(axis=-1)
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
44
43
|
@univariate_feature
|
|
45
|
-
def signal_variance(x, **kwargs):
|
|
44
|
+
def signal_variance(x, /, **kwargs):
|
|
46
45
|
return x.var(axis=-1, **kwargs)
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
50
49
|
@univariate_feature
|
|
51
|
-
def signal_std(x, **kwargs):
|
|
50
|
+
def signal_std(x, /, **kwargs):
|
|
52
51
|
return x.std(axis=-1, **kwargs)
|
|
53
52
|
|
|
54
53
|
|
|
55
54
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
56
55
|
@univariate_feature
|
|
57
|
-
def signal_skewness(x, **kwargs):
|
|
56
|
+
def signal_skewness(x, /, **kwargs):
|
|
58
57
|
return stats.skew(x, axis=x.ndim - 1, **kwargs)
|
|
59
58
|
|
|
60
59
|
|
|
61
60
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
62
61
|
@univariate_feature
|
|
63
|
-
def signal_kurtosis(x, **kwargs):
|
|
62
|
+
def signal_kurtosis(x, /, **kwargs):
|
|
64
63
|
return stats.kurtosis(x, axis=x.ndim - 1, **kwargs)
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
68
67
|
@univariate_feature
|
|
69
|
-
def signal_root_mean_square(x):
|
|
68
|
+
def signal_root_mean_square(x, /):
|
|
70
69
|
return np.sqrt(np.power(x, 2).mean(axis=-1))
|
|
71
70
|
|
|
72
71
|
|
|
73
72
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
74
73
|
@univariate_feature
|
|
75
|
-
def signal_peak_to_peak(x, **kwargs):
|
|
74
|
+
def signal_peak_to_peak(x, /, **kwargs):
|
|
76
75
|
return np.ptp(x, axis=-1, **kwargs)
|
|
77
76
|
|
|
78
77
|
|
|
79
78
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
80
79
|
@univariate_feature
|
|
81
|
-
def signal_quantile(x, q: numbers.Number = 0.5, **kwargs):
|
|
80
|
+
def signal_quantile(x, /, q: numbers.Number = 0.5, **kwargs):
|
|
82
81
|
return np.quantile(x, q=q, axis=-1, **kwargs)
|
|
83
82
|
|
|
84
83
|
|
|
85
84
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
86
85
|
@univariate_feature
|
|
87
|
-
def signal_line_length(x):
|
|
86
|
+
def signal_line_length(x, /):
|
|
88
87
|
return np.abs(np.diff(x, axis=-1)).mean(axis=-1)
|
|
89
88
|
|
|
90
89
|
|
|
91
90
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
92
91
|
@univariate_feature
|
|
93
|
-
def signal_zero_crossings(x, threshold=1e-15):
|
|
92
|
+
def signal_zero_crossings(x, /, threshold=1e-15):
|
|
94
93
|
zero_ind = np.logical_and(x > -threshold, x < threshold)
|
|
95
94
|
zero_cross = np.diff(zero_ind, axis=-1).astype(int).sum(axis=-1)
|
|
96
95
|
y = x.copy()
|
|
@@ -101,13 +100,13 @@ def signal_zero_crossings(x, threshold=1e-15):
|
|
|
101
100
|
|
|
102
101
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
103
102
|
@univariate_feature
|
|
104
|
-
def signal_hjorth_mobility(x):
|
|
103
|
+
def signal_hjorth_mobility(x, /):
|
|
105
104
|
return np.diff(x, axis=-1).std(axis=-1) / x.std(axis=-1)
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
109
108
|
@univariate_feature
|
|
110
|
-
def signal_hjorth_complexity(x):
|
|
109
|
+
def signal_hjorth_complexity(x, /):
|
|
111
110
|
return (np.diff(x, 2, axis=-1).std(axis=-1) * x.std(axis=-1)) / np.diff(
|
|
112
111
|
x, axis=-1
|
|
113
112
|
).var(axis=-1)
|
|
@@ -115,7 +114,7 @@ def signal_hjorth_complexity(x):
|
|
|
115
114
|
|
|
116
115
|
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
117
116
|
@univariate_feature
|
|
118
|
-
def signal_decorrelation_time(x, fs=1):
|
|
117
|
+
def signal_decorrelation_time(x, /, fs=1):
|
|
119
118
|
f = np.fft.fft(x - x.mean(axis=-1, keepdims=True), axis=-1)
|
|
120
119
|
ac = np.fft.ifft(f.real**2 + f.imag**2, axis=-1)[..., : x.shape[-1] // 2]
|
|
121
120
|
dct = np.empty(x.shape[:-1])
|
|
@@ -3,13 +3,13 @@ import numpy as np
|
|
|
3
3
|
from scipy.signal import welch
|
|
4
4
|
|
|
5
5
|
from ..decorators import FeaturePredecessor, univariate_feature
|
|
6
|
-
from ..extractors import FeatureExtractor
|
|
7
6
|
from . import utils
|
|
7
|
+
from .signal import SIGNAL_PREDECESSORS
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"spectral_preprocessor",
|
|
11
|
+
"spectral_normalized_preprocessor",
|
|
12
|
+
"spectral_db_preprocessor",
|
|
13
13
|
"spectral_root_total_power",
|
|
14
14
|
"spectral_moment",
|
|
15
15
|
"spectral_entropy",
|
|
@@ -22,84 +22,80 @@ __all__ = [
|
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
f, p = utils.slice_freq_band(f, p, f_min=f_min, f_max=f_max)
|
|
36
|
-
return f, p
|
|
25
|
+
@FeaturePredecessor(*SIGNAL_PREDECESSORS)
|
|
26
|
+
def spectral_preprocessor(x, /, **kwargs):
|
|
27
|
+
f_min = kwargs.pop("f_min") if "f_min" in kwargs else None
|
|
28
|
+
f_max = kwargs.pop("f_max") if "f_max" in kwargs else None
|
|
29
|
+
assert "fs" in kwargs
|
|
30
|
+
kwargs["axis"] = -1
|
|
31
|
+
f, p = welch(x, **kwargs)
|
|
32
|
+
f_min, f_max = utils.get_valid_freq_band(kwargs["fs"], x.shape[-1], f_min, f_max)
|
|
33
|
+
f, p = utils.slice_freq_band(f, p, f_min=f_min, f_max=f_max)
|
|
34
|
+
return f, p
|
|
37
35
|
|
|
38
36
|
|
|
39
|
-
@FeaturePredecessor(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return (*x[:-1], x[-1] / x[-1].sum(axis=-1, keepdims=True))
|
|
37
|
+
@FeaturePredecessor(spectral_preprocessor)
|
|
38
|
+
def spectral_normalized_preprocessor(f, p, /):
|
|
39
|
+
return f, p / p.sum(axis=-1, keepdims=True)
|
|
43
40
|
|
|
44
41
|
|
|
45
|
-
@FeaturePredecessor(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return (*x[:-1], 10 * np.log10(x[-1] + eps))
|
|
42
|
+
@FeaturePredecessor(spectral_preprocessor)
|
|
43
|
+
def spectral_db_preprocessor(f, p, /, eps=1e-15):
|
|
44
|
+
return f, 10 * np.log10(p + eps)
|
|
49
45
|
|
|
50
46
|
|
|
51
|
-
@FeaturePredecessor(
|
|
47
|
+
@FeaturePredecessor(spectral_preprocessor)
|
|
52
48
|
@univariate_feature
|
|
53
|
-
def spectral_root_total_power(f, p):
|
|
49
|
+
def spectral_root_total_power(f, p, /):
|
|
54
50
|
return np.sqrt(p.sum(axis=-1))
|
|
55
51
|
|
|
56
52
|
|
|
57
|
-
@FeaturePredecessor(
|
|
53
|
+
@FeaturePredecessor(spectral_normalized_preprocessor)
|
|
58
54
|
@univariate_feature
|
|
59
|
-
def spectral_moment(f, p):
|
|
55
|
+
def spectral_moment(f, p, /):
|
|
60
56
|
return np.sum(f * p, axis=-1)
|
|
61
57
|
|
|
62
58
|
|
|
63
|
-
@FeaturePredecessor(
|
|
59
|
+
@FeaturePredecessor(spectral_preprocessor)
|
|
64
60
|
@univariate_feature
|
|
65
|
-
def spectral_hjorth_activity(f, p):
|
|
61
|
+
def spectral_hjorth_activity(f, p, /):
|
|
66
62
|
return np.sum(p, axis=-1)
|
|
67
63
|
|
|
68
64
|
|
|
69
|
-
@FeaturePredecessor(
|
|
65
|
+
@FeaturePredecessor(spectral_normalized_preprocessor)
|
|
70
66
|
@univariate_feature
|
|
71
|
-
def spectral_hjorth_mobility(f, p):
|
|
67
|
+
def spectral_hjorth_mobility(f, p, /):
|
|
72
68
|
return np.sqrt(np.sum(np.power(f, 2) * p, axis=-1))
|
|
73
69
|
|
|
74
70
|
|
|
75
|
-
@FeaturePredecessor(
|
|
71
|
+
@FeaturePredecessor(spectral_normalized_preprocessor)
|
|
76
72
|
@univariate_feature
|
|
77
|
-
def spectral_hjorth_complexity(f, p):
|
|
73
|
+
def spectral_hjorth_complexity(f, p, /):
|
|
78
74
|
return np.sqrt(np.sum(np.power(f, 4) * p, axis=-1))
|
|
79
75
|
|
|
80
76
|
|
|
81
|
-
@FeaturePredecessor(
|
|
77
|
+
@FeaturePredecessor(spectral_normalized_preprocessor)
|
|
82
78
|
@univariate_feature
|
|
83
|
-
def spectral_entropy(f, p):
|
|
79
|
+
def spectral_entropy(f, p, /):
|
|
84
80
|
idx = p > 0
|
|
85
81
|
plogp = np.zeros_like(p)
|
|
86
82
|
plogp[idx] = p[idx] * np.log(p[idx])
|
|
87
83
|
return -np.sum(plogp, axis=-1)
|
|
88
84
|
|
|
89
85
|
|
|
90
|
-
@FeaturePredecessor(
|
|
86
|
+
@FeaturePredecessor(spectral_normalized_preprocessor)
|
|
91
87
|
@univariate_feature
|
|
92
88
|
@nb.njit(cache=True, fastmath=True)
|
|
93
|
-
def spectral_edge(f, p, edge=0.9):
|
|
89
|
+
def spectral_edge(f, p, /, edge=0.9):
|
|
94
90
|
se = np.empty(p.shape[:-1])
|
|
95
91
|
for i in np.ndindex(p.shape[:-1]):
|
|
96
92
|
se[i] = f[np.searchsorted(np.cumsum(p[i]), edge)]
|
|
97
93
|
return se
|
|
98
94
|
|
|
99
95
|
|
|
100
|
-
@FeaturePredecessor(
|
|
96
|
+
@FeaturePredecessor(spectral_db_preprocessor)
|
|
101
97
|
@univariate_feature
|
|
102
|
-
def spectral_slope(f, p):
|
|
98
|
+
def spectral_slope(f, p, /):
|
|
103
99
|
log_f = np.vstack((np.log(f), np.ones(f.shape[0]))).T
|
|
104
100
|
r = np.linalg.lstsq(log_f, p.reshape(-1, p.shape[-1]).T)[0]
|
|
105
101
|
r = r.reshape(2, *p.shape[:-1])
|
|
@@ -107,10 +103,10 @@ def spectral_slope(f, p):
|
|
|
107
103
|
|
|
108
104
|
|
|
109
105
|
@FeaturePredecessor(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
spectral_preprocessor,
|
|
107
|
+
spectral_normalized_preprocessor,
|
|
108
|
+
spectral_db_preprocessor,
|
|
113
109
|
)
|
|
114
110
|
@univariate_feature
|
|
115
|
-
def spectral_bands_power(f, p, bands=utils.DEFAULT_FREQ_BANDS):
|
|
111
|
+
def spectral_bands_power(f, p, /, bands=utils.DEFAULT_FREQ_BANDS):
|
|
116
112
|
return utils.reduce_freq_bands(f, p, bands, np.sum)
|