diff --git a/doc/conf.py b/doc/conf.py index c2765602..5e94cd4f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -124,6 +124,8 @@ "n_estimated_nodes", "n_samples", "n_channels", + "n_patterns", + "n_filters", "Renderer", "n_ytimes", "n_ychannels", diff --git a/examples/decoding/cohy_decomposition.py b/examples/decoding/cohy_decomposition.py index f4550b86..26a2e18c 100644 --- a/examples/decoding/cohy_decomposition.py +++ b/examples/decoding/cohy_decomposition.py @@ -3,10 +3,10 @@ Multivariate decomposition for efficient connectivity analysis ============================================================== -This example demonstrates how the tools in the decoding module can be used to -decompose data into the most relevant components of connectivity and used for -a computationally efficient multivariate analysis of connectivity, such as in -brain-computer interface (BCI) applications. +This example demonstrates how the tools in the decoding module can be used to decompose +data into the most relevant components of connectivity and used for a computationally +efficient multivariate analysis of connectivity, such as in brain-computer interface +(BCI) applications. """ # Author: Thomas S. Binns @@ -21,23 +21,22 @@ from matplotlib import pyplot as plt from mne_connectivity import ( + CoherencyDecomposition, make_signals_in_freq_bands, seed_target_indices, spectral_connectivity_epochs, ) -from mne_connectivity.decoding import CoherencyDecomposition ######################################################################################## # Background # ---------- -# -# Multivariate forms of signal analysis allow you to simultaneously consider -# the activity of multiple signals. In the case of connectivity, the -# interaction between multiple sensors can be analysed at once and the strongest -# components of this interaction captured in a lower-dimensional set of connectivity -# spectra. This approach brings not only practical benefits (e.g. easier -# interpretability of results from the dimensionality reduction), but can also offer -# methodological improvements (e.g. enhanced signal-to-noise ratio and reduced bias). +# Multivariate forms of signal analysis allow you to simultaneously consider the +# activity of multiple signals. In the case of connectivity, the interaction between +# multiple sensors can be analysed at once and the strongest components of this +# interaction captured in a lower-dimensional set of connectivity spectra. This approach +# brings not only practical benefits (e.g. easier interpretability of results from the +# dimensionality reduction), but can also offer methodological improvements (e.g. +# enhanced signal-to-noise ratio and reduced bias). # # Coherency-based methods are popular approaches for analysing connectivity, capturing # correlations between signals in the frequency domain. Various coherency-based @@ -244,8 +243,7 @@ # connectivity is present. This problem can be mitigated by fitting filters to only # those frequencies where you expect connectivity to be present, e.g. as is done with # the decomposition class. - -######################################################################################## +# # In addition to assessing the validity of the approach, we can also look at the time # taken to run the analysis. Doing so, we see that the decomposition class is much # faster than the ``spectral_connectivity_...()`` functions, thanks to the fact that the @@ -550,6 +548,20 @@ # Ultimately, there are distinct advantages and disadvantages to both approaches, and # one may be more suitable than the other depending on your use case. +######################################################################################## +# Visualising spatial contributions to connectivity +# ------------------------------------------------- +# In addition to the lower-dimensional representation of connectivity, we can also +# extract information about the spatial distributions of connectivity over channels. +# This information is captured in the spatial patterns, derived from the spatial +# filters :footcite:`HaufeEtAl2014`. +# +# The patterns (and filters) can be visualised as topomaps using the +# :meth:`~mne_connectivity.decoding.CoherencyDecomposition.plot_patterns` and +# :meth:`~mne_connectivity.decoding.CoherencyDecomposition.plot_filters` methods of the +# :class:`~mne_connectivity.decoding.CoherencyDecomposition` class, discussed in more +# detail in :doc:`cohy_decomposition_plotting`. + ######################################################################################## # References # ---------- diff --git a/examples/decoding/cohy_decomposition_plotting.py b/examples/decoding/cohy_decomposition_plotting.py new file mode 100644 index 00000000..780495b4 --- /dev/null +++ b/examples/decoding/cohy_decomposition_plotting.py @@ -0,0 +1,172 @@ +""" +============================================================== +Visualising spatial contributions to multivariate connectivity +============================================================== + +This example demonstrates how the spatial filters and patterns of connectivity obtained +from the decomposition tools in the decoding module can be visualised and interpreted. +""" + +# Author: Thomas S. Binns +# License: BSD (3-clause) + +# %% + +import mne +from mne import make_fixed_length_epochs +from mne.datasets.fieldtrip_cmc import data_path + +from mne_connectivity import CoherencyDecomposition + +######################################################################################## +# Background +# ---------- +# Multivariate forms of signal analysis allow you to simultaneously consider the +# activity of multiple signals. In the case of connectivity, the interaction between +# multiple sensors can be analysed at once and the strongest components of this +# interaction captured in a lower-dimensional set of connectivity spectra. This approach +# brings not only practical benefits (e.g. easier interpretability of results from the +# dimensionality reduction), but can also offer methodological improvements (e.g. +# enhanced signal-to-noise ratio and reduced bias). +# +# Coherency-based methods are popular approaches for analysing connectivity, capturing +# correlations between signals in the frequency domain. Various coherency-based +# multivariate methods exist, including: canonical coherency (CaCoh; multivariate +# measure of coherency/coherence) :footcite:`VidaurreEtAl2019`; and maximised imaginary +# coherency (MIC; multivariate measure of the imaginary part of coherency) +# :footcite:`EwaldEtAl2012`. +# +# These methods are described in detail in the following examples: +# - comparison of coherency-based methods - :doc:`../compare_coherency_methods` +# - CaCoh - :doc:`../cacoh` +# - MIC - :doc:`../mic_mim` +# +# The CaCoh and MIC methods work by finding spatial filters that decompose the data into +# components of connectivity, and applying them to the data. Connectivity can then be +# computed on this transformed data (see :doc:`cohy_decomposition` for more +# information). +# +# However, in addition to the connectivity scores, useful insights about the data can be +# gained by visualising the topographies of the spatial filters and their corresponding +# spatial patterns. These provide important information about the spatial distributions +# of connectivity information, and represent two complementary aspects: +# +# - The filters represent how the connectivity sources are extracted from the channel +# data, akin to an inverse model. +# - The patterns represent how the channel data is formed by the connectivity sources, +# akin to a forward model. +# +# This distinction is discussed further in Haufe *et al.* (2014) +# :footcite:`HaufeEtAl2014`, but in short: **the patterns should be used to interpret +# the contribution of distinct brain regions/sensors to a given component of +# connectivity**. Accordingly, keep in mind that the filters and patterns are not a +# replacement for source reconstruction, as without this the patterns will still only +# tell you about the spatial contributions of sensors, not underlying brain regions, +# to connectivity. + +######################################################################################## +# Generating the filters and patterns +# ----------------------------------- +# We will first load some example MEG data collected during a hand movement task, which +# we will generate the spatial filters and patterns for (see +# `here `_ for more information on +# the data). We divide the data into continuous epochs. + +# %% + +# Load example MEG data +raw = mne.io.read_raw_ctf(data_path() / "SubjectCMC.ds") +raw.pick("mag") +raw.crop(50.0, 110.0).load_data() +raw.notch_filter(50) +raw.resample(100) + +# Create epochs +epochs = make_fixed_length_epochs(raw, duration=2.0).load_data() + +######################################################################################## +# We designate the left hemisphere sensors as the seeds and the right hemisphere sensors +# as the targets. Since this is sensor-space data, we will use the MIC method to analyse +# connectivity, given its resilience to zero time-lag interactions (see +# :doc:`../compare_coherency_methods` for more information). + +# %% + +# Left hemisphere sensors +seeds = [idx for idx, ch_info in enumerate(epochs.info["chs"]) if ch_info["loc"][0] < 0] + +# Right hemisphere sensors +targets = [ + idx for idx, ch_info in enumerate(epochs.info["chs"]) if ch_info["loc"][0] > 0 +] + +# Define indices +indices = (seeds, targets) + +######################################################################################## +# To fit the filters (and in turn compute the corresponding patterns), we instantiate +# the :class:`~mne_connectivity.decoding.CoherencyDecomposition` object and call the +# :meth:`~mne_connectivity.decoding.CoherencyDecomposition.fit` method. We also define +# our connectivity frequency band of interest to be 20-30 Hz. See +# :doc:`cohy_decomposition` for more information. + +# %% + +# Instantiate decomposition object +mic = CoherencyDecomposition( + info=epochs.info, + method="mic", + indices=indices, + mode="multitaper", + fmin=20, + fmax=30, + rank=(3, 3), +) + +# Fit filters & generate patterns +mic.fit(epochs.get_data()) + +######################################################################################## +# Visualising the patterns +# ------------------------ +# Visualising the patterns as topomaps can be done using the +# :meth:`~mne_connectivity.decoding.CoherencyDecomposition.plot_patterns` method. +# +# When interpreting patterns, note that the absolute value reflects the strength of the +# contribution to connectivity, and that the sign differences can be used to visualise +# the orientation of the underlying dipole sources. The spatial patterns are **not** +# bound between :math:`[-1, 1]`. +# +# Plotting the patterns for 20-30 Hz connectivity below, we find the strongest +# connectivity between the left and right hemispheres comes from centromedial left and +# frontolateral right sensors, based on the areas with the largest absolute values. As +# these patterns come from decomposition on sensor-space data, we make no assumptions +# about the underlying brain regions involved in this connectivity. + +# %% + +# Plot patterns +mic.plot_patterns(info=epochs.info, sensors="m.", size=2) + +######################################################################################## +# Visualising the filters +# ----------------------- +# We can also visualise the filters as topomaps using the +# :meth:`~mne_connectivity.decoding.CoherencyDecomposition.plot_filters` method. +# +# Here we see that the filters show a similar topography to the patterns. However, this +# is not always the case, and you should never confuse the information represented by +# the filters (i.e. an inverse model) and patterns (i.e. a forward model), which can +# lead to very incorrect interpretations of the data :footcite:`HaufeEtAl2014`. + +# %% + +# Plot filters +mic.plot_filters(info=epochs.info, sensors="m.", size=2) + +######################################################################################## +# References +# ---------- +# .. footbibliography:: + +# %% diff --git a/mne_connectivity/__init__.py b/mne_connectivity/__init__.py index 92c8b7e0..ce18a284 100644 --- a/mne_connectivity/__init__.py +++ b/mne_connectivity/__init__.py @@ -24,6 +24,7 @@ TemporalConnectivity, ) from .datasets import make_signals_in_freq_bands +from .decoding import CoherencyDecomposition from .effective import phase_slope_index from .envelope import envelope_correlation, symmetric_orth from .io import read_connectivity diff --git a/mne_connectivity/decoding/decomposition.py b/mne_connectivity/decoding/decomposition.py index 9d86410a..1263cd86 100644 --- a/mne_connectivity/decoding/decomposition.py +++ b/mne_connectivity/decoding/decomposition.py @@ -8,10 +8,14 @@ import numpy as np from mne import Info +from mne._fiff.pick import pick_info from mne.decoding.mixin import TransformerMixin +from mne.defaults import _BORDER_DEFAULT, _EXTRAPOLATE_DEFAULT, _INTERPOLATION_DEFAULT +from mne.evoked import EvokedArray from mne.fixes import BaseEstimator from mne.time_frequency import csd_array_fourier, csd_array_morlet, csd_array_multitaper from mne.utils import _check_option, _validate_type +from mne.viz.utils import plt_show from ..spectral.epochs_multivariate import _CaCohEst, _check_rank_input, _MICEst from ..utils import _check_multivariate_indices, fill_doc @@ -465,3 +469,304 @@ def get_transformed_indices(self): np.arange(self.n_components), np.arange(self.n_components) + self.n_components, ) + + @fill_doc + def plot_patterns( + self, + info, + components=None, + ch_type=None, + scalings=None, + sensors=True, + show_names=False, + mask=None, + mask_params=None, + contours=6, + outlines="head", + sphere=None, + image_interp=_INTERPOLATION_DEFAULT, + extrapolate=_EXTRAPOLATE_DEFAULT, + border=_BORDER_DEFAULT, + res=64, + size=1, + cmap="RdBu_r", + vlim=(None, None), + cnorm=None, + colorbar=True, + cbar_fmt="%.1E", + units="AU", + axes=None, + name_format=None, + nrows=1, + ncols="auto", + show=True, + ): + """Plot topographic patterns of components. + + The patterns explain how the measured data was generated from the + neural sources (a.k.a. the forward model) :footcite:`HaufeEtAl2014`. + + Seed and target patterns are plotted separately. + + Parameters + ---------- + %(info_decoding_plotting)s + %(components_topomap)s + %(ch_type_topomap)s + %(scalings_topomap)s + %(sensors_topomap)s + %(show_names_topomap)s + %(mask_patterns_topomap)s + %(mask_params_topomap)s + %(contours_topomap)s + %(outlines_topomap)s + %(sphere_topomap)s + %(image_interp_topomap)s + %(extrapolate_topomap)s + %(border_topomap)s + %(res_topomap)s + %(size_topomap)s + %(cmap_topomap)s + %(vlim_topomap)s + %(cnorm_topomap)s + %(colorbar_topomap)s + %(colorbar_format_topomap)s + %(units_topomap)s + %(axes_topomap)s + %(name_format_topomap)s + %(nrows_topomap)s + %(ncols_topomap)s + %(show)s + + Returns + ------- + %(figs_topomap)s + """ + if self.patterns_ is None: + raise RuntimeError( + "no patterns are available, please call the `fit` method first" + ) + + return self._plot_filters_patterns( + (self.patterns_[0].T, self.patterns_[1].T), + info, + components, + ch_type, + scalings, + sensors, + show_names, + mask, + mask_params, + contours, + outlines, + sphere, + image_interp, + extrapolate, + border, + res, + size, + cmap, + vlim, + cnorm, + colorbar, + cbar_fmt, + units, + axes, + name_format, + nrows, + ncols, + show, + ) + + @fill_doc + def plot_filters( + self, + info, + components=None, + ch_type=None, + scalings=None, + sensors=True, + show_names=False, + mask=None, + mask_params=None, + contours=6, + outlines="head", + sphere=None, + image_interp=_INTERPOLATION_DEFAULT, + extrapolate=_EXTRAPOLATE_DEFAULT, + border=_BORDER_DEFAULT, + res=64, + size=1, + cmap="RdBu_r", + vlim=(None, None), + cnorm=None, + colorbar=True, + cbar_fmt="%.1E", + units="AU", + axes=None, + name_format=None, + nrows=1, + ncols="auto", + show=True, + ): + """Plot topographic filters of components. + + The filters are used to extract discriminant neural sources from the measured + data (a.k.a. the backward model). :footcite:`HaufeEtAl2014`. + + Seed and target filters are plotted separately. + + Parameters + ---------- + %(info_decoding_plotting)s + %(components_topomap)s + %(ch_type_topomap)s + %(scalings_topomap)s + %(sensors_topomap)s + %(show_names_topomap)s + %(mask_filters_topomap)s + %(mask_params_topomap)s + %(contours_topomap)s + %(outlines_topomap)s + %(sphere_topomap)s + %(image_interp_topomap)s + %(extrapolate_topomap)s + %(border_topomap)s + %(res_topomap)s + %(size_topomap)s + %(cmap_topomap)s + %(vlim_topomap)s + %(cnorm_topomap)s + %(colorbar_topomap)s + %(colorbar_format_topomap)s + %(units_topomap)s + %(axes_topomap)s + %(name_format_topomap)s + %(nrows_topomap)s + %(ncols_topomap)s + %(show)s + + Returns + ------- + %(figs_topomap)s + """ + if self.filters_ is None: + raise RuntimeError( + "no filters are available, please call the `fit` method first" + ) + + return self._plot_filters_patterns( + self.filters_, + info, + components, + ch_type, + scalings, + sensors, + show_names, + mask, + mask_params, + contours, + outlines, + sphere, + image_interp, + extrapolate, + border, + res, + size, + cmap, + vlim, + cnorm, + colorbar, + cbar_fmt, + units, + axes, + name_format, + nrows, + ncols, + show, + ) + + def _plot_filters_patterns( + self, + plot_data, + info, + components, + ch_type, + scalings, + sensors, + show_names, + mask, + mask_params, + contours, + outlines, + sphere, + image_interp, + extrapolate, + border, + res, + size, + cmap, + vlim, + cnorm, + colorbar, + cbar_fmt, + units, + axes, + name_format, + nrows, + ncols, + show, + ): + """Plot filters/targets for components.""" + # Sort inputs + _validate_type(info, Info, "`info`", "mne.Info") + if components is None: + components = np.arange(self.n_components) + + # plot seeds and targets + figs = [] + for group_idx, group_name in zip([0, 1], ["Seeds", "Targets"]): + # create info for seeds/targets + group_info = pick_info(info, self.indices[group_idx]) + with group_info._unlock(): + group_info["sfreq"] = 1.0 # 1 component per time point + # create Evoked object + evoked = EvokedArray(plot_data[group_idx], group_info, tmin=0) + # then call plot_topomap + figs.append( + evoked.plot_topomap( + times=components, + average=None, # do not average across independent components + ch_type=ch_type, + scalings=scalings, + sensors=sensors, + show_names=show_names, + mask=mask, + mask_params=mask_params, + contours=contours, + outlines=outlines, + sphere=sphere, + image_interp=image_interp, + extrapolate=extrapolate, + border=border, + res=res, + size=size, + cmap=cmap, + vlim=vlim, + cnorm=cnorm, + colorbar=colorbar, + cbar_fmt=cbar_fmt, + units=units, + axes=axes, + time_format=f"{self._conn_estimator.name}%01d" + if name_format is None + else name_format, + nrows=nrows, + ncols=ncols, + show=False, # set Seeds/Targets suptitle first + ) + ) + figs[-1].suptitle(group_name) # differentiate seeds from targets + plt_show(show=show, fig=figs[-1]) + + return figs diff --git a/mne_connectivity/decoding/tests/test_decomposition.py b/mne_connectivity/decoding/tests/test_decomposition.py index be7931a2..7fdc701f 100644 --- a/mne_connectivity/decoding/tests/test_decomposition.py +++ b/mne_connectivity/decoding/tests/test_decomposition.py @@ -1,13 +1,14 @@ import numpy as np import pytest +from mne.channels import make_dig_montage, make_standard_montage from numpy.testing import assert_allclose from mne_connectivity import ( + CoherencyDecomposition, make_signals_in_freq_bands, seed_target_indices, spectral_connectivity_epochs, ) -from mne_connectivity.decoding import CoherencyDecomposition from mne_connectivity.utils import _check_multivariate_indices @@ -217,8 +218,26 @@ def test_spectral_decomposition(method, mode): # Test rank can be reset to default decomp_class.set_params(rank=None) - -test_spectral_decomposition("cacoh", "cwt_morlet") + # TEST PLOTTING + # Test plot filters/patterns + # use standard montage to avoid errors around weird fiducial positions + standard_1020_pos = make_standard_montage("standard_1020").get_positions() + epochs.info.set_montage( + make_dig_montage( + ch_pos={ + name: [idx, idx, idx] + for idx, name in enumerate(epochs.info["ch_names"]) + }, # avoid overlapping positions for channels (raises error) + nasion=standard_1020_pos["nasion"], + lpa=standard_1020_pos["lpa"], + rpa=standard_1020_pos["rpa"], + ) + ) + for plot in (decomp_class.plot_filters, decomp_class.plot_patterns): + # XXX: required for this to be picked up by coverage + figs = plot(epochs.info, components=0, units="A.U.", show=False) + figs = plot(epochs.info, components=None, units=None, show=False) + assert len(figs) == 2 @pytest.mark.parametrize("method", ["cacoh", "mic"]) @@ -497,6 +516,19 @@ def test_spectral_decomposition_error_catch(method, mode): ): decomp_class.transform(X=epochs.get_data()) + # TEST PLOTTING BEFORE FITTING + with pytest.raises( + RuntimeError, + match="no filters are available, please call the `fit` method first", + ): + decomp_class.plot_filters(epochs.info) + + with pytest.raises( + RuntimeError, + match="no patterns are available, please call the `fit` method first", + ): + decomp_class.plot_patterns(epochs.info) + decomp_class.set_params(n_components=None) # reset to default decomp_class.fit(X=epochs.get_data()) @@ -507,3 +539,8 @@ def test_spectral_decomposition_error_catch(method, mode): decomp_class.transform(X=epochs.get_data()[0, 0]) with pytest.raises(ValueError, match="`X` does not match Info"): decomp_class.transform(X=epochs.get_data()[:, :-1]) + + # TEST BAD PLOTTING + for plot in (decomp_class.plot_filters, decomp_class.plot_patterns): + with pytest.raises(TypeError, match="`info` must be an instance of mne.Info"): + plot({"info": epochs.info}) diff --git a/mne_connectivity/spectral/epochs_multivariate.py b/mne_connectivity/spectral/epochs_multivariate.py index e435cfef..a6edeec1 100644 --- a/mne_connectivity/spectral/epochs_multivariate.py +++ b/mne_connectivity/spectral/epochs_multivariate.py @@ -110,8 +110,8 @@ def __init__( self.n_cons = n_cons self.n_freqs = n_freqs self.n_times = n_times - self.n_jobs = n_jobs self.store_filters = store_filters + self.n_jobs = n_jobs # include time dimension, even when unused for indexing flexibility if n_times == 0: @@ -183,7 +183,7 @@ class _MultivariateCohEstBase(_EpochMeanMultivariateConEstBase): """Base estimator for multivariate coherency methods. See: - - Imaginary part of coherency, i.e. multivariate imaginary part of + - Imaginary part of coherency, i.e. maximised imaginary part of coherency (MIC) and multivariate interaction measure (MIM): Ewald et al. (2012). NeuroImage. DOI: 10.1016/j.neuroimage.2011.11.084 - Coherency/coherence, i.e. canonical coherency (CaCoh): Vidaurre et al. diff --git a/mne_connectivity/utils/docs.py b/mne_connectivity/utils/docs.py index b7f0147f..c90100a4 100644 --- a/mne_connectivity/utils/docs.py +++ b/mne_connectivity/utils/docs.py @@ -1,5 +1,6 @@ """The documentation functions.""" # Authors: Eric Larson +# Thomas S. Binns # # License: BSD (3-clause) @@ -8,6 +9,7 @@ except ImportError: from mne.externals.doccer import indentcount_lines as _indentcount_lines # noqa + ############################################################################## # Define our standard documentation entries @@ -58,40 +60,40 @@ """ docdict["mode"] = """ -mode : str (default "multitaper") - The cross-spectral density computation method. Can be ``"multitaper"``, - ``"fourier"``, or ``"cwt_morlet"``. +mode : str (default 'multitaper') + The cross-spectral density computation method. Can be ``'multitaper'``, + ``'fourier'``, or ``'cwt_morlet'``. """ docdict["mt_bandwidth"] = """ mt_bandwidth : int | float | None (default None) The bandwidth of the multitaper windowing function in Hz to use when computing the - cross-spectral density. Only used if ``mode="multitaper"``. + cross-spectral density. Only used if ``mode='multitaper'``. """ docdict["mt_adaptive"] = """ mt_adaptive : bool (default False) Whether to use adaptive weights when combining the tapered spectra in the - cross-spectral density. Only used if ``mode="multitaper"``. + cross-spectral density. Only used if ``mode='multitaper'``. """ docdict["mt_low_bias"] = """ mt_low_bias : bool (default True) Whether to use tapers with over 90 percent spectral concentration within the bandwidth when computing the cross-spectral density. Only used if - ``mode="multitaper"``. + ``mode='multitaper'``. """ docdict["cwt_freqs"] = """ cwt_freqs : array of int or float | None (default None) The frequencies of interest in Hz. Must not be ``None`` and only used if - ``mode="cwt_morlet"``. + ``mode='cwt_morlet'``. """ docdict["cwt_n_cycles"] = """ cwt_n_cycles : int | float | array of int or float (default 7) The number of cycles to use when constructing the Morlet wavelets. Fixed number or - one per frequency. Only used if ``mode=cwt_morlet``. + one per frequency. Only used if ``mode='cwt_morlet'``. """ docdict["coh"] = "'coh' : Coherence" @@ -183,7 +185,7 @@ ``None``. """ -# Decoding +# Decoding initialisation docdict["info_decoding"] = """ info : mne.Info Information about the data which will be decomposed and transformed, such as that @@ -195,20 +197,20 @@ method : str The multivariate method to use for the decomposition. Can be: - * ``"cacoh"`` - Canonical Coherency (CaCoh) :footcite:`VidaurreEtAl2019` - * ``"mic"`` - Maximised Imaginary part of Coherency (MIC) :footcite:`EwaldEtAl2012` + * ``'cacoh'`` - Canonical Coherency (CaCoh) :footcite:`VidaurreEtAl2019` + * ``'mic'`` - Maximised Imaginary part of Coherency (MIC) :footcite:`EwaldEtAl2012` """ docdict["fmin_decoding"] = """ fmin : int | float | None (default None) - The lowest frequency of interest in Hz. Must not be ``None`` and only used if - ``mode in ["multitaper", "fourier"]``. + The lowest frequency of interest in Hz. Must not be `None` and only used if + ``mode in ['multitaper', 'fourier']``. """ docdict["fmax_decoding"] = """ fmax : int | float | None (default None) - The highest frequency of interest in Hz. Must not be ``None`` and only used if - ``mode in ["multitaper", "fourier"]``. + The highest frequency of interest in Hz. Must not be `None` and only used if + ``mode in ['multitaper', 'fourier']``. """ docdict["indices_decoding"] = """ @@ -238,6 +240,7 @@ that of the data may reduce the degree of overfitting when computing the filters. """ +# Decoding attrs docdict["filters_"] = """ filters_ : tuple of array, shape=(n_signals, n_components) A tuple of two arrays containing the spatial filters for transforming the seed and @@ -250,6 +253,224 @@ filters for the seed and target data, respectively. """ +# Decoding plotting +docdict["info_decoding_plotting"] = """ +info : mne.Info + Information about the sensors of the data which has been decomposed, such as that + coming from an :class:`mne.Epochs` object. +""" + +# Topomaps +docdict["components_topomap"] = """ +components : int | array of int | None (default None) + The components to plot. If `None`, all components are shown. +""" + +docdict["ch_type_topomap"] = """ +ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None (default None) + The channel type to plot. For ``'grad'``, the gradiometers are collected in pairs + and the RMS for each pair is plotted. If `None`, the first available channel type + from the order shown above is used. +""" + +docdict["scalings_topomap"] = """ +scalings : dict | float | None (default None) + The scalings of the channel types to be applied for plotting. If `None`, uses + ``dict(eeg=1e6, grad=1e13, mag=1e15)``. +""" + +docdict["sensors_topomap"] = """ +sensors : bool | str (default True) + Whether to add markers for sensor locations. If `str`, should be a valid + matplotlib format string (e.g., ``'r+'`` for red plusses; see the Notes section of + :meth:`~matplotlib.axes.Axes.plot`). If `True`, black circles are used. +""" + +docdict["show_names_topomap"] = """ +show_names : bool | callable (default False) + Whether to show channel names next to each sensor marker. If `callable`, channel + names will be formatted using the callable; e.g., to delete the prefix 'MEG ' from + all channel names, pass the function ``lambda x: x.replace('MEG ', '')``. If + ``mask`` is not `None`, only non-masked sensor names will be shown. +""" + +docdict["mask_filters_topomap"] = """ +mask : array of bool, shape=(n_channels, n_filters) | None (default None) + An array specifying channel-filter combinations to highlight with a distinct + plotting style. Array elements set to `True` will be plotted with the parameters + given in ``mask_params``. If `None`, no combinations will be highlighted. +""" +docdict["mask_patterns_topomap"] = """ +mask : array of bool, shape=(n_channels, n_patterns) | None (default None) + An array specifying channel-pattern combinations to highlight with a distinct + plotting style. Array elements set to `True` will be plotted with the parameters + given in ``mask_params``. If `None`, no combinations will be highlighted. +""" + +docdict["mask_params_topomap"] = """ +mask_params : dict | None (default None) + The plotting parameters for distinct combinations given in ``mask``. + Default `None` equals:: + + dict(marker='o', markerfacecolor='w', markeredgecolor='k', + linewidth=0, markersize=4) +""" + +docdict["contours_topomap"] = """ +contours : int | array (default 6) + The number of contour lines to draw. If ``0``, no contours will be drawn. If a + positive integer, that number of contour levels are chosen using the matplotlib tick + locator (may sometimes be inaccurate, use array for accuracy). If an array-like, the + values are used as the contour levels. The values should be in µV for EEG, fT for + magnetometers and fT/m for gradiometers. If ``colorbar=True``, the colorbar will + have ticks corresponding to the contour levels. +""" + +docdict["outlines_topomap"] = """ +outlines : 'head' | dict | None (default 'head') + The outlines to be drawn. If 'head', the default head scheme will be drawn. If dict, + each key refers to a tuple of x and y positions, the values in 'mask_pos' will serve + as image mask. Alternatively, a matplotlib patch object can be passed for advanced + masking options, either directly or as a function that returns patches (required for + multi-axis plots). If `None`, nothing will be drawn. +""" + +docdict["sphere_topomap"] = """ +sphere : float | array | mne.bem.ConductorModel | None | 'auto' | 'eeglab' (default None) + The sphere parameters to use for the head outline. Can be array-like of shape (4,) + to give the X/Y/Z origin and radius in meters, or a single float to give just the + radius (origin assumed 0, 0, 0). Can also be an instance of a spherical + :class:`~mne.bem.ConductorModel` to use the origin and radius from that object. If + ``'auto'`` the sphere is fit to digitization points. If ``'eeglab'`` the head circle + is defined by EEG electrodes ``'Fpz'``, ``'Oz'``, ``'T7'``, and ``'T8'`` (if + ``'Fpz'`` is not present, it will be approximated from the coordinates of ``'Oz'``). + `None` is equivalent to ``'auto'`` when enough extra digitization points are + available, and (0, 0, 0, 0.95) otherwise. +""" # noqa E501 + +docdict["image_interp_topomap"] = """ +image_interp : str (default 'cubic') + The image interpolation to be used. Options are ``'cubic'`` to use + :class:`scipy.interpolate.CloughTocher2DInterpolator`, ``'nearest'`` to use + :class:`scipy.spatial.Voronoi`, or ``'linear'`` to use + :class:`scipy.interpolate.LinearNDInterpolator`. +""" + +docdict["extrapolate_topomap"] = """ +extrapolate : str + The extrapolation options. Can be one of: + + - ``'box'`` + Extrapolate to four points placed to form a square encompassing all data points, + where each side of the square is three times the range of the data in the + respective dimension. + - ``'local'`` (default for MEG sensors) + Extrapolate only to nearby points (approximately to points closer than median + inter-electrode distance). This will also set the mask to be polygonal based on + the convex hull of the sensors. + - ``'head'`` (default for non-MEG sensors) + Extrapolate out to the edges of the clipping circle. This will be on the head + circle when the sensors are contained within the head circle, but it can extend + beyond the head when sensors are plotted outside the head circle. +""" + +docdict["border_topomap"] = """ +border : float | 'mean' (default 'mean') + The value to extrapolate to on the topomap borders. If ``'mean'``, each extrapolated + point has the average value of its neighbours. +""" + +docdict["res_topomap"] = """ +res : int (default 64) + The resolution of the topomap image (number of pixels along each side). +""" + +docdict["size_topomap"] = """ +size : int | float (default 1) + The side length of each subplot in inches. +""" + +docdict["cmap_topomap"] = """ +cmap : str | matplotlib.colors.Colormap | (matplotlib.colors.Colormap, bool) | 'interactive' | None (default 'RdBu_r') + The colormap to use. If a `str`, should be a valid matplotlib colormap. If a + `tuple`, the first value is `matplotlib.colors.Colormap` object to use and the + second value is a boolean defining interactivity. In interactive mode the colors are + adjustable by clicking and dragging the colorbar with left and right mouse button. + Left mouse button moves the scale up and down and right mouse button adjusts the + range. Hitting space bar resets the range. Up and down arrows can be used to change + the colormap. If `None`, ``'Reds'`` is used for data that is either all positive or + all negative, and ``'RdBu_r'`` is used otherwise. ``'interactive'`` is equivalent to + ``(None, True)``. + + .. warning:: Interactive mode works smoothly only for a small amount + of topomaps. Interactive mode is disabled by default for more than + 2 topomaps. +""" # noqa E501 + +docdict["vlim_topomap"] = """ +vlim : tuple of length 2 (default (None, None)) + The lower and upper colormap bounds, respectively. If both entries are `None`, sets + bounds to ``(min(data), max(data))``. If one entry is `None`, the corresponding + boundary is set at the min/max of the data. +""" + +docdict["cnorm_topomap"] = """ +cnorm : matplotlib.colors.Normalize | None (default None) + How to normalize the colormap. If `None`, standard linear normalization is used. If + not `None`, ``vlim`` is ignored. See the :ref:`Matplotlib docs + ` for more details on colormap normalization. +""" + +docdict["colorbar_topomap"] = """ +colorbar : bool (default True) + Whether to plot a colorbar in the rightmost column of the figure. +""" + +docdict["colorbar_format_topomap"] = r""" +cbar_fmt : str (default '%.1E') + The formatting string for colorbar tick labels. See :ref:`formatspec` for details. +""" + +docdict["units_topomap"] = """ +units : str (default 'AU') + The units for the colorbar label. Ignored if ``colorbar=False``. +""" + +docdict["axes_topomap"] = """ +axes : matplotlib.axes.Axes | list of matplotlib.axes.Axes | None (default None) + The axes to plot to. If `None`, a new figure will be created with the correct number + of axes. If not `None`, the number of axes must match ``components``. +""" + +docdict["name_format_topomap"] = r""" +name_format : str | None (default None) + The string format for axes titles. If `None`, uses ``f"{method}%%01d"``, i.e. the + method name followed by the component number. +""" + +docdict["nrows_topomap"] = """ +nrows : int | 'auto' (default 'auto') + The number of rows of components to plot. If ``'auto'``, the necessary number will + be inferred. +""" + +docdict["ncols_topomap"] = """ +ncols : int | 'auto' (default 'auto') + The number of columns of components to plot. If ``'auto'``, the necessary number + will be inferred. If ``nrows='auto'`` and ``ncols='auto'``, becomes ``nrows=1, + ncols='auto'``. +""" + +docdict["figs_topomap"] = """ +figs : list of matplotlib.figure.Figure + The seed and target figures, respectively. +""" + +docdict["show"] = """ +show : bool (default True) + Whether to show the figure. +""" + docdict_indented = dict() # type: ignore