# noqa: E501 # Authors: The MNE-Python contributors. # License: BSD-3-Clause # Copyright the MNE-Python contributors. """ .. _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 `: """ # %% 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`: 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 `. # # 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` 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(picks="data", exclude="bads", amplitude=False) 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()`, # :meth:`Epochs.compute_psd()`, and # :meth:`Evoked.compute_psd()` methods, and pass # ``method='welch'`` or ``method='multitaper'`` as a parameter. # # The class methods :meth:`Raw.plot_psd()`, # :meth:`Epochs.plot_psd()`, # :meth:`Raw.plot_psd_topo()`, and # :meth:`Epochs.plot_psd_topomap()` have been # kept in the API to support legacy code, but should be avoided when writing # new code. # # # References # ---------- # .. footbibliography::