Skip to content

Commit 01fe3b4

Browse files
sappelhoffnbara
authored andcommitted
[FIX] ASR: reuse full SHAPE_RANGE grid in clean_windows (include beta=3.5)
clean_windows hardcoded np.arange(1.7, 3.5, 0.15), a 12-point grid that excludes the beta=3.5 endpoint, overriding the correct 13-point SHAPE_RANGE that fit_eeg_distribution defaults to. Reuse SHAPE_RANGE so the KL grid search can select beta=3.5.
1 parent 1fcfbfd commit 01fe3b4

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

‎meegkit/asr.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
from statsmodels.robust.scale import mad
99

1010
from .utils import block_covariance, nonlinear_eigenspace
11-
from .utils.asr import fit_eeg_distribution, geometric_median, yulewalk, yulewalk_filter
11+
from .utils.asr import (
12+
SHAPE_RANGE,
13+
fit_eeg_distribution,
14+
geometric_median,
15+
yulewalk,
16+
yulewalk_filter,
17+
)
1218

1319

1420
class ASR:
@@ -324,7 +330,7 @@ def clean_windows(X, sfreq, max_bad_chans=0.2, zthresholds=[-3.5, 5],
324330
# set internal variables
325331
truncate_quant = [0.0220, 0.6000]
326332
step_sizes = [0.01, 0.01]
327-
shape_range = np.arange(1.7, 3.5, 0.15)
333+
shape_range = SHAPE_RANGE
328334
max_bad_chans = np.round(X.shape[0] * max_bad_chans)
329335

330336
# set data indices

‎tests/test_asr.py‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""ASR test."""
22
import inspect
33
import os
4+
from unittest.mock import patch
45

56
import matplotlib.pyplot as plt
67
import numpy as np
78
import pytest
89
from scipy import signal
910

1011
from meegkit.asr import ASR, asr_calibrate, asr_process, clean_windows
11-
from meegkit.utils.asr import yulewalk, yulewalk_filter
12+
from meegkit.utils.asr import SHAPE_RANGE, fit_eeg_distribution, yulewalk, yulewalk_filter
1213
from meegkit.utils.matrix import sliding_window
1314

1415
# Data files
@@ -276,6 +277,44 @@ def test_asr_class(method, reref, show=False):
276277
ASR(sfreq=125)
277278
ASR(Sfreq=150)
278279

280+
def test_clean_windows_uses_full_shape_range():
281+
"""clean_windows passes the full 13-point SHAPE_RANGE (incl beta=3.5) down.
282+
283+
Spies on the shape_range argument to fit_eeg_distribution (6th positional).
284+
"""
285+
raw = np.load(os.path.join(THIS_FOLDER, "data", "eeg_raw.npy"))
286+
sfreq = 250
287+
288+
with patch(
289+
"meegkit.asr.fit_eeg_distribution", side_effect=fit_eeg_distribution
290+
) as mock_fit:
291+
clean_windows(raw, sfreq)
292+
293+
assert mock_fit.called
294+
captured = mock_fit.call_args.args[5]
295+
assert len(captured) == 13
296+
assert np.isclose(captured[-1], 3.5)
297+
assert np.allclose(captured, SHAPE_RANGE)
298+
299+
300+
def test_fit_eeg_distribution_shape_range_materiality():
301+
"""Dropping the beta=3.5 endpoint changes the selected shape parameter.
302+
303+
A bounded, near-uniform sample drives the grid search to the largest beta.
304+
"""
305+
# Bounded, near-uniform sample: favors the largest available beta.
306+
Y = np.linspace(-1, 1, 2000)
307+
308+
shape_range_trunc = np.arange(1.7, 3.5, 0.15)
309+
310+
beta_full = fit_eeg_distribution(Y, shape_range=SHAPE_RANGE)[3]
311+
beta_trunc = fit_eeg_distribution(Y, shape_range=shape_range_trunc)[3]
312+
313+
assert np.isclose(beta_full, 3.5)
314+
assert beta_trunc <= np.max(shape_range_trunc) + 1e-9
315+
assert beta_full > beta_trunc
316+
317+
279318
if __name__ == "__main__":
280319
pytest.main([__file__])
281320
# test_yulewalk(250, True)

0 commit comments

Comments
 (0)