Note
Click here to download the full example code
This example calculates and displays ERDS maps of event-related EEG data. ERDS (sometimes also written as ERD/ERS) is short for event-related desynchronization (ERD) and event-related synchronization (ERS) [1]. Conceptually, ERD corresponds to a decrease in power in a specific frequency band relative to a baseline. Similarly, ERS corresponds to an increase in power. An ERDS map is a time/frequency representation of ERD/ERS over a range of frequencies [2]. ERDS maps are also known as ERSP (event-related spectral perturbation) [3].
In this example, we use an EEG BCI data set containing two different motor imagery tasks (imagined hand and feet movement). Our goal is to generate ERDS maps for each of the two tasks.
First, we load the data and create epochs of 5s length. The data set contains multiple channels, but we will only consider C3, Cz, and C4. We compute maps containing frequencies ranging from 2 to 35Hz. We map ERD to red color and ERS to blue color, which is customary in many ERDS publications. Finally, we perform cluster-based permutation tests to estimate significant ERDS values (corrected for multiple comparisons within channels).
# Authors: Clemens Brunner <clemens.brunner@gmail.com>
# Felix Klotzsche <klotzsche@cbs.mpg.de>
#
# License: BSD-3-Clause
As usual, we import everything we need.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import TwoSlopeNorm
import pandas as pd
import seaborn as sns
import mne
from mne.datasets import eegbci
from mne.io import concatenate_raws, read_raw_edf
from mne.time_frequency import tfr_multitaper
from mne.stats import permutation_cluster_1samp_test as pcluster_test
First, we load and preprocess the data. We use runs 6, 10, and 14 from subject 1 (these runs contains hand and feet motor imagery).
fnames = eegbci.load_data(subject=1, runs=(6, 10, 14))
raw = concatenate_raws([read_raw_edf(f, preload=True) for f in fnames])
raw.rename_channels(lambda x: x.strip('.')) # remove dots from channel names
events, _ = mne.events_from_annotations(raw, event_id=dict(T1=2, T2=3))
Exception ignored in: <_io.FileIO name='/home/circleci/project/mne/data/eegbci_checksums.txt' mode='rb' closefd=True>
Traceback (most recent call last):
File "<decorator-gen-544>", line 12, in load_data
ResourceWarning: unclosed file <_io.BufferedReader name='/home/circleci/project/mne/data/eegbci_checksums.txt'>
Extracting EDF parameters from /home/circleci/mne_data/MNE-eegbci-data/files/eegmmidb/1.0.0/S001/S001R06.edf...
EDF file detected
Setting channel info structure...
Creating raw.info structure...
Reading 0 ... 19999 = 0.000 ... 124.994 secs...
Extracting EDF parameters from /home/circleci/mne_data/MNE-eegbci-data/files/eegmmidb/1.0.0/S001/S001R10.edf...
EDF file detected
Setting channel info structure...
Creating raw.info structure...
Reading 0 ... 19999 = 0.000 ... 124.994 secs...
Extracting EDF parameters from /home/circleci/mne_data/MNE-eegbci-data/files/eegmmidb/1.0.0/S001/S001R14.edf...
EDF file detected
Setting channel info structure...
Creating raw.info structure...
Reading 0 ... 19999 = 0.000 ... 124.994 secs...
Used Annotations descriptions: ['T1', 'T2']
Now we can create 5-second epochs around events of interest.
Not setting metadata
45 matching events found
No baseline correction applied
0 projection items activated
Using data from preloaded Raw for 45 events and 961 original time points ...
0 bad epochs dropped
Here we set suitable values for computing ERDS maps.
freqs = np.arange(2, 36) # frequencies from 2-35Hz
vmin, vmax = -1, 1.5 # set min and max ERDS values in plot
baseline = (-1, 0) # baseline interval (in s)
cnorm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax) # min, center & max ERDS
kwargs = dict(n_permutations=100, step_down_p=0.05, seed=1,
buffer_size=None, out_type='mask') # for cluster test
Finally, we perform time/frequency decomposition over all epochs.
tfr = tfr_multitaper(epochs, freqs=freqs, n_cycles=freqs, use_fft=True,
return_itc=False, average=False, decim=2)
tfr.crop(tmin, tmax).apply_baseline(baseline, mode="percent")
for event in event_ids:
# select desired epochs for visualization
tfr_ev = tfr[event]
fig, axes = plt.subplots(1, 4, figsize=(12, 4),
gridspec_kw={"width_ratios": [10, 10, 10, 1]})
for ch, ax in enumerate(axes[:-1]): # for each channel
# positive clusters
_, c1, p1, _ = pcluster_test(tfr_ev.data[:, ch], tail=1, **kwargs)
# negative clusters
_, c2, p2, _ = pcluster_test(tfr_ev.data[:, ch], tail=-1, **kwargs)
# note that we keep clusters with p <= 0.05 from the combined clusters
# of two independent tests; in this example, we do not correct for
# these two comparisons
c = np.stack(c1 + c2, axis=2) # combined clusters
p = np.concatenate((p1, p2)) # combined p-values
mask = c[..., p <= 0.05].any(axis=-1)
# plot TFR (ERDS map with masking)
tfr_ev.average().plot([ch], cmap="RdBu", cnorm=cnorm, axes=ax,
colorbar=False, show=False, mask=mask,
mask_style="mask")
ax.set_title(epochs.ch_names[ch], fontsize=10)
ax.axvline(0, linewidth=1, color="black", linestyle=":") # event
if ch != 0:
ax.set_ylabel("")
ax.set_yticklabels("")
fig.colorbar(axes[0].images[-1], cax=axes[-1]).ax.set_yscale("linear")
fig.suptitle(f"ERDS ({event})")
plt.show()
[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 0.2s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 2 out of 2 | elapsed: 0.4s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 3 out of 3 | elapsed: 0.5s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 3 out of 3 | elapsed: 0.5s finished
Not setting metadata
Applying baseline correction (mode: percent)
Using a threshold of 1.724718
stat_fun(H1): min=-8.523637 max=3.197747
Running initial clustering …
Found 78 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
9%|9 | Permuting : 9/99 [00:00<00:00, 260.91it/s]
19%|#9 | Permuting : 19/99 [00:00<00:00, 278.35it/s]
30%|### | Permuting : 30/99 [00:00<00:00, 294.88it/s]
41%|####1 | Permuting : 41/99 [00:00<00:00, 302.93it/s]
54%|#####3 | Permuting : 53/99 [00:00<00:00, 314.21it/s]
66%|######5 | Permuting : 65/99 [00:00<00:00, 321.91it/s]
78%|#######7 | Permuting : 77/99 [00:00<00:00, 327.28it/s]
91%|######### | Permuting : 90/99 [00:00<00:00, 335.72it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 337.32it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 332.83it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
Using a threshold of -1.724718
stat_fun(H1): min=-8.523637 max=3.197747
Running initial clustering …
Found 65 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 324.94it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 325.17it/s]
34%|###4 | Permuting : 34/99 [00:00<00:00, 335.78it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 332.72it/s]
57%|#####6 | Permuting : 56/99 [00:00<00:00, 331.00it/s]
68%|######7 | Permuting : 67/99 [00:00<00:00, 329.92it/s]
79%|#######8 | Permuting : 78/99 [00:00<00:00, 329.20it/s]
90%|########9 | Permuting : 89/99 [00:00<00:00, 328.67it/s]
99%|#########8| Permuting : 98/99 [00:00<00:00, 320.25it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 322.55it/s]
Step-down-in-jumps iteration #1 found 1 cluster to exclude from subsequent iterations
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 324.89it/s]
23%|##3 | Permuting : 23/99 [00:00<00:00, 340.35it/s]
34%|###4 | Permuting : 34/99 [00:00<00:00, 334.58it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 332.08it/s]
57%|#####6 | Permuting : 56/99 [00:00<00:00, 330.57it/s]
68%|######7 | Permuting : 67/99 [00:00<00:00, 329.61it/s]
79%|#######8 | Permuting : 78/99 [00:00<00:00, 328.89it/s]
90%|########9 | Permuting : 89/99 [00:00<00:00, 328.38it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 327.42it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 327.57it/s]
Step-down-in-jumps iteration #2 found 0 additional clusters to exclude from subsequent iterations
No baseline correction applied
Using a threshold of 1.724718
stat_fun(H1): min=-4.573067 max=3.687727
Running initial clustering …
Found 85 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
10%|# | Permuting : 10/99 [00:00<00:00, 294.60it/s]
20%|## | Permuting : 20/99 [00:00<00:00, 294.46it/s]
31%|###1 | Permuting : 31/99 [00:00<00:00, 305.08it/s]
43%|####3 | Permuting : 43/99 [00:00<00:00, 318.62it/s]
56%|#####5 | Permuting : 55/99 [00:00<00:00, 326.63it/s]
68%|######7 | Permuting : 67/99 [00:00<00:00, 331.86it/s]
79%|#######8 | Permuting : 78/99 [00:00<00:00, 330.73it/s]
90%|########9 | Permuting : 89/99 [00:00<00:00, 329.96it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 334.74it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 332.19it/s]
Step-down-in-jumps iteration #1 found 1 cluster to exclude from subsequent iterations
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 324.95it/s]
23%|##3 | Permuting : 23/99 [00:00<00:00, 340.33it/s]
35%|###5 | Permuting : 35/99 [00:00<00:00, 345.22it/s]
48%|####8 | Permuting : 48/99 [00:00<00:00, 355.69it/s]
60%|#####9 | Permuting : 59/99 [00:00<00:00, 349.06it/s]
72%|#######1 | Permuting : 71/99 [00:00<00:00, 350.29it/s]
84%|########3 | Permuting : 83/99 [00:00<00:00, 351.15it/s]
97%|#########6| Permuting : 96/99 [00:00<00:00, 356.19it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 355.66it/s]
Step-down-in-jumps iteration #2 found 0 additional clusters to exclude from subsequent iterations
Using a threshold of -1.724718
stat_fun(H1): min=-4.573067 max=3.687727
Running initial clustering …
Found 57 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 325.08it/s]
24%|##4 | Permuting : 24/99 [00:00<00:00, 355.21it/s]
36%|###6 | Permuting : 36/99 [00:00<00:00, 355.29it/s]
48%|####8 | Permuting : 48/99 [00:00<00:00, 355.18it/s]
61%|###### | Permuting : 60/99 [00:00<00:00, 355.11it/s]
73%|#######2 | Permuting : 72/99 [00:00<00:00, 355.09it/s]
85%|########4 | Permuting : 84/99 [00:00<00:00, 355.14it/s]
97%|#########6| Permuting : 96/99 [00:00<00:00, 355.15it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 356.92it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
No baseline correction applied
Using a threshold of 1.724718
stat_fun(H1): min=-6.599131 max=3.329547
Running initial clustering …
Found 64 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
10%|# | Permuting : 10/99 [00:00<00:00, 294.93it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 325.53it/s]
33%|###3 | Permuting : 33/99 [00:00<00:00, 325.43it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 333.44it/s]
59%|#####8 | Permuting : 58/99 [00:00<00:00, 344.74it/s]
71%|####### | Permuting : 70/99 [00:00<00:00, 346.64it/s]
83%|########2 | Permuting : 82/99 [00:00<00:00, 348.09it/s]
96%|#########5| Permuting : 95/99 [00:00<00:00, 353.35it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 352.72it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
Using a threshold of -1.724718
stat_fun(H1): min=-6.599131 max=3.329547
Running initial clustering …
Found 64 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
12%|#2 | Permuting : 12/99 [00:00<00:00, 353.06it/s]
24%|##4 | Permuting : 24/99 [00:00<00:00, 354.05it/s]
36%|###6 | Permuting : 36/99 [00:00<00:00, 354.51it/s]
49%|####9 | Permuting : 49/99 [00:00<00:00, 362.22it/s]
62%|######1 | Permuting : 61/99 [00:00<00:00, 360.47it/s]
73%|#######2 | Permuting : 72/99 [00:00<00:00, 353.60it/s]
86%|########5 | Permuting : 85/99 [00:00<00:00, 358.65it/s]
98%|#########7| Permuting : 97/99 [00:00<00:00, 358.08it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 360.54it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
No baseline correction applied
Using a threshold of 1.713872
stat_fun(H1): min=-3.687815 max=3.369164
Running initial clustering …
Found 66 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 324.83it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 325.32it/s]
32%|###2 | Permuting : 32/99 [00:00<00:00, 314.83it/s]
44%|####4 | Permuting : 44/99 [00:00<00:00, 325.59it/s]
56%|#####5 | Permuting : 55/99 [00:00<00:00, 325.61it/s]
68%|######7 | Permuting : 67/99 [00:00<00:00, 331.01it/s]
78%|#######7 | Permuting : 77/99 [00:00<00:00, 325.08it/s]
89%|########8 | Permuting : 88/99 [00:00<00:00, 325.06it/s]
99%|#########8| Permuting : 98/99 [00:00<00:00, 321.09it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 321.54it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
Using a threshold of -1.713872
stat_fun(H1): min=-3.687815 max=3.369164
Running initial clustering …
Found 75 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
10%|# | Permuting : 10/99 [00:00<00:00, 294.75it/s]
21%|##1 | Permuting : 21/99 [00:00<00:00, 310.16it/s]
33%|###3 | Permuting : 33/99 [00:00<00:00, 325.97it/s]
44%|####4 | Permuting : 44/99 [00:00<00:00, 325.96it/s]
55%|#####4 | Permuting : 54/99 [00:00<00:00, 319.31it/s]
66%|######5 | Permuting : 65/99 [00:00<00:00, 320.50it/s]
77%|#######6 | Permuting : 76/99 [00:00<00:00, 321.31it/s]
89%|########8 | Permuting : 88/99 [00:00<00:00, 326.28it/s]
99%|#########8| Permuting : 98/99 [00:00<00:00, 322.11it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 323.41it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
No baseline correction applied
Using a threshold of 1.713872
stat_fun(H1): min=-5.046259 max=5.406477
Running initial clustering …
Found 98 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 323.54it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 324.71it/s]
33%|###3 | Permuting : 33/99 [00:00<00:00, 325.09it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 333.30it/s]
57%|#####6 | Permuting : 56/99 [00:00<00:00, 331.67it/s]
67%|######6 | Permuting : 66/99 [00:00<00:00, 324.89it/s]
77%|#######6 | Permuting : 76/99 [00:00<00:00, 320.16it/s]
88%|########7 | Permuting : 87/99 [00:00<00:00, 321.00it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 326.57it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 325.82it/s]
Step-down-in-jumps iteration #1 found 1 cluster to exclude from subsequent iterations
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 325.66it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 325.65it/s]
33%|###3 | Permuting : 33/99 [00:00<00:00, 325.18it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 333.33it/s]
58%|#####7 | Permuting : 57/99 [00:00<00:00, 338.25it/s]
70%|######9 | Permuting : 69/99 [00:00<00:00, 341.44it/s]
81%|######## | Permuting : 80/99 [00:00<00:00, 338.68it/s]
93%|#########2| Permuting : 92/99 [00:00<00:00, 341.00it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 343.82it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 342.04it/s]
Step-down-in-jumps iteration #2 found 0 additional clusters to exclude from subsequent iterations
Using a threshold of -1.713872
stat_fun(H1): min=-5.046259 max=5.406477
Running initial clustering …
Found 66 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
10%|# | Permuting : 10/99 [00:00<00:00, 295.08it/s]
18%|#8 | Permuting : 18/99 [00:00<00:00, 265.28it/s]
26%|##6 | Permuting : 26/99 [00:00<00:00, 255.08it/s]
36%|###6 | Permuting : 36/99 [00:00<00:00, 266.14it/s]
46%|####6 | Permuting : 46/99 [00:00<00:00, 272.76it/s]
58%|#####7 | Permuting : 57/99 [00:00<00:00, 282.73it/s]
69%|######8 | Permuting : 68/99 [00:00<00:00, 289.68it/s]
80%|#######9 | Permuting : 79/99 [00:00<00:00, 295.03it/s]
91%|######### | Permuting : 90/99 [00:00<00:00, 299.17it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 303.44it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 299.12it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
No baseline correction applied
Using a threshold of 1.713872
stat_fun(H1): min=-5.964817 max=4.078953
Running initial clustering …
Found 92 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
8%|8 | Permuting : 8/99 [00:00<00:00, 230.58it/s]
16%|#6 | Permuting : 16/99 [00:00<00:00, 233.76it/s]
23%|##3 | Permuting : 23/99 [00:00<00:00, 224.55it/s]
32%|###2 | Permuting : 32/99 [00:00<00:00, 235.71it/s]
40%|#### | Permuting : 40/99 [00:00<00:00, 235.89it/s]
48%|####8 | Permuting : 48/99 [00:00<00:00, 235.94it/s]
58%|#####7 | Permuting : 57/99 [00:00<00:00, 240.90it/s]
66%|######5 | Permuting : 65/99 [00:00<00:00, 240.34it/s]
75%|#######4 | Permuting : 74/99 [00:00<00:00, 243.89it/s]
86%|########5 | Permuting : 85/99 [00:00<00:00, 254.07it/s]
98%|#########7| Permuting : 97/99 [00:00<00:00, 265.72it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 262.24it/s]
Step-down-in-jumps iteration #1 found 1 cluster to exclude from subsequent iterations
0%| | Permuting : 0/99 [00:00<?, ?it/s]
11%|#1 | Permuting : 11/99 [00:00<00:00, 325.15it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 325.50it/s]
33%|###3 | Permuting : 33/99 [00:00<00:00, 325.46it/s]
45%|####5 | Permuting : 45/99 [00:00<00:00, 333.50it/s]
58%|#####7 | Permuting : 57/99 [00:00<00:00, 338.38it/s]
70%|######9 | Permuting : 69/99 [00:00<00:00, 341.57it/s]
82%|########1 | Permuting : 81/99 [00:00<00:00, 343.87it/s]
94%|#########3| Permuting : 93/99 [00:00<00:00, 345.45it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 344.28it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 342.79it/s]
Step-down-in-jumps iteration #2 found 0 additional clusters to exclude from subsequent iterations
Using a threshold of -1.713872
stat_fun(H1): min=-5.964817 max=4.078953
Running initial clustering …
Found 52 clusters
0%| | Permuting : 0/99 [00:00<?, ?it/s]
10%|# | Permuting : 10/99 [00:00<00:00, 295.37it/s]
22%|##2 | Permuting : 22/99 [00:00<00:00, 326.15it/s]
34%|###4 | Permuting : 34/99 [00:00<00:00, 336.34it/s]
46%|####6 | Permuting : 46/99 [00:00<00:00, 341.54it/s]
58%|#####7 | Permuting : 57/99 [00:00<00:00, 338.01it/s]
69%|######8 | Permuting : 68/99 [00:00<00:00, 335.71it/s]
81%|######## | Permuting : 80/99 [00:00<00:00, 338.94it/s]
92%|#########1| Permuting : 91/99 [00:00<00:00, 336.96it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 339.11it/s]
100%|##########| Permuting : 99/99 [00:00<00:00, 338.10it/s]
Step-down-in-jumps iteration #1 found 0 clusters to exclude from subsequent iterations
No baseline correction applied
Similar to Epochs
objects, we can also export data from
EpochsTFR
and AverageTFR
objects
to a Pandas DataFrame
. By default, the time
column of the exported data frame is in milliseconds. Here, to be consistent
with the time-frequency plots, we want to keep it in seconds, which we can
achieve by setting time_format=None
:
df = tfr.to_data_frame(time_format=None)
df.head()
This allows us to use additional plotting functions like
seaborn.lineplot()
to plot confidence bands:
df = tfr.to_data_frame(time_format=None, long_format=True)
# Map to frequency bands:
freq_bounds = {'_': 0,
'delta': 3,
'theta': 7,
'alpha': 13,
'beta': 35,
'gamma': 140}
df['band'] = pd.cut(df['freq'], list(freq_bounds.values()),
labels=list(freq_bounds)[1:])
# Filter to retain only relevant frequency bands:
freq_bands_of_interest = ['delta', 'theta', 'alpha', 'beta']
df = df[df.band.isin(freq_bands_of_interest)]
df['band'] = df['band'].cat.remove_unused_categories()
# Order channels for plotting:
df['channel'] = df['channel'].cat.reorder_categories(('C3', 'Cz', 'C4'),
ordered=True)
g = sns.FacetGrid(df, row='band', col='channel', margin_titles=True)
g.map(sns.lineplot, 'time', 'value', 'condition', n_boot=10)
axline_kw = dict(color='black', linestyle='dashed', linewidth=0.5, alpha=0.5)
g.map(plt.axhline, y=0, **axline_kw)
g.map(plt.axvline, x=0, **axline_kw)
g.set(ylim=(None, 1.5))
g.set_axis_labels("Time (s)", "ERDS (%)")
g.set_titles(col_template="{col_name}", row_template="{row_name}")
g.add_legend(ncol=2, loc='lower center')
g.fig.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.08)
Converting "condition" to "category"...
Converting "epoch" to "category"...
Converting "freq" to "category"...
Converting "channel" to "category"...
Converting "ch_type" to "category"...
Having the data as a DataFrame also facilitates subsetting, grouping, and other transforms. Here, we use seaborn to plot the average ERDS in the motor imagery interval as a function of frequency band and imagery condition:
df_mean = (df.query('time > 1')
.groupby(['condition', 'epoch', 'band', 'channel'])[['value']]
.mean()
.reset_index())
g = sns.FacetGrid(df_mean, col='condition', col_order=['hands', 'feet'],
margin_titles=True)
g = (g.map(sns.violinplot, 'channel', 'value', 'band', n_boot=10,
palette='deep', order=['C3', 'Cz', 'C4'],
hue_order=freq_bands_of_interest,
linewidth=0.5).add_legend(ncol=4, loc='lower center'))
g.map(plt.axhline, **axline_kw)
g.set_axis_labels("", "ERDS (%)")
g.set_titles(col_template="{col_name}", row_template="{row_name}")
g.fig.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.3)
Total running time of the script: ( 0 minutes 29.456 seconds)
Estimated memory usage: 146 MB