Skip to content

Commit 33b4746

Browse files
Upgrade pyriemann imports
1 parent bf54002 commit 33b4746

5 files changed

Lines changed: 62 additions & 29 deletions

File tree

‎meegkit/asr.py‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,14 @@
22
import logging
33

44
import numpy as np
5+
import pyriemann
6+
from pyriemann.geometry.mean import gmean as mean_covariance
57
from scipy import linalg, signal
68
from statsmodels.robust.scale import mad
79

810
from .utils import block_covariance, nonlinear_eigenspace
911
from .utils.asr import fit_eeg_distribution, geometric_median, yulewalk, yulewalk_filter
1012

11-
try:
12-
import pyriemann
13-
try: # pyriemann >= 0.11 renamed mean_covariance to gmean
14-
from pyriemann.geometry.mean import gmean as mean_covariance
15-
except ImportError: # older pyriemann
16-
from pyriemann.utils.mean import mean_covariance
17-
except ImportError:
18-
pyriemann = None
19-
mean_covariance = None
20-
2113

2214
class ASR:
2315
"""Artifact Subspace Reconstruction.
@@ -106,10 +98,6 @@ def __init__(self, *, sfreq=250, cutoff=5, blocksize=100, win_len=0.5,
10698
min_clean_fraction=0.25, method="euclid", memory=None,
10799
estimator="scm", **kwargs):
108100

109-
if pyriemann is None and method == "riemann":
110-
logging.warning("Need pyriemann to use riemannian ASR flavor.")
111-
method = "euclid"
112-
113101
self.cutoff = cutoff
114102
self.blocksize = blocksize
115103
self.win_len = win_len

‎meegkit/trca.py‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44
import numpy as np
55
import scipy.linalg as linalg
66
from pyriemann.estimation import Covariances
7-
8-
try: # pyriemann >= 0.11 renamed mean_covariance to gmean
9-
from pyriemann.geometry.mean import gmean as mean_covariance
10-
except ImportError: # older pyriemann
11-
from pyriemann.utils.mean import mean_covariance
7+
from pyriemann.geometry.mean import gmean as mean_covariance
128

139
from .utils import theshapeof
1410
from .utils.trca import bandpass, schaefer_strimmer_cov

‎meegkit/utils/covariances.py‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Covariance calculation."""
22
import numpy as np
3+
from pyriemann.geometry.covariance import covariances
34
from scipy import linalg
45

56
from .base import mldivide
@@ -33,14 +34,8 @@ def block_covariance(data, window=128, overlap=0.5, padding=True, estimator="cov
3334
Block covariance.
3435
3536
"""
36-
try: # pyriemann >= 0.11 moved covariance utils to geometry.covariance
37-
from pyriemann.geometry.covariance import check_function, cov_est_functions
38-
except ImportError: # older pyriemann
39-
from pyriemann.utils.covariance import check_function, cov_est_functions
40-
4137
assert 0 <= overlap < 1, "overlap must be < 1"
42-
est = check_function(estimator, cov_est_functions)
43-
cov = []
38+
blocks = []
4439
n_chans, n_samples = data.shape
4540
if padding: # pad data with zeros
4641
pad = np.zeros((n_chans, int(window / 2)))
@@ -49,10 +44,10 @@ def block_covariance(data, window=128, overlap=0.5, padding=True, estimator="cov
4944
jump = int(window * overlap)
5045
ix = 0
5146
while (ix + window < n_samples):
52-
cov.append(est(data[:, ix:ix + window]))
47+
blocks.append(data[:, ix:ix + window])
5348
ix = ix + jump
5449

55-
return np.array(cov)
50+
return covariances(np.array(blocks), estimator=estimator)
5651

5752

5853
def cov_lags(X, Y, shifts=None):

‎requirements.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ pandas
66
joblib
77
tqdm
88
statsmodels
9-
pyriemann>=0.7
9+
pyriemann>=0.12

‎tests/test_pyriemann_imports.py‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""pyriemann API compatibility tests."""
2+
from pathlib import Path
3+
4+
import numpy as np
5+
6+
from meegkit.utils.covariances import block_covariance
7+
8+
ROOT = Path(__file__).resolve().parents[1]
9+
10+
11+
def _version_tuple(version):
12+
return tuple(int(part) for part in version.split("."))
13+
14+
15+
def test_pyriemann_lower_bound_uses_geometry_api():
16+
"""Ensure the supported pyriemann range has the geometry namespace."""
17+
requirement = next(
18+
line
19+
for line in (ROOT / "requirements.txt").read_text().splitlines()
20+
if line.startswith("pyriemann")
21+
)
22+
23+
_, lower_bound = requirement.split(">=")
24+
25+
assert _version_tuple(lower_bound) >= (0, 12)
26+
27+
28+
def test_pyriemann_imports_use_modern_public_api():
29+
"""Keep deprecated pyriemann utils imports out of meegkit."""
30+
source_files = [
31+
ROOT / "meegkit" / "asr.py",
32+
ROOT / "meegkit" / "trca.py",
33+
ROOT / "meegkit" / "utils" / "covariances.py",
34+
]
35+
source = "\n".join(path.read_text() for path in source_files)
36+
37+
assert "pyriemann.utils.mean" not in source
38+
assert "pyriemann.utils.covariance" not in source
39+
assert "check_function" not in source
40+
assert "cov_est_functions" not in source
41+
42+
43+
def test_block_covariance_uses_pyriemann_covariances():
44+
"""Compare block covariance with pyriemann's vectorized covariance helper."""
45+
from pyriemann.geometry.covariance import covariances
46+
47+
data = 0.1 + 0.01 * (1 + np.arange(24, dtype=float).reshape(3, 8))
48+
expected_blocks = np.array(
49+
[data[:, start:start + 4] for start in range(0, 4, 2)]
50+
)
51+
52+
actual = block_covariance(data, window=4, overlap=0.5, padding=False)
53+
54+
np.testing.assert_allclose(actual, covariances(expected_blocks, estimator="cov"))

0 commit comments

Comments
 (0)