From d4a893334d2884889f1152f53a48be4454773d73 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 10:23:40 -0600 Subject: [PATCH 01/27] prep: docdict --- mne/utils/docs.py | 21 +++++++++++++++++++++ mne/viz/topomap.py | 23 ++--------------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/mne/utils/docs.py b/mne/utils/docs.py index fbb1aa19087..bc7cba9fdbc 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -219,6 +219,20 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): Freesurfer subject directory. """ +docdict['average_plot_evoked_topomap'] = """ +average : float | array-like of float, shape (n_times,) | None + The time window (in seconds) around a given time point to be used for + averaging. For example, 0.2 would translate into a time window that + starts 0.1 s before and ends 0.1 s after the given time point. If the + time window exceeds the duration of the data, it will be clipped. + Different time windows (one per time point) can be provided by + passing an ``array-like`` object (e.g., ``[0.1, 0.2, 0.3]``). If + ``None`` (default), no averaging will take place. + + .. versionchanged:: 1.1 + Support for ``array-like`` input. +""" + docdict['average_plot_psd'] = """\ average : bool If False, the PSDs of all channels is displayed. No averaging @@ -2241,6 +2255,13 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): a power-of-two size (can be much faster). """ +docdict['nrows_ncols_topomap'] = """ +nrows, ncols : int | 'auto' + The number of rows and columns of topographies to plot. If either ``nrows`` + or ``ncols`` is ``'auto'``, the necessary number will be inferred. Defaults + to ``nrows=1, ncols='auto'``.\ +""" + # %% # O diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 1accf259a92..69e1b661882 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1472,17 +1472,7 @@ def plot_evoked_topomap( automatically by checking for local maxima in global field power. If "interactive", the time can be set interactively at run-time by using a slider. - average : float | array-like of float, shape (n_times,) | None - The time window (in seconds) around a given time point to be used for - averaging. For example, 0.2 would translate into a time window that - starts 0.1 s before and ends 0.1 s after the given time point. If the - time window exceeds the duration of the data, it will be clipped. - Different time windows (one per time point) can be provided by - passing an ``array-like`` object (e.g., ``[0.1, 0.2, 0.3]``). If - ``None`` (default), no averaging will take place. - - .. versionchanged:: 1.1 - Support for ``array-like`` input. + %(average_plot_evoked_topomap)s %(ch_type_topomap)s %(scalings_topomap)s %(proj_plot)s @@ -1519,16 +1509,7 @@ def plot_evoked_topomap( String format for topomap values. Defaults (None) to "%%01d ms" if ``time_unit='ms'``, "%%0.3f s" if ``time_unit='s'``, and "%%g" otherwise. Can be an empty string to omit the time label. - nrows : int | 'auto' - The number of rows of topographies to plot. Defaults to 1. If 'auto', - obtains the number of rows depending on the amount of times to plot - and the number of cols. Not valid when times == 'interactive'. - - .. versionadded:: 0.20 - ncols : int | 'auto' - The number of columns of topographies to plot. If 'auto' (default), - obtains the number of columns depending on the amount of times to plot - and the number of rows. Not valid when times == 'interactive'. + %(nrows_ncols_topomap)s Ignored when times == 'interactive'. .. versionadded:: 0.20 %(show)s From d47720c78b80ef80787a3f855166d0ec0b04c56e Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 10:24:20 -0600 Subject: [PATCH 02/27] update CSP.plot_patterns() --- mne/decoding/csp.py | 159 +++++++++++++++++++++----------------------- mne/viz/utils.py | 14 ++++ 2 files changed, 88 insertions(+), 85 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index 4807d8325b6..14ea55b67b8 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -14,9 +14,15 @@ from .base import BaseEstimator from .mixin import TransformerMixin from ..cov import _regularized_covariance -from ..defaults import _INTERPOLATION_DEFAULT +from ..defaults import (_BORDER_DEFAULT, _EXTRAPOLATE_DEFAULT, + _INTERPOLATION_DEFAULT) from ..fixes import pinv -from ..utils import fill_doc, _check_option, _validate_type, copy_doc +from ..utils import fill_doc, _check_option, _validate_type, copy_doc, warn +from ..viz.utils import _warn_deprecated_vmin_vmax + +TITLE_WARNING_MSG = ( + 'The "title" parameter is deprecated and will be removed in version 1.4. ' + 'Use "fig.suptitle()" instead.') @fill_doc @@ -237,14 +243,16 @@ def fit_transform(self, X, y, **fit_params): # noqa: D102 return super().fit_transform(X, y=y, **fit_params) @fill_doc - def plot_patterns(self, info, components=None, ch_type=None, - vmin=None, vmax=None, cmap='RdBu_r', sensors=True, - colorbar=True, scalings=None, units='a.u.', res=64, - size=1, cbar_fmt='%3.1f', name_format='CSP%01d', - show=True, show_names=False, title=None, mask=None, - mask_params=None, outlines='head', contours=6, - image_interp=_INTERPOLATION_DEFAULT, average=None, - sphere=None): + def plot_patterns( + self, info, components=None, *, average=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), vmin=None, vmax=None, + cnorm=None, colorbar=True, cbar_fmt='%3.1f', units=None, + axes=None, name_format='CSP%01d', title=None, nrows=1, + ncols='auto', show=True): """Plot topographic patterns of components. The patterns explain how the measured data was generated from the @@ -255,80 +263,57 @@ def plot_patterns(self, info, components=None, ch_type=None, %(info_not_none)s Used for fitting. If not available, consider using :func:`mne.create_info`. components : float | array of float | None - The patterns to plot. If None, n_components will be shown. - ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None - The channel type to plot. For 'grad', the gradiometers are - collected in pairs and the RMS for each pair is plotted. - If None, then first available channel type from order given - above is used. Defaults to None. - vmin : float | callable - The value specifying the lower bound of the color range. - If None, and vmax is None, -vmax is used. Else np.min(data). - If callable, the output equals vmin(data). - vmax : float | callable - The value specifying the upper bound of the color range. - If None, the maximum absolute value is used. If vmin is None, - but vmax is not, default np.min(data). - If callable, the output equals vmax(data). - cmap : matplotlib colormap | (colormap, bool) | 'interactive' | None - Colormap to use. If tuple, the first value indicates the colormap - 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 all - positive data, otherwise defaults to 'RdBu_r'. If 'interactive', - translates to (None, True). Defaults to 'RdBu_r'. - - .. warning:: Interactive mode works smoothly only for a small - amount of topomaps. - sensors : bool | str - Add markers for sensor locations to the plot. Accepts matplotlib - plot format string (e.g., 'r+' for red plusses). If True, - a circle will be used (via .add_artist). Defaults to True. - colorbar : bool - Plot a colorbar. + The patterns to plot. If ``None``, all components will be shown. + %(average_plot_evoked_topomap)s + %(ch_type_topomap)s scalings : dict | float | None The scalings of the channel types to be applied for plotting. If None, defaults to ``dict(eeg=1e6, grad=1e13, mag=1e15)``. - units : dict | str | None - The unit of the channel type used for colorbar label. If - scale is None the unit is automatically determined. - res : int - The resolution of the topomap image (n pixels along each side). - size : float - Side length per topomap in inches. - cbar_fmt : str - String format for colorbar values. - name_format : str - String format for topomap values. Defaults to "CSP%%01d". - show : bool - Show figure if True. - show_names : bool | callable - If True, show channel names on top of the map. If a callable is - passed, 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 significant sensors will be shown. - title : str | None - Title. If None (default), no title is displayed. + %(sensors_topomap)s + %(show_names_topomap)s %(mask_patterns_topomap)s %(mask_params_topomap)s + %(contours_topomap)s %(outlines_topomap)s - contours : int | array of float - The number of contour lines to draw. If 0, no contours will be - drawn. When an integer, matplotlib ticker locator is used to find - suitable values for the contour thresholds (may sometimes be - inaccurate, use array for accuracy). If an array, the values - represent the levels for the contours. Defaults to 6. - %(image_interp_topomap)s - average : float | None - The time window around a given time to be used for averaging - (seconds). For example, 0.01 would translate into window that - starts 5 ms before and ends 5 ms after a given time point. - Defaults to None, which means no averaging. %(sphere_topomap_auto)s + %(image_interp_topomap)s + %(extrapolate_topomap)s + + .. versionadded:: 1.3 + %(border_topomap)s + + .. versionadded:: 1.3 + %(res_topomap)s + %(size_topomap)s + %(cmap_topomap)s + %(vlim_plot_topomap_psd)s + + .. versionadded:: 1.3 + %(vmin_vmax_topomap)s + + .. deprecated:: v1.4 + The ``vmin`` and ``vmax`` parameters will be removed in version + 1.4. Please use the ``vlim`` parameter instead. + %(cnorm)s + + .. versionadded:: 1.3 + %(colorbar_topomap)s + %(cbar_fmt_topomap)s + %(units_topomap_evoked)s + %(axes_evoked_plot_topomap)s + name_format : str + String format for topomap values. Defaults to "CSP%%01d". + %(title_none)s + + .. deprecated:: v1.4 + The ``title`` parameter will be removed in version 1.4. Please + use :meth:`fig.suptitle()` + instead. + %(nrows_ncols_topomap)s + + .. versionadded:: 1.3 + show : bool + Show figure if True. Returns ------- @@ -339,6 +324,8 @@ def plot_patterns(self, info, components=None, ch_type=None, if components is None: components = np.arange(self.n_components) + vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') + # set sampling frequency to have 1 component per time point info = cp.deepcopy(info) with info._unlock(): @@ -347,15 +334,17 @@ def plot_patterns(self, info, components=None, ch_type=None, patterns = EvokedArray(self.patterns_.T, info, tmin=0) # the call plot_topomap fig = patterns.plot_topomap( - times=components, ch_type=ch_type, - vlim=(vmin, vmax), cmap=cmap, colorbar=colorbar, res=res, - cbar_fmt=cbar_fmt, sensors=sensors, - scalings=scalings, units=units, time_unit='s', - time_format=name_format, size=size, show_names=show_names, - mask_params=mask_params, mask=mask, outlines=outlines, - contours=contours, image_interp=image_interp, show=show, - average=average, sphere=sphere) + times=components, average=average, 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=name_format, + nrows=nrows, ncols=ncols, show=show) + if title is not None: + warn(TITLE_WARNING_MSG, FutureWarning) fig.suptitle(title) return fig diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 33275a501a3..8ae413ce681 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -101,6 +101,20 @@ def _setup_vmin_vmax(data, vmin, vmax, norm=False): return vmin, vmax +def _warn_deprecated_vmin_vmax(vlim, vmin, vmax, version): + if vmin is not None or vmax is not None: + warn('The "vmin" and "vmax" parameters are deprecated and will be ' + f'removed in version {version}. Use the "vlim" parameter ' + 'instead.', FutureWarning) + if vlim[0] is None and vlim[1] is None: + vlim = (vmin, vmax) + else: + warn('You provided either "vmin" or "vmax" (which are ' + 'deprecated) as well as "vlim". Using "vlim" and ' + 'ignoring "vmin" and "vmax".') + return vlim + + def plt_show(show=True, fig=None, **kwargs): """Show a figure while suppressing warnings. From 4a34e624b24144e40241eb8bfd61c10e91ba7ea9 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 10:39:55 -0600 Subject: [PATCH 03/27] update CSP.plot_filters() --- mne/decoding/csp.py | 153 +++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 87 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index 14ea55b67b8..2eec8c878f0 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -349,13 +349,16 @@ def plot_patterns( return fig @fill_doc - def plot_filters(self, info, components=None, ch_type=None, - vmin=None, vmax=None, cmap='RdBu_r', sensors=True, - colorbar=True, scalings=None, units='a.u.', res=64, - size=1, cbar_fmt='%3.1f', name_format='CSP%01d', - show=True, show_names=False, title=None, mask=None, - mask_params=None, outlines='head', contours=6, - image_interp=_INTERPOLATION_DEFAULT, average=None): + def plot_filters( + self, info, components=None, *, average=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), vmin=None, vmax=None, + cnorm=None, colorbar=True, cbar_fmt='%3.1f', units=None, + axes=None, name_format='CSP%01d', title=None, nrows=1, + ncols='auto', show=True): """Plot topographic filters of components. The filters are used to extract discriminant neural sources from @@ -366,86 +369,57 @@ def plot_filters(self, info, components=None, ch_type=None, %(info_not_none)s Used for fitting. If not available, consider using :func:`mne.create_info`. components : float | array of float | None - The patterns to plot. If None, n_components will be shown. - ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None - The channel type to plot. For 'grad', the gradiometers are - collected in pairs and the RMS for each pair is plotted. - If None, then first available channel type from order given - above is used. Defaults to None. - vmin : float | callable - The value specifying the lower bound of the color range. - If None, and vmax is None, -vmax is used. Else np.min(data). - If callable, the output equals vmin(data). - vmax : float | callable - The value specifying the upper bound of the color range. - If None, the maximum absolute value is used. If vmin is None, - but vmax is not, defaults to np.min(data). - If callable, the output equals vmax(data). - cmap : matplotlib colormap | (colormap, bool) | 'interactive' | None - Colormap to use. If tuple, the first value indicates the colormap - 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 all - positive data, otherwise defaults to 'RdBu_r'. If 'interactive', - translates to (None, True). Defaults to 'RdBu_r'. - - .. warning:: Interactive mode works smoothly only for a small - amount of topomaps. - sensors : bool | str - Add markers for sensor locations to the plot. Accepts matplotlib - plot format string (e.g., 'r+' for red plusses). If True, - a circle will be used (via .add_artist). Defaults to True. - colorbar : bool - Plot a colorbar. + The patterns to plot. If ``None``, all components will be shown. + %(average_plot_evoked_topomap)s + %(ch_type_topomap)s scalings : dict | float | None The scalings of the channel types to be applied for plotting. If None, defaults to ``dict(eeg=1e6, grad=1e13, mag=1e15)``. - units : dict | str | None - The unit of the channel type used for colorbar label. If - scale is None the unit is automatically determined. - res : int - The resolution of the topomap image (n pixels along each side). - size : float - Side length per topomap in inches. - cbar_fmt : str - String format for colorbar values. + %(sensors_topomap)s + %(show_names_topomap)s + %(mask_patterns_topomap)s + %(mask_params_topomap)s + %(contours_topomap)s + %(outlines_topomap)s + %(sphere_topomap_auto)s + %(image_interp_topomap)s + %(extrapolate_topomap)s + + .. versionadded:: 1.3 + %(border_topomap)s + + .. versionadded:: 1.3 + %(res_topomap)s + %(size_topomap)s + %(cmap_topomap)s + %(vlim_plot_topomap_psd)s + + .. versionadded:: 1.3 + %(vmin_vmax_topomap)s + + .. deprecated:: v1.4 + The ``vmin`` and ``vmax`` parameters will be removed in version + 1.4. Please use the ``vlim`` parameter instead. + %(cnorm)s + + .. versionadded:: 1.3 + %(colorbar_topomap)s + %(cbar_fmt_topomap)s + %(units_topomap_evoked)s + %(axes_evoked_plot_topomap)s name_format : str String format for topomap values. Defaults to "CSP%%01d". + %(title_none)s + + .. deprecated:: v1.4 + The ``title`` parameter will be removed in version 1.4. Please + use :meth:`fig.suptitle()` + instead. + %(nrows_ncols_topomap)s + + .. versionadded:: 1.3 show : bool Show figure if True. - show_names : bool | callable - If True, show channel names on top of the map. If a callable is - passed, 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 significant sensors will be shown. - title : str | None - Title. If None (default), no title is displayed. - mask : ndarray of bool, shape (n_channels, n_times) | None - The channels to be marked as significant at a given time point. - Indices set to `True` will be considered. Defaults to None. - mask_params : dict | None - Additional plotting parameters for plotting significant sensors. - Default (None) equals:: - - dict(marker='o', markerfacecolor='w', markeredgecolor='k', - linewidth=0, markersize=4) - %(outlines_topomap)s - contours : int | array of float - The number of contour lines to draw. If 0, no contours will be - drawn. When an integer, matplotlib ticker locator is used to find - suitable values for the contour thresholds (may sometimes be - inaccurate, use array for accuracy). If an array, the values - represent the levels for the contours. Defaults to 6. - %(image_interp_topomap)s - average : float | None - The time window around a given time to be used for averaging - (seconds). For example, 0.01 would translate into window that - starts 5 ms before and ends 5 ms after a given time point. - Defaults to None, which means no averaging. Returns ------- @@ -453,6 +427,9 @@ def plot_filters(self, info, components=None, ch_type=None, The figure. """ from .. import EvokedArray + + vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') + if components is None: components = np.arange(self.n_components) @@ -464,14 +441,16 @@ def plot_filters(self, info, components=None, ch_type=None, filters = EvokedArray(self.filters_.T, info, tmin=0) # the call plot_topomap fig = filters.plot_topomap( - times=components, ch_type=ch_type, vlim=(vmin, vmax), - cmap=cmap, colorbar=colorbar, res=res, - cbar_fmt=cbar_fmt, sensors=sensors, scalings=scalings, units=units, - time_unit='s', time_format=name_format, size=size, - show_names=show_names, mask_params=mask_params, - mask=mask, outlines=outlines, contours=contours, - image_interp=image_interp, show=show, average=average) + times=components, average=average, 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=name_format, + nrows=nrows, ncols=ncols, show=show) if title is not None: + warn(TITLE_WARNING_MSG, FutureWarning) fig.suptitle(title) return fig From 57abb0ec9339fa986e24c09f0dbce057f5f9de61 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 14:09:20 -0600 Subject: [PATCH 04/27] touch CSP tutorial --- tutorials/machine-learning/50_decoding.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/machine-learning/50_decoding.py b/tutorials/machine-learning/50_decoding.py index 8cf7fd90953..3fb0036a21f 100644 --- a/tutorials/machine-learning/50_decoding.py +++ b/tutorials/machine-learning/50_decoding.py @@ -10,8 +10,8 @@ Design philosophy ================= -Decoding (a.k.a. MVPA) in MNE largely follows the machine -learning API of the scikit-learn package. +Decoding (a.k.a. MVPA) in MNE largely follows the machine learning API of the +scikit-learn package. Each estimator implements ``fit``, ``transform``, ``fit_transform``, and (optionally) ``inverse_transform`` methods. For more details on this design, visit scikit-learn_. For additional theoretical insights into the decoding From f316cb6969889ac288aff30fa41e82a225fcb431 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 14:42:10 -0600 Subject: [PATCH 05/27] update API of ICA.plot_components() and plot_ica_components() --- mne/preprocessing/ica.py | 37 ++++++++-------- mne/viz/topomap.py | 96 ++++++++++++++++++++++++++++------------ 2 files changed, 86 insertions(+), 47 deletions(-) diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py index ece75d41173..fc9e586557b 100644 --- a/mne/preprocessing/ica.py +++ b/mne/preprocessing/ica.py @@ -2182,24 +2182,25 @@ def copy(self): return deepcopy(self) @copy_function_doc_to_method_doc(plot_ica_components) - def plot_components(self, picks=None, ch_type=None, res=64, - vmin=None, vmax=None, cmap='RdBu_r', sensors=True, - colorbar=False, title=None, show=True, outlines='head', - contours=6, image_interp=_INTERPOLATION_DEFAULT, - inst=None, plot_std=True, - topomap_args=None, image_args=None, psd_args=None, - reject='auto', sphere=None, verbose=None): - return plot_ica_components(self, picks=picks, ch_type=ch_type, - res=res, vmin=vmin, - vmax=vmax, cmap=cmap, sensors=sensors, - colorbar=colorbar, title=title, show=show, - outlines=outlines, contours=contours, - image_interp=image_interp, - inst=inst, plot_std=plot_std, - topomap_args=topomap_args, - image_args=image_args, psd_args=psd_args, - reject=reject, sphere=sphere, - verbose=verbose) + def plot_components( + self, picks=None, ch_type=None, *, inst=None, plot_std=True, + reject='auto', sensors=True, show_names=False, 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), vmin=None, vmax=None, + cnorm=None, colorbar=False, cbar_fmt='%3.1f', units=None, + axes=None, title=None, nrows=1, ncols='auto', show=True, + topomap_args=None, image_args=None, psd_args=None, verbose=None): + return plot_ica_components( + self, picks=picks, ch_type=ch_type, inst=inst, plot_std=plot_std, + reject=reject, sensors=sensors, show_names=show_names, + contours=contours, outlines=outlines, sphere=sphere, + image_interp=image_interp, extrapolate=extrapolate, border=border, + res=res, size=size, cmap=cmap, vlim=vlim, vmin=vmin, vmax=vmax, + cnorm=cnorm, colorbar=colorbar, cbar_fmt=cbar_fmt, units=units, + axes=axes, title=title, nrows=nrows, ncols=ncols, show=show, + topomap_args=topomap_args, image_args=image_args, + psd_args=psd_args, verbose=verbose) @copy_function_doc_to_method_doc(plot_ica_properties) def plot_properties(self, inst, picks=None, axes=None, dB=True, diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 69e1b661882..10468b5e59c 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1111,15 +1111,15 @@ def _plot_ica_topomap(ica, idx=0, ch_type=None, res=64, @verbose -def plot_ica_components(ica, picks=None, ch_type=None, res=64, - vmin=None, vmax=None, cmap='RdBu_r', - sensors=True, colorbar=False, title=None, - show=True, outlines='head', contours=6, - image_interp=_INTERPOLATION_DEFAULT, - inst=None, plot_std=True, - topomap_args=None, image_args=None, - psd_args=None, reject='auto', - sphere=None, *, verbose=None): +def plot_ica_components( + ica, picks=None, ch_type=None, *, inst=None, plot_std=True, + reject='auto', sensors=True, show_names=False, 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), vmin=None, vmax=None, + cnorm=None, colorbar=False, cbar_fmt='%3.1f', units=None, axes=None, + title=None, nrows=1, ncols='auto', show=True, topomap_args=None, + image_args=None, psd_args=None, verbose=None): """Project mixing matrix on interpolated sensor topography. Parameters @@ -1128,17 +1128,6 @@ def plot_ica_components(ica, picks=None, ch_type=None, res=64, The ICA solution. %(picks_ica)s %(ch_type_topomap)s - %(res_topomap)s - %(vmin_vmax_topomap)s - %(cmap_topomap)s - %(sensors_topomap)s - %(colorbar_topomap)s - title : str | None - Title to use. - %(show)s - %(outlines_topomap)s - %(contours_topomap)s - %(image_interp_topomap)s inst : Raw | Epochs | None To be able to see component properties after clicking on component topomap you need to pass relevant data - instances of Raw or Epochs @@ -1149,22 +1138,71 @@ def plot_ica_components(ica, picks=None, ch_type=None, res=64, Defaults to True, which plots one standard deviation above/below. If set to float allows to control how many standard deviations are plotted. For example 2.5 will plot 2.5 standard deviation above/below. - topomap_args : dict | None - Dictionary of arguments to ``plot_topomap``. If None, doesn't pass any - additional arguments. Defaults to None. - image_args : dict | None - Dictionary of arguments to ``plot_epochs_image``. If None, doesn't pass - any additional arguments. Defaults to None. - psd_args : dict | None - Dictionary of arguments to :meth:`~mne.Epochs.compute_psd`. If - ``None``, doesn't pass any additional arguments. Defaults to ``None``. reject : 'auto' | dict | None Allows to specify rejection parameters used to drop epochs (or segments if continuous signal is passed as inst). If None, no rejection is applied. The default is 'auto', which applies the rejection parameters used when fitting the ICA object. + %(sensors_topomap)s + %(show_names_topomap)s + %(contours_topomap)s + %(outlines_topomap)s %(sphere_topomap_auto)s + %(image_interp_topomap)s + %(extrapolate_topomap)s + + .. versionadded:: 1.3 + %(border_topomap)s + + .. versionadded:: 1.3 + %(res_topomap)s + %(size_topomap)s + + .. versionadded:: 1.3 + %(cmap_topomap)s + %(vlim_plot_topomap_psd)s + + .. versionadded:: 1.3 + %(vmin_vmax_topomap)s + + .. deprecated:: v1.4 + The ``vmin`` and ``vmax`` parameters will be removed in version + 1.4. Please use the ``vlim`` parameter instead. + %(cnorm)s + + .. versionadded:: 1.3 + %(colorbar_topomap)s + %(cbar_fmt_topomap)s + %(units_topomap_evoked)s + %(axes_evoked_plot_topomap)s + %(title_none)s + + .. deprecated:: v1.4 + The ``title`` parameter will be removed in version 1.4. Please + use :meth:`fig.suptitle()` + instead. + %(nrows_ncols_topomap)s + + .. versionadded:: 1.3 + %(show)s + topomap_args : dict | None + Dictionary of arguments to ``plot_topomap``. If None, doesn't pass any + additional arguments. Defaults to None. + + .. deprecated:: v1.4 + The ``topomap_args`` parameter will be removed in version 1.4. All + relevant topomap parameters (e.g., ``show_names``, ``extrapolate``, + ``border``, ``size``, etc) are now directly exposed in this + function's signature. + image_args : dict | None + Dictionary of arguments to pass to :func:`~mne.viz.plot_epochs_image` + in interactive mode. Ignored if ``inst`` is not supplied. If ``None``, + nothing is passed. Defaults to ``None``. + psd_args : dict | None + Dictionary of arguments to pass to :meth:`~mne.Epochs.compute_psd` in + interactive mode. Ignored if ``inst`` is not supplied. If ``None``, + nothing is passed. Defaults to ``None``. %(verbose)s Returns From 206bf650d7658edec19d7a11431060f14ea1236c Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 14:43:08 -0600 Subject: [PATCH 06/27] lingering docdict simplification --- mne/decoding/csp.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index 2eec8c878f0..1ccfe781a6d 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -312,8 +312,7 @@ def plot_patterns( %(nrows_ncols_topomap)s .. versionadded:: 1.3 - show : bool - Show figure if True. + %(show)s Returns ------- @@ -418,8 +417,7 @@ def plot_filters( %(nrows_ncols_topomap)s .. versionadded:: 1.3 - show : bool - Show figure if True. + %(show)s Returns ------- From aa71249a0e9697a6d97a129377a920838096bc46 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 16:24:16 -0600 Subject: [PATCH 07/27] revert adding "units" to plot_components API --- mne/preprocessing/ica.py | 10 +++++----- mne/viz/topomap.py | 7 +++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py index fc9e586557b..11c9f0f46a3 100644 --- a/mne/preprocessing/ica.py +++ b/mne/preprocessing/ica.py @@ -2188,17 +2188,17 @@ def plot_components( outlines='head', sphere=None, image_interp=_INTERPOLATION_DEFAULT, extrapolate=_EXTRAPOLATE_DEFAULT, border=_BORDER_DEFAULT, res=64, size=1, cmap='RdBu_r', vlim=(None, None), vmin=None, vmax=None, - cnorm=None, colorbar=False, cbar_fmt='%3.1f', units=None, - axes=None, title=None, nrows=1, ncols='auto', show=True, - topomap_args=None, image_args=None, psd_args=None, verbose=None): + cnorm=None, colorbar=False, cbar_fmt='%3.1f', axes=None, + title=None, nrows=1, ncols='auto', show=True, topomap_args=None, + image_args=None, psd_args=None, verbose=None): return plot_ica_components( self, picks=picks, ch_type=ch_type, inst=inst, plot_std=plot_std, reject=reject, sensors=sensors, show_names=show_names, contours=contours, outlines=outlines, sphere=sphere, image_interp=image_interp, extrapolate=extrapolate, border=border, res=res, size=size, cmap=cmap, vlim=vlim, vmin=vmin, vmax=vmax, - cnorm=cnorm, colorbar=colorbar, cbar_fmt=cbar_fmt, units=units, - axes=axes, title=title, nrows=nrows, ncols=ncols, show=show, + cnorm=cnorm, colorbar=colorbar, cbar_fmt=cbar_fmt, axes=axes, + title=title, nrows=nrows, ncols=ncols, show=show, topomap_args=topomap_args, image_args=image_args, psd_args=psd_args, verbose=verbose) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 10468b5e59c..9967e843dc0 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1117,9 +1117,9 @@ def plot_ica_components( outlines='head', sphere=None, image_interp=_INTERPOLATION_DEFAULT, extrapolate=_EXTRAPOLATE_DEFAULT, border=_BORDER_DEFAULT, res=64, size=1, cmap='RdBu_r', vlim=(None, None), vmin=None, vmax=None, - cnorm=None, colorbar=False, cbar_fmt='%3.1f', units=None, axes=None, - title=None, nrows=1, ncols='auto', show=True, topomap_args=None, - image_args=None, psd_args=None, verbose=None): + cnorm=None, colorbar=False, cbar_fmt='%3.1f', axes=None, title=None, + nrows=1, ncols='auto', show=True, topomap_args=None, image_args=None, + psd_args=None, verbose=None): """Project mixing matrix on interpolated sensor topography. Parameters @@ -1174,7 +1174,6 @@ def plot_ica_components( .. versionadded:: 1.3 %(colorbar_topomap)s %(cbar_fmt_topomap)s - %(units_topomap_evoked)s %(axes_evoked_plot_topomap)s %(title_none)s From 4b926cca5248b827eda17517263befdafe096893 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Wed, 14 Dec 2022 16:30:37 -0600 Subject: [PATCH 08/27] implement vlim in plot_components --- mne/viz/topomap.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 9967e843dc0..e212a88e5c3 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -35,7 +35,7 @@ plt_show, _process_times, DraggableColorbar, _get_cmap, _validate_if_list_of_axes, _setup_cmap, _check_time_unit, _set_3d_axes_equal, _check_type_projs, _format_units_psd, - _prepare_sensor_names) + _prepare_sensor_names, _warn_deprecated_vmin_vmax) from ..defaults import _handle_default from ..transforms import apply_trans, invert_transform from ..io.meas_info import Info, _simplify_info @@ -1229,6 +1229,8 @@ def plot_ica_components( raise RuntimeError('The ICA\'s measurement info is missing. Please ' 'fit the ICA or add the corresponding info object.') + vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') + topomap_args = dict() if topomap_args is None else topomap_args topomap_args = copy.copy(topomap_args) if 'sphere' not in topomap_args: @@ -1241,7 +1243,7 @@ def plot_ica_components( for k in range(0, n_components, p): picks = range(k, min(k + p, n_components)) fig = plot_ica_components( - ica, picks=picks, ch_type=ch_type, res=res, vmax=vmax, + ica, picks=picks, ch_type=ch_type, res=res, vlim=vlim, cmap=cmap, sensors=sensors, colorbar=colorbar, title=title, show=show, outlines=outlines, contours=contours, image_interp=image_interp, inst=inst, plot_std=plot_std, @@ -1279,7 +1281,7 @@ def plot_ica_components( titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: data_, names_ = _merge_ch_data(data_, ch_type, names.copy()) - vlim = _setup_vmin_vmax(data_, vmin, vmax) + vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( data_.flatten(), pos, vlim=vlim, res=res, axes=ax, cmap=cmap[0], outlines=outlines, contours=contours, From 5fe6bd4a41c97673ed77d89fac11e4bcba827b5b Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 12:13:52 -0600 Subject: [PATCH 09/27] update ICA.plot_properties(); fix associated tests --- mne/preprocessing/ica.py | 8 +-- mne/utils/docs.py | 13 ++++- mne/viz/tests/test_ica.py | 2 +- mne/viz/topomap.py | 107 ++++++++++++++++++++++++-------------- 4 files changed, 84 insertions(+), 46 deletions(-) diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py index 11c9f0f46a3..69b4fbedcc9 100644 --- a/mne/preprocessing/ica.py +++ b/mne/preprocessing/ica.py @@ -2188,9 +2188,9 @@ def plot_components( outlines='head', sphere=None, image_interp=_INTERPOLATION_DEFAULT, extrapolate=_EXTRAPOLATE_DEFAULT, border=_BORDER_DEFAULT, res=64, size=1, cmap='RdBu_r', vlim=(None, None), vmin=None, vmax=None, - cnorm=None, colorbar=False, cbar_fmt='%3.1f', axes=None, - title=None, nrows=1, ncols='auto', show=True, topomap_args=None, - image_args=None, psd_args=None, verbose=None): + cnorm=None, colorbar=False, cbar_fmt='%3.2f', axes=None, + title=None, nrows='auto', ncols='auto', show=True, + topomap_args=None, image_args=None, psd_args=None, verbose=None): return plot_ica_components( self, picks=picks, ch_type=ch_type, inst=inst, plot_std=plot_std, reject=reject, sensors=sensors, show_names=show_names, @@ -2894,7 +2894,7 @@ def corrmap(icas, template, threshold="auto", label=None, ch_type="eeg", *, template_fig = icas[template[0]].plot_components( picks=template[1], ch_type=ch_type, title=ttl, outlines=outlines, cmap=cmap, contours=contours, - show=show, topomap_args=dict(sphere=sphere)) + show=show, sphere=sphere) else: # plotting an array template_fig = _plot_corrmap( [template], [0], [0], ch_type, icas[0].copy(), "Template", diff --git a/mne/utils/docs.py b/mne/utils/docs.py index bc7cba9fdbc..daa155537f6 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -2255,11 +2255,22 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): a power-of-two size (can be much faster). """ +docdict['nrows_ncols_ica_components'] = """ +nrows, ncols : int | 'auto' + The number of rows and columns of topographies to plot. If both ``nrows`` + and ``ncols`` are ``'auto'``, will plot up to 20 components in a 5×4 grid, + and return multiple figures if more than 20 components are requested. + If only one of ``nrows`` or ``ncols`` is ``'auto'``, the other is inferred + and a single figure is generated. If scalars are provided for both + arguments, will plot up to ``nrows*ncols`` components in a grid and return + multiple figures as needed. Defaults to ``nrows='auto', ncols='auto'``. +""" + docdict['nrows_ncols_topomap'] = """ nrows, ncols : int | 'auto' The number of rows and columns of topographies to plot. If either ``nrows`` or ``ncols`` is ``'auto'``, the necessary number will be inferred. Defaults - to ``nrows=1, ncols='auto'``.\ + to ``nrows=1, ncols='auto'``. """ # %% diff --git a/mne/viz/tests/test_ica.py b/mne/viz/tests/test_ica.py index 48be8af97f6..be5a4379e12 100644 --- a/mne/viz/tests/test_ica.py +++ b/mne/viz/tests/test_ica.py @@ -147,7 +147,7 @@ def test_plot_ica_properties(): assert 'extrapolation mode local to mean' in log, log ica.plot_properties(epochs, picks=1, dB=False, plot_std=1.5, **topoargs) fig = ica.plot_properties(epochs, picks=1, image_args={'sigma': 1.5}, - topomap_args={'res': 4, 'colorbar': True}, + res=4, colorbar=True, psd_args={'fmax': 65.}, plot_std=False, log_scale=True, figsize=[4.5, 4.5], reject=reject)[0] diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index e212a88e5c3..ec1cafb6002 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1117,9 +1117,9 @@ def plot_ica_components( outlines='head', sphere=None, image_interp=_INTERPOLATION_DEFAULT, extrapolate=_EXTRAPOLATE_DEFAULT, border=_BORDER_DEFAULT, res=64, size=1, cmap='RdBu_r', vlim=(None, None), vmin=None, vmax=None, - cnorm=None, colorbar=False, cbar_fmt='%3.1f', axes=None, title=None, - nrows=1, ncols='auto', show=True, topomap_args=None, image_args=None, - psd_args=None, verbose=None): + cnorm=None, colorbar=False, cbar_fmt='%3.2f', axes=None, title=None, + nrows='auto', ncols='auto', show=True, topomap_args=None, + image_args=None, psd_args=None, verbose=None): """Project mixing matrix on interpolated sensor topography. Parameters @@ -1175,13 +1175,15 @@ def plot_ica_components( %(colorbar_topomap)s %(cbar_fmt_topomap)s %(axes_evoked_plot_topomap)s - %(title_none)s + title : str | None + The title of the generated figure. If ``None`` (default) and + ``axes=None``, a default title of "ICA Components" will be used. .. deprecated:: v1.4 The ``title`` parameter will be removed in version 1.4. Please use :meth:`fig.suptitle()` - instead. - %(nrows_ncols_topomap)s + instead to set the title after the figure is created. + %(nrows_ncols_ica_components)s .. versionadded:: 1.3 %(show)s @@ -1228,79 +1230,104 @@ def plot_ica_components( if ica.info is None: raise RuntimeError('The ICA\'s measurement info is missing. Please ' 'fit the ICA or add the corresponding info object.') - + # TODO ↓↓↓↓↓ remove after 1.3 release (begin) vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') - topomap_args = dict() if topomap_args is None else topomap_args - topomap_args = copy.copy(topomap_args) - if 'sphere' not in topomap_args: - topomap_args['sphere'] = sphere - if picks is None: # plot components by sets of 20 - ch_type = _get_ch_type(ica, ch_type) - n_components = ica.mixing_matrix_.shape[1] - p = 20 + if topomap_args: # not None, not empty dict + warn('The "topomap_args" parameter is deprecated and will be ' + 'removed in version 1.4. All relevant topomap parameters are now ' + 'directly exposed in this function\'s signature.', FutureWarning) + topomap_args = copy.copy(topomap_args) + else: + topomap_args = dict() + # TODO ↑↑↑↑↑ remove after 1.3 release (end) + + n_components = ica.mixing_matrix_.shape[1] + + # for backward compat, nrow='auto' ncol='auto' should yield 4 rows 5 cols + # and create multiple figures if more than 20 components requested + if nrows == 'auto' and ncols == 'auto': + ncols = 5 + max_subplots_per_fig = 20 + elif nrows == 'auto' or ncols == 'auto': + # user provided incomplete row/col spec; put all in one figure + max_subplots_per_fig = n_components + else: + max_subplots_per_fig = nrows * ncols + + # handle ch_type=None + ch_type = _get_ch_type(ica, ch_type) + + if picks is None: figs = [] - for k in range(0, n_components, p): - picks = range(k, min(k + p, n_components)) + for k in range(0, n_components, max_subplots_per_fig): + picks = range(k, min(k + max_subplots_per_fig, n_components)) fig = plot_ica_components( - ica, picks=picks, ch_type=ch_type, res=res, vlim=vlim, - cmap=cmap, sensors=sensors, colorbar=colorbar, title=title, - show=show, outlines=outlines, contours=contours, - image_interp=image_interp, inst=inst, plot_std=plot_std, - topomap_args=topomap_args, image_args=image_args, - psd_args=psd_args, reject=reject, sphere=sphere) + ica, picks=picks, ch_type=ch_type, inst=inst, + plot_std=plot_std, reject=reject, sensors=sensors, + show_names=show_names, 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, axes=axes, title=title, nrows=nrows, + ncols=ncols, show=show, topomap_args=topomap_args, + image_args=image_args, psd_args=psd_args, verbose=verbose) figs.append(fig) return figs else: picks = _picks_to_idx(ica.n_components_, picks, picks_on="components") - ch_type = _get_ch_type(ica, ch_type) - - cmap = _setup_cmap(cmap, n_axes=len(picks)) - data = np.dot(ica.mixing_matrix_[:, picks].T, - ica.pca_components_[:ica.n_components_]) data_picks, pos, merge_channels, names, ch_type, sphere, clip_origin = \ _prepare_topomap_plot(ica, ch_type, sphere=sphere) + + cmap = _setup_cmap(cmap, n_axes=len(picks)) + names = _prepare_sensor_names(names, show_names) outlines = _make_head_outlines(sphere, pos, outlines, clip_origin) + data = np.dot(ica.mixing_matrix_[:, picks].T, + ica.pca_components_[:ica.n_components_]) data = np.atleast_2d(data) data = data[:, data_picks] - # prepare data for iteration - fig, axes, _, _ = _prepare_trellis(len(data), ncols=5) if title is None: title = 'ICA components' - fig.suptitle(title) + user_passed_axes = axes is not None + if not user_passed_axes: + fig, axes, _, _ = _prepare_trellis(len(data), ncols=ncols, nrows=nrows) + fig.suptitle(title) - titles = list() + subplot_titles = list() for ii, data_, ax in zip(picks, data, axes): kwargs = dict(color='gray') if ii in ica.exclude else dict() comp_title = ica._ica_names[ii] if len(set(ica.get_channel_types())) > 1: comp_title += f' ({ch_type})' - titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) + subplot_titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: data_, names_ = _merge_ch_data(data_, ch_type, names.copy()) vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( - data_.flatten(), pos, vlim=vlim, res=res, axes=ax, - cmap=cmap[0], outlines=outlines, contours=contours, - image_interp=image_interp, show=False, sensors=sensors, - ch_type=ch_type, **topomap_args)[0] + data_.flatten(), pos, ch_type=ch_type, sensors=sensors, + names=names, contours=contours, outlines=outlines, sphere=sphere, + image_interp=image_interp, extrapolate=extrapolate, border=border, + res=res, size=size, cmap=cmap[0], vlim=vlim, cnorm=cnorm, + axes=ax, show=False, **topomap_args)[0] + im.axes.set_label(ica._ica_names[ii]) if colorbar: cbar, cax = _add_colorbar(ax, im, cmap, title="AU", - side="right", pad=.05, format='%3.2f') + side="right", pad=.05, format=cbar_fmt) cbar.ax.tick_params(labelsize=12) cbar.set_ticks(vlim) _hide_frame(ax) del pos tight_layout(fig=fig) - fig.subplots_adjust(top=0.88, bottom=0.) + if not user_passed_axes: + fig.subplots_adjust(top=0.88, bottom=0.) fig.canvas.draw() # add title selection interactivity - def onclick_title(event, ica=ica, titles=titles): + def onclick_title(event, ica=ica, titles=subplot_titles): # check if any title was pressed title_pressed = None for title in titles: From 709d60dadaf35a2b98a89b2c2d3e811442a232db Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 12:16:19 -0600 Subject: [PATCH 10/27] comments --- mne/viz/topomap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index ec1cafb6002..1c19f934882 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1289,11 +1289,14 @@ def plot_ica_components( data = np.atleast_2d(data) data = data[:, data_picks] + # TODO ↓↓↓↓↓ remove after 1.3 release (begin) if title is None: title = 'ICA components' + # TODO ↑↑↑↑↑ remove after 1.3 release (end) user_passed_axes = axes is not None if not user_passed_axes: fig, axes, _, _ = _prepare_trellis(len(data), ncols=ncols, nrows=nrows) + # TODO ↓↓↓↓↓ remove after 1.3 release fig.suptitle(title) subplot_titles = list() @@ -1322,8 +1325,10 @@ def plot_ica_components( _hide_frame(ax) del pos tight_layout(fig=fig) + # TODO ↓↓↓↓↓ remove after 1.3 release (begin) if not user_passed_axes: fig.subplots_adjust(top=0.88, bottom=0.) + # TODO ↑↑↑↑↑ remove after 1.3 release (end) fig.canvas.draw() # add title selection interactivity From ca07d0e3696e802aa65d7d212cab3ea522ad861d Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 12:22:51 -0600 Subject: [PATCH 11/27] touch ICA tutorial --- tutorials/preprocessing/40_artifact_correction_ica.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/preprocessing/40_artifact_correction_ica.py b/tutorials/preprocessing/40_artifact_correction_ica.py index 8a95b0ab471..d6511baba9c 100644 --- a/tutorials/preprocessing/40_artifact_correction_ica.py +++ b/tutorials/preprocessing/40_artifact_correction_ica.py @@ -23,8 +23,8 @@ import os import mne -from mne.preprocessing import (ICA, create_eog_epochs, create_ecg_epochs, - corrmap) +from mne.preprocessing import (ICA, corrmap, create_ecg_epochs, + create_eog_epochs) sample_data_folder = mne.datasets.sample.data_path() sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample', From f26f36a1c39ded467815609f26b2eee082e3ef0d Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 12:33:16 -0600 Subject: [PATCH 12/27] reuse warning msg --- mne/decoding/csp.py | 10 +++------- mne/viz/topomap.py | 5 ++++- mne/viz/utils.py | 5 +++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index 1ccfe781a6d..fed6dca0d72 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -18,11 +18,7 @@ _INTERPOLATION_DEFAULT) from ..fixes import pinv from ..utils import fill_doc, _check_option, _validate_type, copy_doc, warn -from ..viz.utils import _warn_deprecated_vmin_vmax - -TITLE_WARNING_MSG = ( - 'The "title" parameter is deprecated and will be removed in version 1.4. ' - 'Use "fig.suptitle()" instead.') +from ..viz.utils import _warn_deprecated_vmin_vmax, _TITLE_WARNING_MSG @fill_doc @@ -343,7 +339,7 @@ def plot_patterns( nrows=nrows, ncols=ncols, show=show) if title is not None: - warn(TITLE_WARNING_MSG, FutureWarning) + warn(_TITLE_WARNING_MSG, FutureWarning) fig.suptitle(title) return fig @@ -448,7 +444,7 @@ def plot_filters( cbar_fmt=cbar_fmt, units=units, axes=axes, time_format=name_format, nrows=nrows, ncols=ncols, show=show) if title is not None: - warn(TITLE_WARNING_MSG, FutureWarning) + warn(_TITLE_WARNING_MSG, FutureWarning) fig.suptitle(title) return fig diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 1c19f934882..b6e81a8361c 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -35,7 +35,8 @@ plt_show, _process_times, DraggableColorbar, _get_cmap, _validate_if_list_of_axes, _setup_cmap, _check_time_unit, _set_3d_axes_equal, _check_type_projs, _format_units_psd, - _prepare_sensor_names, _warn_deprecated_vmin_vmax) + _prepare_sensor_names, _warn_deprecated_vmin_vmax, + _TITLE_WARNING_MSG) from ..defaults import _handle_default from ..transforms import apply_trans, invert_transform from ..io.meas_info import Info, _simplify_info @@ -1292,6 +1293,8 @@ def plot_ica_components( # TODO ↓↓↓↓↓ remove after 1.3 release (begin) if title is None: title = 'ICA components' + else: + warn(_TITLE_WARNING_MSG, FutureWarning) # TODO ↑↑↑↑↑ remove after 1.3 release (end) user_passed_axes = axes is not None if not user_passed_axes: diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 8ae413ce681..8f8f9dccff3 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -51,6 +51,11 @@ 'ecog': "ECoG channel", 'misc': "miscellaneous sensor"} +# TODO ↓↓↓↓↓ remove after 1.3 release +_TITLE_WARNING_MSG = ( + 'The "title" parameter is deprecated and will be removed in version 1.4. ' + 'Use "fig.suptitle()" instead.') + @decorator def safe_event(fun, *args, **kwargs): From 64223db6270e28ce7f7c3057d5e05d7a426133fb Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 13:00:35 -0600 Subject: [PATCH 13/27] revert deprecating title for ICA components plots --- mne/decoding/csp.py | 7 ++++++- mne/viz/topomap.py | 13 +------------ mne/viz/utils.py | 5 ----- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index fed6dca0d72..d96e0811f7b 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -18,7 +18,12 @@ _INTERPOLATION_DEFAULT) from ..fixes import pinv from ..utils import fill_doc, _check_option, _validate_type, copy_doc, warn -from ..viz.utils import _warn_deprecated_vmin_vmax, _TITLE_WARNING_MSG +from ..viz.utils import _warn_deprecated_vmin_vmax + +# TODO ↓↓↓↓↓ remove after 1.3 release +_TITLE_WARNING_MSG = ( + 'The "title" parameter is deprecated and will be removed in version 1.4. ' + 'Use "fig.suptitle()" instead.') @fill_doc diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index b6e81a8361c..38d379923b6 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -35,8 +35,7 @@ plt_show, _process_times, DraggableColorbar, _get_cmap, _validate_if_list_of_axes, _setup_cmap, _check_time_unit, _set_3d_axes_equal, _check_type_projs, _format_units_psd, - _prepare_sensor_names, _warn_deprecated_vmin_vmax, - _TITLE_WARNING_MSG) + _prepare_sensor_names, _warn_deprecated_vmin_vmax) from ..defaults import _handle_default from ..transforms import apply_trans, invert_transform from ..io.meas_info import Info, _simplify_info @@ -1179,11 +1178,6 @@ def plot_ica_components( title : str | None The title of the generated figure. If ``None`` (default) and ``axes=None``, a default title of "ICA Components" will be used. - - .. deprecated:: v1.4 - The ``title`` parameter will be removed in version 1.4. Please - use :meth:`fig.suptitle()` - instead to set the title after the figure is created. %(nrows_ncols_ica_components)s .. versionadded:: 1.3 @@ -1290,16 +1284,11 @@ def plot_ica_components( data = np.atleast_2d(data) data = data[:, data_picks] - # TODO ↓↓↓↓↓ remove after 1.3 release (begin) if title is None: title = 'ICA components' - else: - warn(_TITLE_WARNING_MSG, FutureWarning) - # TODO ↑↑↑↑↑ remove after 1.3 release (end) user_passed_axes = axes is not None if not user_passed_axes: fig, axes, _, _ = _prepare_trellis(len(data), ncols=ncols, nrows=nrows) - # TODO ↓↓↓↓↓ remove after 1.3 release fig.suptitle(title) subplot_titles = list() diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 8f8f9dccff3..8ae413ce681 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -51,11 +51,6 @@ 'ecog': "ECoG channel", 'misc': "miscellaneous sensor"} -# TODO ↓↓↓↓↓ remove after 1.3 release -_TITLE_WARNING_MSG = ( - 'The "title" parameter is deprecated and will be removed in version 1.4. ' - 'Use "fig.suptitle()" instead.') - @decorator def safe_event(fun, *args, **kwargs): From 27d678310dda94eb18282b4adea0cb0225c8573e Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 13:12:56 -0600 Subject: [PATCH 14/27] changelog --- doc/changes/latest.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index 89843fb5ab6..07987b54e59 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -66,3 +66,7 @@ Bugs API changes ~~~~~~~~~~~ - In :func:`mne.time_frequency.dpss_windows`, interpolating is deprecated (nowadays SciPy's computations are fast enough for large ``N`` without interpolation). This affects parameters ``interp_from`` and ``interp_kind``. Two new parameters of the underlying SciPy :func:`~scipy.signal.windows.dpss` function are also exposed: ``sym`` (for choosing symmetric vs. periodic windows) and ``norm`` (window normalization method). (:gh:`11293` by `Daniel McCloy`_) +- In :meth:`mne.decoding.CSP.plot_patterns`, :meth:`mne.decoding.CSP.plot_filters`, :meth:`mne.preprocessing.ICA.plot_components`, and :func:`mne.viz.plot_ica_components`, the parameters ``vmin`` and ``vmax`` are deprecated in favor of ``vlim``, for consistency with other topomap-plotting functions and methods (:gh:`11371` by `Daniel McCloy`_) +- In :meth:`mne.decoding.CSP.plot_patterns` and :meth:`mne.decoding.CSP.plot_filters` the ``title`` parameter is deprecated and will be removed in version 1.4 (:gh:`11371` by `Daniel McCloy`_) +- The APIs of :meth:`mne.preprocessing.ICA.plot_components` and :func:`mne.viz.plot_ica_components` gained new parameters ``show_names``, ``extrapolate``, ``border``, ``size``, ``cnorm``, ``cbar_fmt``, ``axes``, ``nrows``, ``ncols``, for consistency with other topomap-plotting functions and methods (:gh:`11371` by `Daniel McCloy`_) +- The APIs of :meth:`mne.decoding.CSP.plot_patterns` and :meth:`mne.decoding.CSP.plot_filters` gained new parameters ``extrapolate``, ``border``, ``cnorm``, ``axes``, ``nrows``, ``ncols``, for consistency with other topomap-plotting functions and methods (:gh:`11371` by `Daniel McCloy`_) From a74476ca36a0ccd8ddbb0e0f397df503c01e0784 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 13:49:02 -0600 Subject: [PATCH 15/27] test fixes --- mne/viz/tests/test_ica.py | 2 +- mne/viz/topomap.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/viz/tests/test_ica.py b/mne/viz/tests/test_ica.py index be5a4379e12..0a602e290e9 100644 --- a/mne/viz/tests/test_ica.py +++ b/mne/viz/tests/test_ica.py @@ -147,7 +147,7 @@ def test_plot_ica_properties(): assert 'extrapolation mode local to mean' in log, log ica.plot_properties(epochs, picks=1, dB=False, plot_std=1.5, **topoargs) fig = ica.plot_properties(epochs, picks=1, image_args={'sigma': 1.5}, - res=4, colorbar=True, + topomap_args=dict(res=4, colorbar=True), psd_args={'fmax': 65.}, plot_std=False, log_scale=True, figsize=[4.5, 4.5], reject=reject)[0] diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 38d379923b6..adbce4e3b77 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1299,7 +1299,7 @@ def plot_ica_components( comp_title += f' ({ch_type})' subplot_titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: - data_, names_ = _merge_ch_data(data_, ch_type, names.copy()) + data_, names_ = _merge_ch_data(data_, ch_type, copy.copy(names)) vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( data_.flatten(), pos, ch_type=ch_type, sensors=sensors, From 61cd61e7785ec862e0452c69bcfa0a5ce8686ff4 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 14:07:38 -0600 Subject: [PATCH 16/27] docstring tweaks --- mne/decoding/csp.py | 2 +- mne/utils/docs.py | 8 ++++---- mne/viz/topomap.py | 8 ++------ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index d96e0811f7b..60eee9ab9bf 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -287,7 +287,7 @@ def plot_patterns( %(res_topomap)s %(size_topomap)s %(cmap_topomap)s - %(vlim_plot_topomap_psd)s + %(vlim_plot_topomap)s .. versionadded:: 1.3 %(vmin_vmax_topomap)s diff --git a/mne/utils/docs.py b/mne/utils/docs.py index daa155537f6..407dd5bf5a8 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -2260,10 +2260,10 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): The number of rows and columns of topographies to plot. If both ``nrows`` and ``ncols`` are ``'auto'``, will plot up to 20 components in a 5×4 grid, and return multiple figures if more than 20 components are requested. - If only one of ``nrows`` or ``ncols`` is ``'auto'``, the other is inferred - and a single figure is generated. If scalars are provided for both - arguments, will plot up to ``nrows*ncols`` components in a grid and return - multiple figures as needed. Defaults to ``nrows='auto', ncols='auto'``. + If one is ``'auto'`` and the other a scalar, a single figure is generated. + If scalars are provided for both arguments, will plot up to ``nrows*ncols`` + components in a grid and return multiple figures as needed. Default is + ``nrows='auto', ncols='auto'``. """ docdict['nrows_ncols_topomap'] = """ diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index adbce4e3b77..30b56831210 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1161,7 +1161,7 @@ def plot_ica_components( .. versionadded:: 1.3 %(cmap_topomap)s - %(vlim_plot_topomap_psd)s + %(vlim_plot_topomap)s .. versionadded:: 1.3 %(vmin_vmax_topomap)s @@ -1204,11 +1204,7 @@ def plot_ica_components( Returns ------- fig : instance of matplotlib.figure.Figure | list of matplotlib.figure.Figure - - The figure object(s). Components are plotted on a grid with maximum - dimensions of 5⨉4. If more than 20 components are plotted, a new figure - will be created for each batch of 20, and a list of those figures - will be returned. + The figure object(s). Notes ----- From ce51fa6b473fb931227cda25effde1d0dfbaf7d1 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 15:21:58 -0600 Subject: [PATCH 17/27] add test for revealed bug --- mne/viz/tests/test_topomap.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py index 4d45cfaea18..8f1e47b1e11 100644 --- a/mne/viz/tests/test_topomap.py +++ b/mne/viz/tests/test_topomap.py @@ -229,6 +229,21 @@ def test_plot_evoked_topomap_errors(evoked, monkeypatch): evoked.plot_topomap() +@pytest.mark.parametrize('units, scalings, expected_unit', [ + (None, None, 'µV'), + ('foo', None, 'foo'), + (None, 7., 'AU'), # non-default scaling → "AU" +]) +def test_plot_evoked_topomap_units(evoked, units, scalings, expected_unit): + evoked.pick(['EEG 001', 'EEG 002', 'EEG 003']) + fig = evoked.plot_topomap(times=0.1, res=8, contours=0, sensors=False, + units=units, scalings=scalings) + cbar = [ax for ax in fig.axes if hasattr(ax, '_colorbar')] + assert len(cbar) == 1 + cbar = cbar[0] + assert cbar.get_title() == expected_unit + + @pytest.mark.parametrize('extrapolate', ('box', 'local', 'head')) def test_plot_evoked_topomap_extrapolation(evoked, extrapolate): """Test topomap extrapolation options.""" From 1332e6df5df02963412ab7f1afd870ab64514c8c Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 15:23:12 -0600 Subject: [PATCH 18/27] fix revealed bug --- mne/viz/topomap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 30b56831210..c03bde9b6aa 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1620,7 +1620,10 @@ def plot_evoked_topomap( "times='interactive'.") # units, scalings key = 'grad' if ch_type.startswith('planar') else ch_type + default_scaling = _handle_default('scalings', None)[key] scaling = _handle_default('scalings', scalings)[key] + # if non-default scaling, fall back to "AU" if unit wasn't given by user + key = 'misc' if scaling != default_scaling else key unit = _handle_default('units', units)[key] # ch_names (required for NIRS) ch_names = names From 42a3309065883c88edebbd7c666446e00d8eb608 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 15:24:09 -0600 Subject: [PATCH 19/27] better units handling for CSP --- mne/decoding/csp.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py index 60eee9ab9bf..7d5c56898bf 100644 --- a/mne/decoding/csp.py +++ b/mne/decoding/csp.py @@ -300,7 +300,7 @@ def plot_patterns( .. versionadded:: 1.3 %(colorbar_topomap)s %(cbar_fmt_topomap)s - %(units_topomap_evoked)s + %(units_topomap)s %(axes_evoked_plot_topomap)s name_format : str String format for topomap values. Defaults to "CSP%%01d". @@ -321,11 +321,14 @@ def plot_patterns( The figure. """ from .. import EvokedArray - if components is None: - components = np.arange(self.n_components) vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') + if units is None: + units = 'AU' + if components is None: + components = np.arange(self.n_components) + # set sampling frequency to have 1 component per time point info = cp.deepcopy(info) with info._unlock(): @@ -405,7 +408,7 @@ def plot_filters( .. versionadded:: 1.3 %(colorbar_topomap)s %(cbar_fmt_topomap)s - %(units_topomap_evoked)s + %(units_topomap)s %(axes_evoked_plot_topomap)s name_format : str String format for topomap values. Defaults to "CSP%%01d". @@ -429,6 +432,8 @@ def plot_filters( vlim = _warn_deprecated_vmin_vmax(vlim, vmin, vmax, '1.4') + if units is None: + units = 'AU' if components is None: components = np.arange(self.n_components) From 6ce2ac33e97483ca25c7a5f3024937c609bda0de Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 16:32:46 -0600 Subject: [PATCH 20/27] fix (?) plotting error --- mne/viz/topomap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index c03bde9b6aa..4f8004a1cfc 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1296,7 +1296,6 @@ def plot_ica_components( subplot_titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: data_, names_ = _merge_ch_data(data_, ch_type, copy.copy(names)) - vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( data_.flatten(), pos, ch_type=ch_type, sensors=sensors, names=names, contours=contours, outlines=outlines, sphere=sphere, From c4d1148129d9f88101626c601795f6b1aa6dea90 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 16:58:20 -0600 Subject: [PATCH 21/27] missing docstring in the new test --- mne/viz/tests/test_topomap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py index 8f1e47b1e11..9a350cb778f 100644 --- a/mne/viz/tests/test_topomap.py +++ b/mne/viz/tests/test_topomap.py @@ -235,6 +235,7 @@ def test_plot_evoked_topomap_errors(evoked, monkeypatch): (None, 7., 'AU'), # non-default scaling → "AU" ]) def test_plot_evoked_topomap_units(evoked, units, scalings, expected_unit): + """Test that colorbar units respect scalings correctly.""" evoked.pick(['EEG 001', 'EEG 002', 'EEG 003']) fig = evoked.plot_topomap(times=0.1, res=8, contours=0, sensors=False, units=units, scalings=scalings) From d561a6a1079c4761b97077001097e1c2f3e099ea Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Thu, 15 Dec 2022 17:27:03 -0600 Subject: [PATCH 22/27] better fix that doesn't break other things --- mne/viz/topomap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 4f8004a1cfc..d1b6231cb23 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1296,11 +1296,12 @@ def plot_ica_components( subplot_titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: data_, names_ = _merge_ch_data(data_, ch_type, copy.copy(names)) + _vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( data_.flatten(), pos, ch_type=ch_type, sensors=sensors, names=names, contours=contours, outlines=outlines, sphere=sphere, image_interp=image_interp, extrapolate=extrapolate, border=border, - res=res, size=size, cmap=cmap[0], vlim=vlim, cnorm=cnorm, + res=res, size=size, cmap=cmap[0], vlim=_vlim, cnorm=cnorm, axes=ax, show=False, **topomap_args)[0] im.axes.set_label(ica._ica_names[ii]) From e8b810cff99a39b9dc63ff2c2075dbbb5afedb10 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Fri, 16 Dec 2022 08:54:08 -0600 Subject: [PATCH 23/27] fix test --- mne/viz/topomap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index d1b6231cb23..520feb770be 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1309,7 +1309,7 @@ def plot_ica_components( cbar, cax = _add_colorbar(ax, im, cmap, title="AU", side="right", pad=.05, format=cbar_fmt) cbar.ax.tick_params(labelsize=12) - cbar.set_ticks(vlim) + cbar.set_ticks(_vlim) _hide_frame(ax) del pos tight_layout(fig=fig) From abc4236073d7beb3c50ca48fc56edc05de7a9bd7 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Fri, 16 Dec 2022 15:13:58 -0600 Subject: [PATCH 24/27] docstring tweak --- mne/utils/docs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mne/utils/docs.py b/mne/utils/docs.py index 407dd5bf5a8..93205ccd1b0 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -3801,9 +3801,9 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): _units = """ units : {}str | None - The units of the channel type; used for the colorbar label. Ignored if - ``colorbar=False``. If ``None`` {}the label will be "AU" indicating - arbitrary units. Default is ``None``. + The units to use for the colorbar label. Ignored if ``colorbar=False``. + If ``None`` {}the label will be "AU" indicating arbitrary units. + Default is ``None``. """ docdict['units_topomap'] = _units.format('', '') docdict['units_topomap_evoked'] = _units.format( From 281a53f964b1d8870382d81a97904d3c9d14e7df Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Fri, 16 Dec 2022 15:15:01 -0600 Subject: [PATCH 25/27] add code comment --- mne/viz/topomap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 520feb770be..7ed3589a61c 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1296,6 +1296,9 @@ def plot_ica_components( subplot_titles.append(ax.set_title(comp_title, fontsize=12, **kwargs)) if merge_channels: data_, names_ = _merge_ch_data(data_, ch_type, copy.copy(names)) + # ↓↓↓ NOTE: we intentionally use the default norm=False here, so that + # ↓↓↓ we get vlims that are symmetric-about-zero, even if the data for + # ↓↓↓ a given component happens to be one-sided. _vlim = _setup_vmin_vmax(data_, *vlim) im = plot_topomap( data_.flatten(), pos, ch_type=ch_type, sensors=sensors, From f06f43943d0320c6c02c80ac485de6408af065fc Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Fri, 16 Dec 2022 15:15:35 -0600 Subject: [PATCH 26/27] unrelated minor simplification/refactor --- mne/viz/topomap.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 7ed3589a61c..ceb7cd65d08 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -1239,22 +1239,23 @@ def plot_ica_components( # and create multiple figures if more than 20 components requested if nrows == 'auto' and ncols == 'auto': ncols = 5 - max_subplots_per_fig = 20 + max_subplots = 20 elif nrows == 'auto' or ncols == 'auto': # user provided incomplete row/col spec; put all in one figure - max_subplots_per_fig = n_components + max_subplots = n_components else: - max_subplots_per_fig = nrows * ncols + max_subplots = nrows * ncols # handle ch_type=None ch_type = _get_ch_type(ica, ch_type) if picks is None: figs = [] - for k in range(0, n_components, max_subplots_per_fig): - picks = range(k, min(k + max_subplots_per_fig, n_components)) + cut_points = range(max_subplots, n_components, max_subplots) + pick_groups = np.split(range(n_components), cut_points) + for _picks in pick_groups: fig = plot_ica_components( - ica, picks=picks, ch_type=ch_type, inst=inst, + ica, picks=_picks, ch_type=ch_type, inst=inst, plot_std=plot_std, reject=reject, sensors=sensors, show_names=show_names, contours=contours, outlines=outlines, sphere=sphere, image_interp=image_interp, From fb8642948171cfa5c2a0acf93fb5d491bf26f133 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Fri, 16 Dec 2022 17:26:14 -0600 Subject: [PATCH 27/27] fix test --- mne/viz/tests/test_topomap.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py index 9a350cb778f..62536ca2b48 100644 --- a/mne/viz/tests/test_topomap.py +++ b/mne/viz/tests/test_topomap.py @@ -38,7 +38,6 @@ _fake_scroll) from mne.utils import requires_sklearn, check_version - data_dir = testing.data_path(download=False) subjects_dir = op.join(data_dir, 'subjects') ecg_fname = op.join(data_dir, 'MEG', 'sample', 'sample_audvis_ecg-proj.fif') @@ -239,10 +238,18 @@ def test_plot_evoked_topomap_units(evoked, units, scalings, expected_unit): evoked.pick(['EEG 001', 'EEG 002', 'EEG 003']) fig = evoked.plot_topomap(times=0.1, res=8, contours=0, sensors=False, units=units, scalings=scalings) - cbar = [ax for ax in fig.axes if hasattr(ax, '_colorbar')] - assert len(cbar) == 1 - cbar = cbar[0] - assert cbar.get_title() == expected_unit + # ideally we'd do this: + # cbar = [ax for ax in fig.axes if hasattr(ax, '_colorbar')] + # assert len(cbar) == 1 + # cbar = cbar[0] + # assert cbar.get_title() == expected_unit + # ...but not all matplotlib versions support it, and we can't use + # @requires_version because it's hard figure out exactly which MPL version + # is the cutoff since it relies on a private attribute. So for now we just + # do this: + for ax in fig.axes: + if hasattr(ax, '_colorbar'): + assert ax.get_title() == expected_unit @pytest.mark.parametrize('extrapolate', ('box', 'local', 'head'))