Repository: mne-tools/mne-python

Issue:
confusing (wrong?) description in mne.time_frequency.tfr_array_multitaper
The description of mne.time_frequency.tfr_array_multitaper is:

> Compute time-frequency transforms using wavelets and multitaper windows.
> Uses Morlet wavelets windowed with multiple DPSS tapers.

However it is not Morlet wavelet that are windowed by DPSS tapers. In _make_dpss, which is called in _compute_tfr, which is called in tfr_array_multitaper, there is:

            oscillation = np.exp(2.0 * 1j * np.pi * f * (t - t_win / 2.))

            # Get dpss tapers
            tapers, conc = dpss_windows(t.shape[0], time_bandwidth / 2.,
                                        n_taps)

            Wk = oscillation * tapers[m]

This is a DPSS taper multiplied by a complex exponential. A Morlet wavelet is a Gaussian multiplied by a complex exponential. There is no Gaussian and thus no Morlet wavelet in the code from what I can tell, unless I misunderstood something.


Discussion:
indeed it seems very wrong... at least the doc does not match the code...

what fix would you suggest?

>

Ideally would be great to have the actual mathematical formula. I feel it is often missing in the MNE documentation (I'm new to MNE). However, if one does not want to go into that, one could just write the same as for mne.time_frequency.tfr_multitaper

> Compute Time-Frequency Representation (TFR) using DPSS tapers.

Since from what I understand these 2 functions do the same, but just have a different type of input. tfr_multitaper takes as input epochs whereas tfr_array_multitaper takes NumPy array. It would be helpful to specify that fact in the description (if that's actually the case).

The only difference is that tfr_multitaper has the argument zero_mean but tfr_array_multitaper doesn't. I wonder why that is? It seems to be present in tfr_morlet and tfr_array_morlet.

Best might be to have some consistency between the descriptions of:
tfr_multitaper vs tfr_array_multitaper
tfr_morlet vs tfr_array_morlet
tfr_stockwell vs tfr_array_stockwell

I'm new to all this code and MNE, so I don't have a comprehensive overview of the code at this point...
+1 on all you suggest.

so let's update the docstrings to match what the code actually does

> The only difference is that tfr_multitaper has the argument zero_mean but
tfr_array_multitaper doesn't. I wonder why that is? It seems to be present
in tfr_morlet and tfr_array_morlet.

this is an API fix to do. Yes the idea is that tfr_ and tfr_array
functions are the same except
one works with mne objects and the other with numpy arrays

can you send us a PR?

thanks a lot for your help

>


Resolving patch:
diff --git a/mne/time_frequency/_stockwell.py b/mne/time_frequency/_stockwell.py
--- a/mne/time_frequency/_stockwell.py
+++ b/mne/time_frequency/_stockwell.py
@@ -103,7 +103,11 @@ def tfr_array_stockwell(data, sfreq, fmin=None, fmax=None, n_fft=None,
                         width=1.0, decim=1, return_itc=False, n_jobs=1):
     """Compute power and intertrial coherence using Stockwell (S) transform.
 
-    See [1]_, [2]_, [3]_, [4]_ for more information.
+    Same computation as `~mne.time_frequency.tfr_stockwell`, but operates on
+    :class:`NumPy arrays <numpy.ndarray>` instead of `~mne.Epochs` objects.
+
+    See :footcite:`Stockwell2007,MoukademEtAl2014,WheatEtAl2010,JonesEtAl2006`
+    for more information.
 
     Parameters
     ----------
@@ -149,23 +153,7 @@ def tfr_array_stockwell(data, sfreq, fmin=None, fmax=None, n_fft=None,
 
     References
     ----------
-    .. [1] Stockwell, R. G. "Why use the S-transform." AMS Pseudo-differential
-       operators: Partial differential equations and time-frequency
-       analysis 52 (2007): 279-309.
-    .. [2] Moukadem, A., Bouguila, Z., Abdeslam, D. O, and Dieterlen, A.
-       Stockwell transform optimization applied on the detection of split in
-       heart sounds (2014). Signal Processing Conference (EUSIPCO), 2013
-       Proceedings of the 22nd European, pages 2015--2019.
-    .. [3] Wheat, K., Cornelissen, P. L., Frost, S.J, and Peter C. Hansen
-       (2010). During Visual Word Recognition, Phonology Is Accessed
-       within 100 ms and May Be Mediated by a Speech Production
-       Code: Evidence from Magnetoencephalography. The Journal of
-       Neuroscience, 30 (15), 5229-5233.
-    .. [4] K. A. Jones and B. Porjesz and D. Chorlian and M. Rangaswamy and C.
-       Kamarajan and A. Padmanabhapillai and A. Stimus and H. Begleiter
-       (2006). S-transform time-frequency analysis of P300 reveals deficits in
-       individuals diagnosed with alcoholism.
-       Clinical Neurophysiology 117 2128--2143
+    .. footbibliography::
     """
     _validate_type(data, np.ndarray, 'data')
     if data.ndim != 3:
@@ -207,7 +195,13 @@ def tfr_array_stockwell(data, sfreq, fmin=None, fmax=None, n_fft=None,
 def tfr_stockwell(inst, fmin=None, fmax=None, n_fft=None,
                   width=1.0, decim=1, return_itc=False, n_jobs=1,
                   verbose=None):
-    """Time-Frequency Representation (TFR) using Stockwell Transform.
+    """Compute Time-Frequency Representation (TFR) using Stockwell Transform.
+
+    Same computation as `~mne.time_frequency.tfr_array_stockwell`, but operates
+    on `~mne.Epochs` objects instead of :class:`NumPy arrays <numpy.ndarray>`.
+
+    See :footcite:`Stockwell2007,MoukademEtAl2014,WheatEtAl2010,JonesEtAl2006`
+    for more information.
 
     Parameters
     ----------
@@ -251,6 +245,10 @@ def tfr_stockwell(inst, fmin=None, fmax=None, n_fft=None,
     Notes
     -----
     .. versionadded:: 0.9.0
+
+    References
+    ----------
+    .. footbibliography::
     """
     # verbose dec is used b/c subfunctions are verbose
     data = _get_data(inst, return_itc)
diff --git a/mne/time_frequency/multitaper.py b/mne/time_frequency/multitaper.py
--- a/mne/time_frequency/multitaper.py
+++ b/mne/time_frequency/multitaper.py
@@ -458,9 +458,10 @@ def tfr_array_multitaper(epoch_data, sfreq, freqs, n_cycles=7.0,
                          zero_mean=True, time_bandwidth=None, use_fft=True,
                          decim=1, output='complex', n_jobs=1,
                          verbose=None):
-    """Compute time-frequency transforms using wavelets and multitaper windows.
+    """Compute Time-Frequency Representation (TFR) using DPSS tapers.
 
-    Uses Morlet wavelets windowed with multiple DPSS tapers.
+    Same computation as `~mne.time_frequency.tfr_multitaper`, but operates on
+    :class:`NumPy arrays <numpy.ndarray>` instead of `~mne.Epochs` objects.
 
     Parameters
     ----------
@@ -471,7 +472,7 @@ def tfr_array_multitaper(epoch_data, sfreq, freqs, n_cycles=7.0,
     freqs : array-like of float, shape (n_freqs,)
         The frequencies.
     n_cycles : float | array of float
-        Number of cycles  in the Morlet wavelet. Fixed number or one per
+        Number of cycles in the wavelet. Fixed number or one per
         frequency. Defaults to 7.0.
     zero_mean : bool
         If True, make sure the wavelets have a mean of zero. Defaults to True.
diff --git a/mne/time_frequency/tfr.py b/mne/time_frequency/tfr.py
--- a/mne/time_frequency/tfr.py
+++ b/mne/time_frequency/tfr.py
@@ -283,10 +283,10 @@ def _compute_tfr(epoch_data, freqs, sfreq=1.0, method='morlet',
         Sampling frequency of the data.
     method : 'multitaper' | 'morlet', default 'morlet'
         The time-frequency method. 'morlet' convolves a Morlet wavelet.
-        'multitaper' uses Morlet wavelets windowed with multiple DPSS
-        multitapers.
+        'multitaper' uses complex exponentials windowed with multiple DPSS
+        tapers.
     n_cycles : float | array of float, default 7.0
-        Number of cycles  in the Morlet wavelet. Fixed number
+        Number of cycles in the wavelet. Fixed number
         or one per frequency.
     zero_mean : bool | None, default None
         None means True for method='multitaper' and False for method='morlet'.
@@ -660,6 +660,10 @@ def tfr_morlet(inst, freqs, n_cycles, use_fft=False, return_itc=True, decim=1,
                output='power', verbose=None):
     """Compute Time-Frequency Representation (TFR) using Morlet wavelets.
 
+    Same computation as `~mne.time_frequency.tfr_array_morlet`, but
+    operates on `~mne.Epochs` objects instead of
+    :class:`NumPy arrays <numpy.ndarray>`.
+
     Parameters
     ----------
     inst : Epochs | Evoked
@@ -722,9 +726,10 @@ def tfr_morlet(inst, freqs, n_cycles, use_fft=False, return_itc=True, decim=1,
 def tfr_array_morlet(epoch_data, sfreq, freqs, n_cycles=7.0,
                      zero_mean=False, use_fft=True, decim=1, output='complex',
                      n_jobs=1, verbose=None):
-    """Compute time-frequency transform using Morlet wavelets.
+    """Compute Time-Frequency Representation (TFR) using Morlet wavelets.
 
-    Convolves epoch data with selected Morlet wavelets.
+    Same computation as `~mne.time_frequency.tfr_morlet`, but operates on
+    :class:`NumPy arrays <numpy.ndarray>` instead of `~mne.Epochs` objects.
 
     Parameters
     ----------
@@ -798,6 +803,10 @@ def tfr_multitaper(inst, freqs, n_cycles, time_bandwidth=4.0,
                    n_jobs=1, picks=None, average=True, verbose=None):
     """Compute Time-Frequency Representation (TFR) using DPSS tapers.
 
+    Same computation as `~mne.time_frequency.tfr_array_multitaper`, but
+    operates on `~mne.Epochs` objects instead of
+    :class:`NumPy arrays <numpy.ndarray>`.
+
     Parameters
     ----------
     inst : Epochs | Evoked


Test patch: