Time-Frequency Analysis with Morlet Wavelets
Author
import numpy as np
import mne
import matplotlib.pyplot as pltEEG and MEG signals are non-stationary: their frequency content changes over time. To capture these dynamics, we need methods that provide both time and frequency information simultaneously. In this session we will use Morlet wavelets — short, frequency-specific filters — to decompose signals into their time-varying frequency components. We will start by constructing wavelets and understanding their parameters, then work with simulated data to apply them via convolution to extract instantaneous power at a given frequency, and finally use MNE’s compute_tfr to compute full time-frequency representations (TFRs) of epoched data. Once you understood how to compute and interpret the TFR, you’ll apply that knowledge to a real EEG dataset to investigate recordings from a motor-imagery task that is common in the brain-computer interface literature.
Setup
Utility Functions
def _plot_wavelets(wavelets, freqs, n_cycles, fs):
wavelets = wavelets if isinstance(wavelets, list) else [wavelets]
freqs = np.atleast_1d(freqs)
n_cycles = np.atleast_1d(n_cycles)
if len(n_cycles) == 1:
n_cycles = np.repeat(n_cycles, len(wavelets))
_, ax = plt.subplots(1, 2, figsize=(10, 4), layout="constrained")
for wavelet, center_freq, n in zip(wavelets, freqs, n_cycles):
n_samples = len(wavelet)
t = np.arange(n_samples) / fs - (n_samples - 1) / (2 * fs)
ax[0].plot(t, wavelet.real, label=f"{center_freq} Hz, {n} cycles")
fft_freqs = np.fft.fftfreq(n_samples, 1 / fs)
spectrum = np.abs(np.fft.fft(wavelet))
positive = fft_freqs >= 0
ax[1].plot(fft_freqs[positive], spectrum[positive])
ax[1].axvline(center_freq, color="gray", linestyle="--", linewidth=1)
ax[0].set_xlabel("Time [s]")
ax[0].set_ylabel("Amplitude [a.u.]")
ax[1].set_xlabel("Frequency [Hz]")
ax[1].set_ylabel("Magnitude [a.u.]")
ax[0].legend()
def _simulate_signal(dur, fs, freq, start, stop, noise_level=0.1):
freqs = np.atleast_1d(freq)
starts = np.atleast_1d(start)
stops = np.atleast_1d(stop)
rng = np.random.RandomState(42)
data = rng.randn(int(dur * fs)) * noise_level
t = np.linspace(0, dur, int(dur * fs))
for freq, s0, s1 in zip(freqs, starts, stops):
signal = np.sin(np.pi * 2.0 * freq * t)
signal[np.logical_or(t < s0, t > s1)] = 0.0
on_time = np.logical_and(t >= s0, t <= s1)
signal[on_time] *= np.hanning(on_time.sum())
data += signal
return t, data
def _simulate_epochs(fs, freq, start, stop, n_epochs, noise_level=0.1, tmin=0, tmax=1):
dur = tmax - tmin + 1 / fs # extra sample so tmax fits within the segment
start_rel = np.atleast_1d(start) - tmin # convert to segment-relative time
stop_rel = np.atleast_1d(stop) - tmin
segments = [_simulate_signal(dur, fs, freq, start_rel, stop_rel, noise_level)[1] for _ in range(n_epochs)]
all_data = np.concatenate(segments)[np.newaxis, :]
info = mne.create_info(ch_names=["CH1"], sfreq=fs, ch_types="eeg")
raw = mne.io.RawArray(all_data, info)
events = np.zeros((n_epochs, 3), dtype=int)
event_offset = int(max(0, -tmin) * fs)
events[:, 0] = np.arange(n_epochs) * int(dur * fs) + event_offset
epochs = mne.Epochs(raw, events, tmin=tmin, tmax=tmax, baseline=None)
return epochs
class utils:
plot_wavelets = _plot_wavelets
simulate_signal = _simulate_signal
simulate_epochs = _simulate_epochsSection 1: Creating and Visualizing Morlet Wavelets
Background
To analyze the time-varying frequency content, we need filters that isolate specific frequency bands with high spectral and temporal precision. One commonly used type of filters are Morlet wavelets which are obtained by multiplying a (complex) sinusoid with a Gaussian envelope. A Morlet wavelet has two key parameters: its frequency and the number of cycles within the Gaussian envelope.
Together, these control the time-frequency tradeoff: more cycles produce a longer wavelet with narrower bandwidth (better frequency resolution, worse temporal resolution), while fewer cycles produce a shorter wavelet with broader bandwidth (better temporal resolution, worse frequency resolution). When keeping the number of cycles constant while increasing the wavelet’s frequency, the wavelet will get shorter (because each cycle takes less time) and the frequency resolution decreases. This can be compensated by proportionally increasing the number of cycles with the wavelet’s frequency.
Like any signal, wavelets are subject to the Nyquist theorem which says that the highest representable frequency is half of the sampling rate. In practice however, it is recommended to stay below one third or fourth of the sampling rate to avoid aliasing artifacts.
Exercises
In this section, you are going to use MNE’s morlet() function to generate wavelets of varying frequency and cycle number. By using the custom function utils.plot_wavelets() you’ll be able to see the tradeoffs that are part of any time-frequency analysis.
| Code | Description |
|---|---|
mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=7) |
Create a Morlet wavelet at 10 Hz with 7 cycles |
mne.time_frequency.morlet(sfreq=fs, freqs=[5, 10, 20], n_cycles=7) |
Create wavelets at multiple frequencies with a fixed number of cycles |
mne.time_frequency.morlet(sfreq=fs, freqs=freqs, n_cycles=freqs/2) |
Scale n_cycles proportionally to frequency to keep bandwidth constant |
utils.plot_wavelets(wavelet, freqs, n_cycles, fs) |
Plot wavelet(s) in the time domain (left) and frequency domain (right) |
Example: Create a Morlet wavelet with 3 cycles and a frequency of 5 Hz at a sampling rate of 125 Hz. Plot it using utils.plot_wavelets — the left panel shows the wavelet in the time domain and the right panel shows its magnitude spectrum.
fs = 125
freq = 5
n_cycles = 3
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Exercise: Create the same wavelet but increase the number of cycles to 6. How did increasing the number of cycles affect the length of the wavelet and its magnitude spectrum?
Solution
fs = 125
freq = 5
n_cycles = 6
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Exercise: Create the same wavelet but increase the frequency to 15 Hz. How did increasing the frequency affect the length of the wavelet and its magnitude spectrum?
Solution
fs = 125
freq = 15
n_cycles = 6
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Example: Generate wavelets for an array of frequencies and plot them. With a constant number of cycles, the spectral bandwidth increases with frequency as the wavelet gets shorter.
fs = 125
freqs = np.array([5, 10, 15, 20])
n_cycles = 6
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freqs, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freqs, n_cycles, fs)Exercise: Generate wavelets for the same freqs but set the number of cycles to freqs/2 to scale the number of cycles with the frequency. How does this affect the spectral resolution?
Solution
fs = 125
freq = np.array([5, 10, 15, 20])
n_cycles = freq/2
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Example: Plot wavelets for 40, 60 and 80 Hz at a sampling rate of 125 Hz. Notice how the wavelets are increasingly distorted as they approach and cross the Nyquist frequency which is half of the sampling rate.
fs = 125
freq = np.array([40, 60, 80])
n_cycles = 6
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Exercise: Create the same wavelets again but increase the sampling rate to 500 Hz.
Solution
fs = 500
freq = np.array([40, 60, 80])
n_cycles = 6
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=freq, n_cycles=n_cycles)
utils.plot_wavelets(wavelet, freq, n_cycles, fs)Section 2: Applying Wavelets via Convolution
Background
Wavelets are applied to a signal via convolution. This acts as a narrow bandpass filter at the wavelet’s frequency. Because the wavelet is complex (i.e. it has a real and an imaginary component), the convolution result is also complex: its real part is the bandpass-filtered signal, and its magnitude is the instantaneous amplitude envelope at that frequency. Squaring the magnitude gives the instantaneous power.
By repeating this operation at many different center frequencies, we obtain the time-varying power at each frequency — the foundation of the full wavelet time-frequency decomposition.
Exercises
In this section you are going to apply the wavelets you generated in the previous section to a simulated signal and compute the power of the filtered signal. Here are the essential code snippets:
| Code | Description |
|---|---|
np.convolve(signal, wavelet, mode="same") |
Apply the wavelet as a bandpass filter by convolution |
filtered.real |
Real part of the filtered signal (the bandpass-filtered waveform) |
np.abs(filtered) ** 2 |
Instantaneous power at the wavelet’s center frequency |
Simulate a signal with a transient 10 Hz oscillation.
t, signal = utils.simulate_signal(dur=5, fs=500, freq=10, start=1, stop=2)
plt.plot(t, signal)Example: Create a 5 Hz wavelet with 3 cycles and convolve it with the signal and plot the signal together with the .real part of the filtered result.
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=5, n_cycles=3)
filtered = np.convolve(signal, wavelet, mode="same")
plt.plot(t, signal)
plt.plot(t, filtered.real)Exercise: Create a 10 Hz wavelet with 3 cycles and convolve it with the signal and plot the signal together with the .real part of the filtered result. How does the filtered signal differ from the previous example?
Solution
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=3)
filtered = np.convolve(signal, wavelet, mode="same")
plt.plot(t, signal)
plt.plot(t, filtered.real)Exercise: Create a 15 Hz wavelet with 3 cycles and convolve it with the signal. Plot the signal together with the .real part of the filtered result. How does it differ from the 10 Hz example?
Solution
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=15, n_cycles=3)
filtered = np.convolve(signal, wavelet, mode="same")
plt.plot(t, signal)
plt.plot(t, filtered.real)Exercise: Repeat the code above but increase the number of cycles to 10. How does that affect the amplitude of the filtered signal?
Solution
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=15, n_cycles=10)
filtered = np.convolve(signal, wavelet, mode="same")
plt.plot(t, signal)
plt.plot(t, filtered.real)Example: Convolve the signal with a 5 Hz wavelet with 3 cycles and compute the instantaneous power by squaring the magnitude of the filtered result. Plot the original signal, the real part of filtered, and the power.
Solution
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=5, n_cycles=3)
filtered = np.convolve(signal, wavelet, mode="same")
power = np.abs(filtered) ** 2
plt.plot(t, signal)
plt.plot(t, filtered.real)
plt.plot(t, power)Exercise: Convolve the signal with a 10 Hz wavelet with 3 cycles and compute the instantaneous power by squaring the magnitude of the filtered result. Plot the original signal, the real part of filtered, and the power. How does the power differ from the 5 Hz example?
wavelet = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=3)
filtered = np.convolve(signal, wavelet, mode="same")
power = np.abs(filtered) ** 2
plt.plot(t, signal)
plt.plot(t, filtered.real)
plt.plot(t, power)Exercise: Compute and plot the power of the signals filtered_8_hz, filtered_10_hz and filtered_12_hz generated by convolving the signal with wavelets of different frequency.
wavelet_8_hz = mne.time_frequency.morlet(sfreq=fs, freqs=8, n_cycles=3)
wavelet_10_hz = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=3)
wavelet_12_hz = mne.time_frequency.morlet(sfreq=fs, freqs=12, n_cycles=3)
filtered_8_hz = np.convolve(signal, wavelet_8_hz, mode="same")
filtered_10_hz = np.convolve(signal, wavelet_10_hz, mode="same")
filtered_12_hz = np.convolve(signal, wavelet_12_hz, mode="same")Solution
power_8_hz = np.abs(filtered_8_hz) ** 2
power_10_hz = np.abs(filtered_10_hz) ** 2
power_12_hz = np.abs(filtered_12_hz) ** 2
plt.plot(t, power_8_hz)
plt.plot(t, power_10_hz)
plt.plot(t, power_12_hz);Exercise: Compute and plot the power of the signals filtered_3_cy, filtered_6_cy and filtered_12_cy generated by convolving the signal with 10 Hz wavelets that vary in the number of cycles.
wavelet_3_cy = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=3)
wavelet_6_cy = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=6)
wavelet_12_cy = mne.time_frequency.morlet(sfreq=fs, freqs=10, n_cycles=12)
filtered_3_cy = np.convolve(signal, wavelet_3_cy, mode="same")
filtered_6_cy = np.convolve(signal, wavelet_6_cy, mode="same")
filtered_12_cy = np.convolve(signal, wavelet_12_cy, mode="same")Solution
power_3_cy = np.abs(filtered_3_cy) ** 2
power_6_cy = np.abs(filtered_6_cy) ** 2
power_12_cy = np.abs(filtered_12_cy) ** 2
plt.plot(t, power_3_cy)
plt.plot(t, power_6_cy)
plt.plot(t, power_12_cy);Section 3: Time-Frequency Representation of Epochs
Background
In the previous section, we convolved individual signals with wavelets one frequency at a time. MNE’s .compute_tfr method automates this across multiple frequencies, channels and epochs. The result is an EpochsTFR object that contains a two-dimensional time × frequency power matrix for each epoch. Averaging across epochs reveals consistent oscillatory dynamics while noise averages out — like an ERP, but frequency-specific.
When visualizing the average power, a (typically pre-stimulus) interval is used as a baseline and subtracted for every frequency bin. This is necessary because the frequency spectrum of EEG and MEG recordings follows a 1/f distribution where power decreases with frequency. Without baseline correction, the low-frequency components would dominate the plot and higher frequencies would be invisible.
An additional concern is edge artifacts: at the start and end of each epoch, the wavelet extends beyond the available data, causing spurious increases in power. These can be removed by cropping the time-frequency representation — the recommended amount to crop from each edge is half the duration of the longest wavelet (i.e., n_cycles / (2 * fmin)).
Exercises
In this section, you are going to use .compute_tfr() on simulated epochs to compute and visualize their time-frequency representation. By changing the number of freqs you can control the visual resolution of the time-frequency plot but not the actual resolution, which is controlled by n_cycles. Here are some code examples:
| Code | Description |
|---|---|
epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=7) |
Compute TFR for all epochs using Morlet wavelets |
epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=freqs/2) |
Scale n_cycles with frequency for a constant time-frequency tradeoff |
power.crop(tmin, tmax) |
Remove edge-artifact-contaminated time points from the TFR |
power.apply_baseline((tmin, tmax)) |
Subtract the mean power in the baseline interval, per frequency |
power.average().plot() |
Average TFR across epochs and plot |
First, we simulate 40 epochs of a 500 Hz signal with three transient oscillations at 10 Hz, 20 Hz and 40 Hz that appear at different times.
epochs = utils.simulate_epochs(
tmin=0,
tmax=1,
fs=500,
freq=[10, 20, 40], # oscillation frequencies
start=[0.2, 0.5, 0.7], # oscillation starts
stop=[0.4, 0.7, 0.9], # oscillation stops
n_epochs=40,
noise_level=0.1)
epochs.average().plot();Creating RawArray with float64 data, n_channels=1, n_times=20040
Range : 0 ... 20039 = 0.000 ... 40.078 secs
Ready.
Not setting metadata
40 matching events found
No baseline correction applied
0 projection items activated
Need more than one channel to make topography for eeg. Disabling interactivity.Example: Compute the time-frequency representation of epochs for frequencies from 5 to 55 Hz in steps of 5 Hz using 2 cycles per wavelet. Average the result across epochs and plot it.
freqs = np.arange(5, 60, 5)
power = epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=2)
power.average().plot(colorbar=False);Using data from preloaded Raw for 40 events and 501 original time points ...
0 bad epochs dropped
No baseline correction appliedExercise: Repeat the example above but increase n_cycles to 3. How does this affect the time and frequency resolution of the TFR?
Solution
freqs = np.arange(5, 60, 5)
power = epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=3)
power.average().plot(colorbar=False);Using data from preloaded Raw for 40 events and 501 original time points ...
No baseline correction appliedExercise: Reduce the step size in the array of freqs to 1 to increase the resolution of the time-frequency representation and plot the power again.
Solution
freqs = np.arange(5, 60, 1)
power = epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=3)
power.average().plot(colorbar=False);Using data from preloaded Raw for 40 events and 501 original time points ...
No baseline correction appliedExercise: Repeat the code from above but set n_cycles to freqs/2 to scale the number of cycles with the wavelet’s frequency.
Solution
freqs = np.arange(5, 60.0, 1.0)
power = epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=freqs/2)
power.average().plot(colorbar=False);Using data from preloaded Raw for 40 events and 501 original time points ...
No baseline correction appliedThe simulated data we used so far was unrealistically clean for EEG. Let’s regenerate the epochs with a 10 times larger noise level so that the oscillations are completely buried.
epochs = utils.simulate_epochs(
tmin=-1,
tmax=2,
fs=500,
freq=[10, 20, 40], # oscillation frequencies
start=[0.2, 0.5, 0.7], # oscillation starts
stop=[0.4, 0.7, 0.9], # oscillation stops
n_epochs=40,
noise_level=1)
epochs.average().plot();Creating RawArray with float64 data, n_channels=1, n_times=60040
Range : 0 ... 60039 = 0.000 ... 120.078 secs
Ready.
Not setting metadata
40 matching events found
No baseline correction applied
0 projection items activated
Need more than one channel to make topography for eeg. Disabling interactivity.The cell below computes and plots the time-frequency representation with n_cycles=6 for every wavelet. There are two important differences compared to the previous plot:
- There are edge artifacts along the left and right borders of the plot
- It is much harder to distinguish genuine oscillations from random noise
To address these problems we can:
- Crop the edges of the TFR that are affected by artifacts
- Apply a baseline correction to normalize power relative to a pre-stimulus period
freqs = np.arange(5, 60.0, 1.0)
n_cycles = 6
power = epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=n_cycles)
power.average().plot(colorbar=False);Using data from preloaded Raw for 40 events and 1501 original time points ...
0 bad epochs dropped
No baseline correction appliedExercise: Use the .crop() method to crop t_crop from the start and end of power and re-create the time-frequency plot. The typical recommendation is to crop half of the length of the longest wavelet (i.e. the one with the lowest frequency).
t_crop = n_cycles / (freqs.min()*2)
t_cropnp.float64(0.6)Solution
power.crop(-0.4, 1.4)
power.average().plot(colorbar=False);No baseline correction appliedExercise: Use the .apply_baseline() method to apply a baseline correction using the time interval before 0 and re-create the time-frequency plot.
Solution
power.apply_baseline((None, 0))
power.average().plot(colorbar=False);Applying baseline correction (mode: mean)
No baseline correction appliedSection 4: Applying Time-Frequency Analysis to Real EEG Data
Background
Now that you know how to compute and interpret time-frequency plots, it’s time to apply the analysis to real EEG data. In this section we will use wavelet decomposition to analyze the EEGBCI dataset where subjects were asked to imagine moving the left or right hand following a visual cue. Such motor imagery is known to induce event-related desynchronization (ERD) because motor neurons transition from an “idle” synchronized alpha rhythm to high-frequency asynchronous firing which does not sum to visible scalp potentials. Because the ERD is not phase-locked to the stimulus, it is invisible in the ERP — but a time-frequency representation can reveal it (see Pfurtscheller & Lopes da Silva, 1999).
The ERD appears as a sustained drop in power in the alpha band (8–13 Hz) and should be largest at the electrodes C3 and C4, which approximately overlie the left and right primary motor cortex. Because of the contralateral organization of the motor system, the ERD should be largest over the right hemisphere for left-hand motor imagery and vice versa. This asymmetry can be used by brain-computer interfaces (BCI) to decode the intended movement of the participant.
Exercises
In this section you will apply what you have learned using simulated data to real EEG recordings. The only new command is .plot_topomap(), which plots the average power across a given time and frequency window as a scalp topography.
| Code | Description |
|---|---|
epochs.compute_tfr(method="morlet", freqs=freqs, n_cycles=n_cycles) |
Compute TFR for all epochs |
power.crop(tmin, tmax) |
Remove edge-artifact-contaminated time points |
power.apply_baseline((tmin, tmax)) |
Apply baseline correction per frequency |
power["condition"].average().plot(picks=["channel"]) |
Plot TFR for a specific condition and channel |
power["condition"].average().plot_topomap(tmin=t0, tmax=t1, fmin=f0, fmax=f1) |
Plot scalp topography averaged over a time and frequency window |
First, we load and epoch the data. Every epoch starts with a visual cue that prompts the participant to imagine moving the "left" or "right" hand. The epochs go from tmin=-1 to tmax=3 seconds which gives us a buffer for cropping the edge artifacts from the wavelet transformation.
raw_fnames = mne.datasets.eegbci.load_data(subjects=1, runs=[4, 8, 12]) # motor imagery runs
event_id = {"left": 1, "right": 2}
epochs = []
for fname in raw_fnames:
raw = mne.io.read_raw_edf(fname, preload=True, verbose=False)
events = mne.events_from_annotations(raw, verbose=False)[0]
raw.filter(1, 40, verbose=False)
epochs.append(
mne.Epochs(raw, events, event_id, tmin=-1, tmax=3, baseline=None, verbose=False)
)
epochs = mne.concatenate_epochs(epochs, verbose=False)
montage = mne.channels.make_standard_montage("standard_1005")
epochs.rename_channels(lambda name: name.strip('.'))
epochs.set_montage(montage, match_case=False);The ERP shows little stimulus-locked activity, which is expected — motor imagery does not produce a strong phase-locked response.
epochs.average().crop(-0.1, 1.5).plot();Exercise: Combine what you learned so far:
- Create an array of
freqscovering the alpha and beta bands (8–30 Hz) - Compute the time-frequency representation using an appropriate number of
n_cycles - Crop the edges of the TFR to remove artifacts
- Apply a baseline correction
- Plot the result using the average power across all channels (
combine="mean")
Solution
freqs = np.arange(8, 30, 0.5)
power = epochs.compute_tfr( method="morlet", freqs=freqs, n_cycles=6)
power.crop(-0.2, 2)
power.apply_baseline((-0.2, 0))
power.average().plot(combine="mean");Applying baseline correction (mode: mean)
No baseline correction appliedExercise: Once you have found the right parameters, plot the TFR for the "left" condition at the right central channel "C4" and the TFR for the "right" condition at the left central channel "C3" (Hint: power["condition"].average().plot(picks=["channel"])). Do you see the ERD contralateral to the imagined movement?
Solution
power["left"].average().plot(picks=["C4"])
power["right"].average().plot(picks=["C3"]);No baseline correction appliedNo baseline correction appliedExercise: Plot the average scalp topography over a time windows that shows the ERD in the alpha band (10–13 Hz) for the "right" and "left" hand condition separately. Can you spot the hemispheric asymmetry?
Solution
power["right"].average().plot_topomap(tmin=0.25, tmax=1, fmin=10, fmax=13, size=4, show_names=True);
power["left"].average().plot_topomap(tmin=0.25, tmax=1, fmin=10, fmax=13, size=4, show_names=True);No baseline correction appliedNo baseline correction applied