Skip to content

Commit

Permalink
minimum/maximum value of the all-positive/negative data (mne-tools#12383
Browse files Browse the repository at this point in the history
)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Larson <larson.eric.d@gmail.com>
  • Loading branch information
3 people authored Jan 24, 2024
1 parent 71faac9 commit 03d78f4
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 4 deletions.
1 change: 1 addition & 0 deletions doc/changes/devel/12383.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ability to detect minima peaks found in :class:`mne.Evoked` if data is all positive and maxima if data is all negative.
27 changes: 23 additions & 4 deletions mne/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,8 @@ def get_peak(
time_as_index=False,
merge_grads=False,
return_amplitude=False,
*,
strict=True,
):
"""Get location and latency of peak amplitude.
Expand Down Expand Up @@ -941,6 +943,12 @@ def get_peak(
If True, return also the amplitude at the maximum response.
.. versionadded:: 0.16
strict : bool
If True, raise an error if values are all positive when detecting
a minimum (mode='neg'), or all negative when detecting a maximum
(mode='pos'). Defaults to True.
.. versionadded:: 1.7
Returns
-------
Expand Down Expand Up @@ -1032,7 +1040,14 @@ def get_peak(
data, _ = _merge_ch_data(data, ch_type, [])
ch_names = [ch_name[:-1] + "X" for ch_name in ch_names[::2]]

ch_idx, time_idx, max_amp = _get_peak(data, self.times, tmin, tmax, mode)
ch_idx, time_idx, max_amp = _get_peak(
data,
self.times,
tmin,
tmax,
mode,
strict=strict,
)

out = (ch_names[ch_idx], time_idx if time_as_index else self.times[time_idx])

Expand Down Expand Up @@ -1949,7 +1964,7 @@ def _write_evokeds(fname, evoked, check=True, *, on_mismatch="raise", overwrite=
end_block(fid, FIFF.FIFFB_MEAS)


def _get_peak(data, times, tmin=None, tmax=None, mode="abs"):
def _get_peak(data, times, tmin=None, tmax=None, mode="abs", *, strict=True):
"""Get feature-index and time of maximum signal from 2D array.
Note. This is a 'getter', not a 'finder'. For non-evoked type
Expand All @@ -1970,6 +1985,10 @@ def _get_peak(data, times, tmin=None, tmax=None, mode="abs"):
values will be considered. If 'neg' only negative values will
be considered. If 'abs' absolute values will be considered.
Defaults to 'abs'.
strict : bool
If True, raise an error if values are all positive when detecting
a minimum (mode='neg'), or all negative when detecting a maximum
(mode='pos'). Defaults to True.
Returns
-------
Expand Down Expand Up @@ -2008,12 +2027,12 @@ def _get_peak(data, times, tmin=None, tmax=None, mode="abs"):

maxfun = np.argmax
if mode == "pos":
if not np.any(data[~mask] > 0):
if strict and not np.any(data[~mask] > 0):
raise ValueError(
"No positive values encountered. Cannot " "operate in pos mode."
)
elif mode == "neg":
if not np.any(data[~mask] < 0):
if strict and not np.any(data[~mask] < 0):
raise ValueError(
"No negative values encountered. Cannot " "operate in neg mode."
)
Expand Down
18 changes: 18 additions & 0 deletions mne/tests/test_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,24 @@ def test_get_peak():
with pytest.raises(ValueError, match="No positive values"):
evoked_all_neg.get_peak(mode="pos")

# Test finding minimum and maximum values
evoked_all_neg_outlier = evoked_all_neg.copy()
evoked_all_pos_outlier = evoked_all_pos.copy()

# Add an outlier to the data
evoked_all_neg_outlier.data[0, 15] = -1e-20
evoked_all_pos_outlier.data[0, 15] = 1e-20

ch_name, time_idx, max_amp = evoked_all_neg_outlier.get_peak(
mode="pos", return_amplitude=True, strict=False
)
assert max_amp == -1e-20

ch_name, time_idx, min_amp = evoked_all_pos_outlier.get_peak(
mode="neg", return_amplitude=True, strict=False
)
assert min_amp == 1e-20

# Test interaction between `mode` and `tmin` / `tmax`
# For the test, create an Evoked where half of the values are negative
# and the rest is positive
Expand Down

0 comments on commit 03d78f4

Please sign in to comment.