Help understanding TFR n_cycles

  • MNE version: e.g. 1.4.2
  • Platform Linux-5.15.90.2-microsoft-standard-WSL2-x86_64-with-glibc2.31
    Python 3.10.10 | packaged by conda-forge | (main, Mar 24 2023, 20:08:06) [GCC 11.3.0]

Hello! I am in the process of writing a script to recreate the newtimef() function from the EEGLab toolbox in mne.

The function is designed to compute the spectral perturbation (ERSP) and inter-trial coherence (ITC) across epochs.

To do this I am using the mne.time_frequency.tfr_morlet() function. My epochs are 3 seconds long and my sampling rate is 1000 Hz.
The frequencies I am using are: np.logspace(*np.log10([1,30]), 30)
The number of cycles I am using are: np.linspace(1, 15, 30) based on this formula from EEGLab.

When I run the function I get the following error:
ValueError: At least one of the wavelets (3069) is longer than the signal (3001). Consider using a longer signal or shorter wavelets.

My question is how is this 3069 value calculated? My understanding was that this value is based on the formula:

So I do not understand how my wavelets are greater than my epochs.

However, I may be mistaken. I would appreciate any help with this.

Thank you!
Jacob

I understand your confusion, I had to dig into the code to see what is actually going on.

A wavelet is theoretically of infinite length. But, depending on the number of cycles, it will quickly approach 0 on both ends:

MNE-Python is pretty conservative when it comes to where to “cut” the infinite wavelet to a certain length where we say “this is close enough to zero”. The formula for this is not n_cycles/freqs, but rather 10 * n_cycles / (2 * np.pi * freqs), give or take a little depending on where the exact samples fall.

So, you could either cut epochs a little bit wider, do the tfr_morlet, and then cut the resulting TFR back to the desired length. Or, you can turn on use_fft=True, which performs the wavelet transform in Fourier space, thus sidestepping the wavelet window length issue, turning the error into a warning.

3 Likes

Thank you! This was really helpful @wmvanvliet! Would you mind telling what the n_cycles/freqs value is then? Also, I am still a bit confused how the 3069 samples was calculated. I assume I would just multiply my sampling rate by 10 * n_cycles / (2 * np.pi * freqs)

I don’t know where the n_cycles/freqs comes from (I’m not well versed in wavelet analysis), my guess would be something to do with the expected time resolution of the TFR(?)

10 * n_cycles / (2 * np.pi * freqs) * epochs.info['sfreq'] should get you close to the 3069 samples. If you want it more exact than that, I must refer you to the source code itself: https://github.com/mne-tools/mne-python/blob/main/mne/time_frequency/tfr.py#L72

I think the right way to think about “temporal window length” is that it is the temporal resolution (or conversely the amount of temporal smearing). It determines how precisely in time you’ll be able to localize an event. It is not actually reflective of the length of the morlet wavelet returned by our morlet function / used in our tfr_morlet function.

@withmywoessner if you have an idea of how we could clarify the documentation around this, please make a PR!