Repository: mne-tools/mne-python

Issue:
No amplitude from evoked.get_peak()
Wouldn't it be logical for `evoked.get_peak()` to also return the max amplitude? 
I coded this myself into a script that uses `get_peak()`, but I had to:

-arrange a new data matrix and channel names according to the pair grads logic
-find the index of the channel with maximum amplitude

`get_peak()` would already do these for free. However returning the amplitude would break API, as a 3-tuple of outputs would be needed.




Discussion:
We can add a return_amplitude to avoid API breakage

Yep. Then the same would also be needed for the underlying machinery of `_get_peak()`.


`_get_peak` is private you can modify the API without adding an argument
Ahh, of course.
But in any case, +1 from me for this ENH
no objection

Ok, I will try to cook a relevant PR.

Resolving patch:
diff --git a/mne/evoked.py b/mne/evoked.py
--- a/mne/evoked.py
+++ b/mne/evoked.py
@@ -537,7 +537,8 @@ def __neg__(self):
         return out
 
     def get_peak(self, ch_type=None, tmin=None, tmax=None,
-                 mode='abs', time_as_index=False, merge_grads=False):
+                 mode='abs', time_as_index=False, merge_grads=False,
+                 return_amplitude=False):
         """Get location and latency of peak amplitude.
 
         Parameters
@@ -561,7 +562,11 @@ def get_peak(self, ch_type=None, tmin=None, tmax=None,
             Whether to return the time index instead of the latency in seconds.
         merge_grads : bool
             If True, compute peak from merged gradiometer data.
+        return_amplitude : bool
+            If True, return also the amplitude at the maximum response.
 
+            .. versionadded:: 0.16
+            
         Returns
         -------
         ch_name : str
@@ -569,6 +574,11 @@ def get_peak(self, ch_type=None, tmin=None, tmax=None,
         latency : float | int
             The time point of the maximum response, either latency in seconds
             or index.
+        amplitude : float
+            The amplitude of the maximum response. Only returned if
+            return_amplitude is True.
+        
+            .. versionadded:: 0.16
         """
         supported = ('mag', 'grad', 'eeg', 'seeg', 'ecog', 'misc', 'hbo',
                      'hbr', 'None')
@@ -630,10 +640,16 @@ def get_peak(self, ch_type=None, tmin=None, tmax=None,
             data = _merge_grad_data(data)
             ch_names = [ch_name[:-1] + 'X' for ch_name in ch_names[::2]]
 
-        ch_idx, time_idx = _get_peak(data, self.times, tmin,
-                                     tmax, mode)
-        return (ch_names[ch_idx],
-                time_idx if time_as_index else self.times[time_idx])
+        ch_idx, time_idx, max_amp = _get_peak(data, self.times, tmin,
+                                              tmax, mode)
+
+        out = (ch_names[ch_idx], time_idx if time_as_index else
+               self.times[time_idx])
+
+        if return_amplitude:
+            out += (max_amp,)
+
+        return out
 
 
 def _check_decim(info, decim, offset):
@@ -1252,6 +1268,8 @@ def _get_peak(data, times, tmin=None, tmax=None, mode='abs'):
         The index of the feature with the maximum value.
     max_time : int
         The time point of the maximum response, index.
+    max_amp : float
+        Amplitude of the maximum response.
     """
     modes = ('abs', 'neg', 'pos')
     if mode not in modes:
@@ -1293,4 +1311,4 @@ def _get_peak(data, times, tmin=None, tmax=None, mode='abs'):
 
     max_loc, max_time = np.unravel_index(maxfun(masked_index), data.shape)
 
-    return max_loc, max_time
+    return max_loc, max_time, data[max_loc, max_time]
diff --git a/mne/source_estimate.py b/mne/source_estimate.py
--- a/mne/source_estimate.py
+++ b/mne/source_estimate.py
@@ -1556,7 +1556,7 @@ def get_peak(self, hemi=None, tmin=None, tmax=None, mode='abs',
         vertno = {'lh': self.lh_vertno, 'rh': self.rh_vertno,
                   None: np.concatenate(self.vertices)}[hemi]
 
-        vert_idx, time_idx = _get_peak(data, self.times, tmin, tmax, mode)
+        vert_idx, time_idx, _ = _get_peak(data, self.times, tmin, tmax, mode)
 
         return (vert_idx if vert_as_index else vertno[vert_idx],
                 time_idx if time_as_index else self.times[time_idx])
@@ -1848,8 +1848,8 @@ def get_peak(self, tmin=None, tmax=None, mode='abs',
         latency : float
             The latency in seconds.
         """
-        vert_idx, time_idx = _get_peak(self.data, self.times, tmin, tmax,
-                                       mode)
+        vert_idx, time_idx, _ = _get_peak(self.data, self.times, tmin, tmax,
+                                          mode)
 
         return (vert_idx if vert_as_index else self.vertices[vert_idx],
                 time_idx if time_as_index else self.times[time_idx])


Test patch:
diff --git a/mne/tests/test_evoked.py b/mne/tests/test_evoked.py
--- a/mne/tests/test_evoked.py
+++ b/mne/tests/test_evoked.py
@@ -334,10 +334,12 @@ def test_get_peak():
     assert_true(ch_name in evoked.ch_names)
     assert_true(time_idx in evoked.times)
 
-    ch_name, time_idx = evoked.get_peak(ch_type='mag',
-                                        time_as_index=True)
+    ch_name, time_idx, max_amp = evoked.get_peak(ch_type='mag',
+                                                 time_as_index=True,
+                                                 return_amplitude=True)
     assert_true(time_idx < len(evoked.times))
     assert_equal(ch_name, 'MEG 1421')
+    assert_allclose(max_amp, 7.17057e-13, rtol=1e-5)
 
     assert_raises(ValueError, evoked.get_peak, ch_type='mag', merge_grads=True)
     ch_name, time_idx = evoked.get_peak(ch_type='grad', merge_grads=True)
@@ -348,17 +350,20 @@ def test_get_peak():
 
     times = np.array([.1, .2, .3])
 
-    ch_idx, time_idx = _get_peak(data, times, mode='abs')
+    ch_idx, time_idx, max_amp = _get_peak(data, times, mode='abs')
     assert_equal(ch_idx, 1)
     assert_equal(time_idx, 1)
+    assert_allclose(max_amp, -3.)
 
-    ch_idx, time_idx = _get_peak(data * -1, times, mode='neg')
+    ch_idx, time_idx, max_amp = _get_peak(data * -1, times, mode='neg')
     assert_equal(ch_idx, 0)
     assert_equal(time_idx, 2)
+    assert_allclose(max_amp, -2.)
 
-    ch_idx, time_idx = _get_peak(data, times, mode='pos')
+    ch_idx, time_idx, max_amp = _get_peak(data, times, mode='pos')
     assert_equal(ch_idx, 0)
     assert_equal(time_idx, 2)
+    assert_allclose(max_amp, 2.)
 
     assert_raises(ValueError, _get_peak, data + 1e3, times, mode='neg')
     assert_raises(ValueError, _get_peak, data - 1e3, times, mode='pos')