""" .. _ex-time-freq-global-field-power: =========================================================== Explore event-related dynamics for specific frequency bands =========================================================== The objective is to show you how to explore spectrally localized effects. For this purpose we adapt the method described in :footcite:`HariSalmelin1997` and use it on the somato dataset. The idea is to track the band-limited temporal evolution of spatial patterns by using the :term:`global field power` (GFP). We first bandpass filter the signals and then apply a Hilbert transform. To reveal oscillatory activity the evoked response is then subtracted from every single trial. Finally, we rectify the signals prior to averaging across trials by taking the magnitude of the Hilbert. Then the :term:`GFP` is computed as described in :footcite:`EngemannGramfort2015`, using the sum of the squares but without normalization by the rank. Baselining is subsequently applied to make the :term:`GFP` comparable between frequencies. The procedure is then repeated for each frequency band of interest and all :term:`GFPs` are visualized. To estimate uncertainty, non-parametric confidence intervals are computed as described in :footcite:`EfronHastie2016` across channels. The advantage of this method over summarizing the Space × Time × Frequency output of a Morlet Wavelet in frequency bands is relative speed and, more importantly, the clear-cut comparability of the spectral decomposition (the same type of filter is used across all bands). We will use this dataset: :ref:`somato-dataset` References ---------- .. footbibliography:: """ # noqa: E501 # Authors: Denis A. Engemann # Stefan Appelhoff # # License: BSD-3-Clause # Copyright the MNE-Python contributors. # %% import matplotlib.pyplot as plt import numpy as np import mne from mne.baseline import rescale from mne.datasets import somato from mne.stats import bootstrap_confidence_interval # %% # Set parameters data_path = somato.data_path() subject = "01" task = "somato" raw_fname = data_path / f"sub-{subject}" / "meg" / f"sub-{subject}_task-{task}_meg.fif" # let's explore some frequency bands iter_freqs = [("Theta", 4, 7), ("Alpha", 8, 12), ("Beta", 13, 25), ("Gamma", 30, 45)] # %% # We create average power time courses for each frequency band # set epoching parameters event_id, tmin, tmax = 1, -1.0, 3.0 baseline = None # get the header to extract events raw = mne.io.read_raw_fif(raw_fname) events = mne.find_events(raw, stim_channel="STI 014") frequency_map = list() for band, fmin, fmax in iter_freqs: # (re)load the data to save memory raw = mne.io.read_raw_fif(raw_fname) raw.pick(picks=["grad", "eog"]) # we just look at gradiometers raw.load_data() # bandpass filter raw.filter( fmin, fmax, n_jobs=None, # use more jobs to speed up. l_trans_bandwidth=1, # make sure filter params are the same h_trans_bandwidth=1, ) # in each band and skip "auto" option. # epoch epochs = mne.Epochs( raw, events, event_id, tmin, tmax, baseline=baseline, reject=dict(grad=4000e-13, eog=350e-6), preload=True, ) # remove evoked response epochs.subtract_evoked() # get analytic signal (envelope) epochs.apply_hilbert(envelope=True) frequency_map.append(((band, fmin, fmax), epochs.average())) del epochs del raw # %% # Now we can compute the Global Field Power # We can track the emergence of spatial patterns compared to baseline # for each frequency band, with a bootstrapped confidence interval. # # We see dominant responses in the Alpha and Beta bands. # Helper function for plotting spread def stat_fun(x): """Return sum of squares.""" return np.sum(x**2, axis=0) # Plot fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True) colors = plt.colormaps["winter_r"](np.linspace(0, 1, 4)) for ((freq_name, fmin, fmax), average), color, ax in zip( frequency_map, colors, axes.ravel()[::-1] ): times = average.times * 1e3 gfp = np.sum(average.data**2, axis=0) gfp = mne.baseline.rescale(gfp, times, baseline=(None, 0)) ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5) ax.axhline(0, linestyle="--", color="grey", linewidth=2) ci_low, ci_up = bootstrap_confidence_interval( average.data, random_state=0, stat_fun=stat_fun ) ci_low = rescale(ci_low, average.times, baseline=(None, 0)) ci_up = rescale(ci_up, average.times, baseline=(None, 0)) ax.fill_between(times, gfp + ci_up, gfp - ci_low, color=color, alpha=0.3) ax.grid(True) ax.set_ylabel("GFP") ax.annotate( f"{freq_name} ({fmin:d}-{fmax:d}Hz)", xy=(0.95, 0.8), horizontalalignment="right", xycoords="axes fraction", ) ax.set_xlim(-1000, 3000) axes.ravel()[-1].set_xlabel("Time [ms]")