|
| 1 | +""" |
| 2 | +Using fooof with MNE |
| 3 | +==================== |
| 4 | +
|
| 5 | +This examples illustrates how to use fooof with MNE and create topographical plots. |
| 6 | +
|
| 7 | +This tutorial does require that you have `MNE |
| 8 | +<https://mne-tools.github.io/>`_ installed. If you don't already have |
| 9 | +MNE, you can follow instructions to get it `here |
| 10 | +<https://mne-tools.github.io/stable/getting_started.html>`_. |
| 11 | +""" |
| 12 | + |
| 13 | +################################################################################################### |
| 14 | + |
| 15 | +# General imports |
| 16 | +import numpy as np |
| 17 | +import pandas as pd |
| 18 | +from matplotlib import cm, colors, colorbar |
| 19 | +import matplotlib.pyplot as plt |
| 20 | + |
| 21 | +# Import MNE, as well as the MNE sample dataset |
| 22 | +import mne |
| 23 | +from mne import io |
| 24 | +from mne.datasets import sample |
| 25 | +from mne.viz import plot_topomap |
| 26 | + |
| 27 | +# FOOOF imports |
| 28 | +from fooof import FOOOF, FOOOFGroup |
| 29 | +from fooof.analysis import * |
| 30 | + |
| 31 | +################################################################################################### |
| 32 | +# Load & Check MNE Data |
| 33 | +# --------------------- |
| 34 | +# |
| 35 | +# First, we will load the example dataset from MNE, and have a quick look at the data. |
| 36 | +# |
| 37 | +# The MNE sample dataset is a combined MEG/EEG recording with an audiovisual task, as |
| 38 | +# described `here <https://martinos.org/mne/stable/manual/sample_dataset.html>`_. |
| 39 | +# |
| 40 | +# For the current example, we are going to sub-select only the EEG data, |
| 41 | +# and analyze it as continuous (non-epoched) data. |
| 42 | +# |
| 43 | + |
| 44 | +################################################################################################### |
| 45 | + |
| 46 | +# Get the data path for the MNE example data |
| 47 | +raw_fname = sample.data_path() + '/MEG/sample/sample_audvis_filt-0-40_raw.fif' |
| 48 | +event_fname = sample.data_path() + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif' |
| 49 | + |
| 50 | +# Load the file of example MNE data |
| 51 | +raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False) |
| 52 | + |
| 53 | +################################################################################################### |
| 54 | + |
| 55 | +# Select EEG channels from the dataset |
| 56 | +raw = raw.pick_types(meg=False, eeg=True, eog=False, exclude='bads') |
| 57 | + |
| 58 | +################################################################################################### |
| 59 | + |
| 60 | +# Explicitly adding a default EEG average reference required for source localization |
| 61 | +raw.set_eeg_reference() |
| 62 | + |
| 63 | +################################################################################################### |
| 64 | + |
| 65 | +# Plot the channel locations and labels |
| 66 | +raw.plot_sensors(show_names=True) |
| 67 | + |
| 68 | +################################################################################################### |
| 69 | + |
| 70 | +# Creating epochs |
| 71 | +reject = dict(eeg=180e-6) |
| 72 | +event_id = {'left/auditory': 1} |
| 73 | +events = mne.read_events(event_fname) |
| 74 | +epochs = mne.Epochs(raw, events=events, event_id=event_id, tmin=5, tmax=125, |
| 75 | + baseline=None, preload=True, verbose=False) |
| 76 | + |
| 77 | +################################################################################################### |
| 78 | + |
| 79 | +# Creating Power Spectra Densities |
| 80 | +spectra, freqs = mne.time_frequency.psd_welch(epochs, fmin=1., fmax=50., n_fft=2000, |
| 81 | + n_overlap=250, n_per_seg=500) |
| 82 | + |
| 83 | +################################################################################################### |
| 84 | +# FOOOFGroup |
| 85 | +# ---------- |
| 86 | +# |
| 87 | +# The FOOOFGroup object is used to fit FOOOF models across the power spectra. |
| 88 | +# |
| 89 | + |
| 90 | +################################################################################################### |
| 91 | + |
| 92 | +# Initialize a FOOOFGroup object, with desired settings |
| 93 | +fg = FOOOFGroup(peak_width_limits=[1, 6], min_peak_height=0.075, |
| 94 | + max_n_peaks=6, peak_threshold=1, verbose=False) |
| 95 | + |
| 96 | +################################################################################################### |
| 97 | + |
| 98 | +# Selecting the first epoch of data to FOOOF |
| 99 | +spectra = np.squeeze(spectra[0,:,:]) |
| 100 | +n_channels, n_freq = spectra.shape |
| 101 | +num_blocks = len(mne.read_events(event_fname)) |
| 102 | + |
| 103 | +# Setting frequency range |
| 104 | +freq_range = [3, 35] |
| 105 | + |
| 106 | +# Fit the FOOOF model across all channels |
| 107 | +fg.fit(freqs, spectra, freq_range) |
| 108 | +fg.plot() |
| 109 | + |
| 110 | +################################################################################################### |
| 111 | + |
| 112 | +# Define labels for periodic & aperiodic features |
| 113 | +feats = ["CFS", "PWS", "BWS"] |
| 114 | +aperiodic_feats = ["Offset","Exponent"] |
| 115 | + |
| 116 | +# Define bands of interest |
| 117 | +bands = {'theta': [3, 7], |
| 118 | + 'alpha': [7, 14], |
| 119 | + 'beta': [15, 30]} |
| 120 | + |
| 121 | +# Create dictionaries to store all the periodic properties across frequencies |
| 122 | +results = {} |
| 123 | +for band_name in bands.keys(): |
| 124 | + results[band_name] = np.zeros(shape=[num_blocks, n_channels, len(feats)]) |
| 125 | + |
| 126 | +# Creating dictionaries to store all the aperiodic properties across frequencies |
| 127 | +exponent_results = np.zeros(shape=[num_blocks, n_channels, len(aperiodic_feats)]) |
| 128 | + |
| 129 | +################################################################################################### |
| 130 | + |
| 131 | +# Populating periodic and aperiodic values |
| 132 | +for block in range(0, num_blocks): |
| 133 | + for ind, res in enumerate(fg): |
| 134 | + exponent_results[block, ind, :] = res.aperiodic_params |
| 135 | + for band_label, band_range in bands.items(): |
| 136 | + results[band_label][block, ind, :] = get_band_peak(res.peak_params, band_range, True) |
| 137 | + |
| 138 | +################################################################################################### |
| 139 | +# Plotting Topographies |
| 140 | +# --------------------- |
| 141 | +# |
| 142 | +# Now we can plot the extracted FOOOF features across all channels. |
| 143 | +# |
| 144 | + |
| 145 | +################################################################################################### |
| 146 | + |
| 147 | +def plot_topo_colorbar(vmin, vmax, label): |
| 148 | + """Helper function to create colorbars for the topography plots.""" |
| 149 | + |
| 150 | + fig = plt.figure(figsize=(2, 3)) |
| 151 | + ax1 = fig.add_axes([0.9, 0.25, 0.15, 0.9]) |
| 152 | + |
| 153 | + cmap = cm.viridis |
| 154 | + norm = colors.Normalize(vmin=vmin, vmax=vmax) |
| 155 | + |
| 156 | + cb1 = colorbar.ColorbarBase(plt.gca(), cmap=cmap, |
| 157 | + norm=norm, orientation='vertical') |
| 158 | + |
| 159 | +################################################################################################### |
| 160 | +# |
| 161 | +# In this example, we will be plotting the alpha center frequency and oscillatory exponent. |
| 162 | + |
| 163 | +################################################################################################### |
| 164 | + |
| 165 | +# Settings to grab the alpha center frequency |
| 166 | +band = 'alpha' |
| 167 | +cur_data = results[band] |
| 168 | + |
| 169 | +topo_dat = np.mean(cur_data,0) |
| 170 | + |
| 171 | +################################################################################################### |
| 172 | + |
| 173 | +# Looking at the alpha center frequeuncy |
| 174 | +print('CURRENT FEATURE:', feats[0]) |
| 175 | +disp_dat = topo_dat[:,0] |
| 176 | + |
| 177 | +inds = np.where(np.isnan(disp_dat)) |
| 178 | +disp_dat[inds] = np.nanmean(disp_dat) |
| 179 | + |
| 180 | +vbuffer = 0.1 * (disp_dat.max() - disp_dat.min()) |
| 181 | +vmin, vmax, = disp_dat.min() - vbuffer, disp_dat.max() + vbuffer |
| 182 | + |
| 183 | +fig, ax = plt.subplots() |
| 184 | +plot_topo_colorbar(vmin, vmax, feats[0]) |
| 185 | +mne.viz.plot_topomap(disp_dat, raw.info, vmin=vmin, vmax=vmax, cmap=cm.viridis, contours=0, axes=ax) |
| 186 | + |
| 187 | +################################################################################################### |
| 188 | + |
| 189 | +# Looking at the aperiodic exponent |
| 190 | +cur_data = exponent_results |
| 191 | + |
| 192 | +topo_dat = np.mean(cur_data,0) |
| 193 | + |
| 194 | +print('CURRENT FEATURE:', aperiodic_feats[1]) |
| 195 | +disp_dat = topo_dat[:,1] |
| 196 | + |
| 197 | +inds = np.where(np.isnan(disp_dat)) |
| 198 | +disp_dat[inds] = np.nanmean(disp_dat) |
| 199 | + |
| 200 | +vbuffer = 0.1 * (disp_dat.max() - disp_dat.min()) |
| 201 | +vmin, vmax, = disp_dat.min() - vbuffer, disp_dat.max() + vbuffer |
| 202 | + |
| 203 | +fig, ax = plt.subplots() |
| 204 | +plot_topo_colorbar(vmin, vmax, exponent_results[1]) |
| 205 | +mne.viz.plot_topomap(disp_dat, raw.info, vmin=vmin, vmax=vmax, cmap=cm.viridis, contours=0, axes=ax) |
0 commit comments