1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
# -*- coding: utf-8 -*-
# noqa: E501
"""
.. _tut-spectrum-class:
==============================================================
The Spectrum and EpochsSpectrum classes: frequency-domain data
==============================================================
This tutorial shows how to create and visualize frequency-domain
representations of your data, starting from continuous :class:`~mne.io.Raw`,
discontinuous :class:`~mne.Epochs`, or averaged :class:`~mne.Evoked` data.
As usual we'll start by importing the modules we need, and loading our
:ref:`sample dataset <sample-dataset>`:
"""
# %%
import numpy as np
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = (sample_data_folder / 'MEG' / 'sample' /
'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False).crop(tmax=60)
# %%
# All three sensor-space containers (:class:`~mne.io.Raw`,
# :class:`~mne.Epochs`, and :class:`~mne.Evoked`) have a
# :meth:`~mne.io.Raw.compute_psd` method with the same options.
raw.compute_psd()
# %%
# By default, the spectral estimation method will be the
# :footcite:t:`Welch1967` method for continuous data, and the multitaper
# method :footcite:`Slepian1978` for epoched or averaged data. This default can
# be overridden by passing ``method='welch'`` or ``method='multitaper'`` to the
# :meth:`~mne.io.Raw.compute_psd` method.
#
# There are many other options available as well; for example we can compute a
# spectrum from a given span of times, for a chosen frequency range, and for a
# subset of the available channels:
raw.compute_psd(method='multitaper', tmin=10, tmax=20, fmin=5, fmax=30,
picks='eeg')
# %%
# You can also pass some parameters to the underlying spectral estimation
# function, such as the FFT window length and overlap for the Welch method; see
# the docstrings of :class:`mne.time_frequency.Spectrum` (esp. its
# ``method_kw`` parameter) and the spectral estimation functions
# :func:`~mne.time_frequency.psd_array_welch` and
# :func:`~mne.time_frequency.psd_array_multitaper` for details.
#
# For epoched data, the class of the spectral estimate will be
# :class:`mne.time_frequency.EpochsSpectrum` instead of
# :class:`mne.time_frequency.Spectrum`, but most of the API is the same for the
# two classes. For example, both have a
# :meth:`~mne.time_frequency.EpochsSpectrum.get_data` method with an option to
# return the bin frequencies:
with mne.use_log_level('WARNING'): # hide some irrelevant info messages
events = mne.find_events(raw, stim_channel='STI 014')
event_dict = {'auditory/left': 1, 'auditory/right': 2, 'visual/left': 3,
'visual/right': 4}
epochs = mne.Epochs(raw, events, tmin=-0.3, tmax=0.7, event_id=event_dict,
preload=True)
epo_spectrum = epochs.compute_psd()
psds, freqs = epo_spectrum.get_data(return_freqs=True)
print(f'\nPSDs shape: {psds.shape}, freqs shape: {freqs.shape}')
epo_spectrum
# %%
# Additionally, both :class:`~mne.time_frequency.Spectrum` and
# :class:`~mne.time_frequency.EpochsSpectrum` have ``__getitem__`` methods,
# meaning their data can be accessed by square-bracket indexing. For
# :class:`~mne.time_frequency.Spectrum` objects (computed from
# :class:`~mne.io.Raw` or :class:`~mne.Evoked` data), the indexing works
# similar to a :class:`~mne.io.Raw` object or a
# :class:`NumPy array<numpy.ndarray>`:
evoked = epochs['auditory'].average()
evk_spectrum = evoked.compute_psd()
# the first 3 frequency bins for the first 4 channels:
print(evk_spectrum[:4, :3])
# %%
# .. hint::
# :class: sidebar
#
# If the original :class:`~mne.Epochs` object had a metadata dataframe
# attached, the derived :class:`~mne.time_frequency.EpochsSpectrum` will
# inherit that metadata and will hence also support subselecting epochs via
# :ref:`Pandas query strings <pandas:indexing.query>`.
#
# In contrast, the :class:`~mne.time_frequency.EpochsSpectrum` has indexing
# similar to :class:`~mne.Epochs` objects: you can use string values to select
# spectral estimates for specific epochs based on their condition names, and
# what you get back is a new instance of
# :class:`~mne.time_frequency.EpochsSpectrum` rather than a
# :class:`NumPy array<numpy.ndarray>` of the data values. Selection via
# :term:`hierarchical event descriptors` (HEDs) is also possible:
# get both "visual/left" and "visual/right" epochs:
epo_spectrum['visual']
# %%
# Visualizing Spectrum objects
# ----------------------------
#
# Both :class:`~mne.time_frequency.Spectrum` and
# :class:`~mne.time_frequency.EpochsSpectrum` objects have plotting methods
# :meth:`~mne.time_frequency.Spectrum.plot` (frequency × power),
# :meth:`~mne.time_frequency.Spectrum.plot_topo` (frequency × power separately
# for each sensor), and :meth:`~mne.time_frequency.Spectrum.plot_topomap`
# (interpolated scalp topography of power, in specific frequency bands). A few
# plot options are demonstrated below; see the docstrings for full details.
evk_spectrum.plot()
evk_spectrum.plot_topo(color='k', fig_facecolor='w', axis_facecolor='w')
# %%
evk_spectrum.plot_topomap(ch_type='eeg', agg_fun=np.median)
# %%
# Migrating legacy code
# ---------------------
#
# Below is a quick-reference table of equivalent code from before and after the
# introduction of the :class:`~mne.time_frequency.Spectrum` and
# :class:`~mne.time_frequency.EpochsSpectrum` classes.
#
# .. table:: Quick reference for common Spectral class actions
# :widths: auto
#
# +---------------------------------------------------+----------------------------------------------------------------------+
# | Old | New |
# +===================================================+======================================================================+
# | ``mne.time_frequency.psd_welch(raw)`` | ``raw.compute_psd().get_data(return_freqs=True)`` |
# +---------------------------------------------------+----------------------------------------------------------------------+
# | ``mne.time_frequency.psd_multitaper(raw)`` | ``raw.compute_psd(method='multitaper').get_data(return_freqs=True)`` |
# +---------------------------------------------------+----------------------------------------------------------------------+
# | ``raw.plot_psd(fmin, fmax, dB, area_mode='std')`` | ``raw.compute_psd(fmin, fmax).plot(dB, ci='std')`` |
# +---------------------------------------------------+----------------------------------------------------------------------+
# | ``raw.plot_psd_topo(n_fft, overlap, axes)`` | ``raw.compute_psd(n_fft, overlap).plot_topo(axes)`` |
# +---------------------------------------------------+----------------------------------------------------------------------+
# | ``epochs.plot_psd_topomap(tmax, bands)`` | ``epochs.compute_psd(tmax).plot_topomap(bands)`` |
# +---------------------------------------------------+----------------------------------------------------------------------+
#
#
# .. warning::
#
# The functions ``mne.time_frequency.psd_welch`` and
# ``mne.time_frequency.psd_multitaper`` have been removed; new code
# should use the :meth:`Raw.compute_psd()<mne.io.Raw.compute_psd>`,
# :meth:`Epochs.compute_psd()<mne.Epochs.compute_psd>`, and
# :meth:`Evoked.compute_psd()<mne.Evoked.compute_psd>` methods, and pass
# ``method='welch'`` or ``method='multitaper'`` as a parameter.
#
# The class methods :meth:`Raw.plot_psd()<mne.io.Raw.plot_psd>`,
# :meth:`Epochs.plot_psd()<mne.Epochs.plot_psd>`,
# :meth:`Raw.plot_psd_topo()<mne.io.Raw.plot_psd_topo>`, and
# :meth:`Epochs.plot_psd_topomap()<mne.Epochs.plot_psd_topomap>` have been
# kept in the API to support legacy code, but should be avoided when writing
# new code.
#
#
# References
# ----------
# .. footbibliography::
|