Repository: mne-tools/mne-python

Issue:
plot_topomap()'s cnorm param should accept a CenteredNorm
Plotting T statistics on topographic maps with @SophieHerbst, I discovered that `mne.viz.plot_topomap()` only accepts `TwoSlopeNorm` as `cnorm` paramter.

However, I believe for our purpose, [`TwoSlopeNorm`](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.TwoSlopeNorm.html) is not the best solution:

> Normalize data with a set center.
> Useful when mapping data with an unequal rates of change around a conceptual center, e.g., data that range from -2 to 4, with 0 as the midpoint.

Instead, I believe [`CenteredNorm`](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.CenteredNorm.html) makes much more sense:

> Normalize symmetrical data around a center (0 by default).
> Unlike [TwoSlopeNorm](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.TwoSlopeNorm.html#matplotlib.colors.TwoSlopeNorm), [CenteredNorm](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.CenteredNorm.html#matplotlib.colors.CenteredNorm) applies an equal rate of change around the center.
> Useful when mapping symmetrical data around a conceptual center e.g., data that range from -2 to 4, with 0 as the midpoint, and with equal rates of change around that midpoint.

Hence, I'd like to have us accept `CenteredNorm` as well. Currenly, this is not possible:

https://github.com/mne-tools/mne-python/blob/0452d69396ade5186bfad166aa7c7f608b288867/mne/viz/topomap.py#L800



Discussion:
cbrnr: The docs have always supported `matplotlib.colors.Normalize` (https://github.com/mne-tools/mne-python/blob/main/mne/utils/docs.py#L587-L592), it was just the check that was too strict.
wmvanvliet: Yup. It was a really easy fix.
hoechenberger: Great, thanks for the swift action on this, @wmvanvliet and @cbrnr!!
wmvanvliet: Error is real:

```
___________________________ test_plot_topomap_cnorm ____________________________
mne/viz/tests/test_topomap.py:690: in test_plot_topomap_cnorm
frommatplotlib.colorsimport CenteredNorm
E   ImportError: cannot import name 'CenteredNorm' from 'matplotlib.colors' (/usr/share/miniconda/envs/test/lib/python3.7/site-packages/matplotlib/colors.py)
```
wmvanvliet: I opted to just pick another `Normalizer` that is present in the old version, since any subclass should work.

Patch:
diff --git a/examples/decoding/decoding_rsa_sgskip.py b/examples/decoding/decoding_rsa_sgskip.py
index bf6cea810..6edd4f4ee 100644
--- a/examples/decoding/decoding_rsa_sgskip.py
+++ b/examples/decoding/decoding_rsa_sgskip.py
@@ -78,9 +78,8 @@ event_id['0/human bodypart/human/not-face/animal/natural']
 ##############################################################################
 # Read MEG data
 n_runs = 4  # 4 for full data (use less to speed up computations)
-fnames = [data_path / 'sample_subject_{}_tsss_mc.fif'.format(b)
-          for b in range(n_runs)]
-raws = [read_raw_fif(fname, verbose='error')
+fnames = [data_path / f'sample_subject_{b}_tsss_mc.fif' for b in range(n_runs)]
+raws = [read_raw_fif(fname, verbose='error', on_split_missing='ignore')
         for fname in fnames]  # ignore filename warnings
 raw = concatenate_raws(raws)
 
diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py
index e5f88b7dd..4a4754026 100644
--- a/mne/viz/topomap.py
+++ b/mne/viz/topomap.py
@@ -28,7 +28,7 @@ from ..io.pick import (pick_types, _picks_by_type, pick_info, pick_channels,
                        _MEG_CH_TYPES_SPLIT)
 from ..utils import (_clean_names, _time_mask, verbose, logger, fill_doc,
                      _validate_type, _check_sphere, _check_option, _is_numeric,
-                     warn, check_version)
+                     warn)
 from .utils import (tight_layout, _setup_vmin_vmax, _prepare_trellis,
                     _check_delayed_ssp, _draw_proj_checkbox, figure_nobar,
                     plt_show, _process_times, DraggableColorbar,
@@ -793,11 +793,8 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap=None, sensors=True,
     for more details on colormap normalization.
     """
     sphere = _check_sphere(sphere)
-    if check_version("matplotlib", "3.2.0"):
-        from matplotlib.colors import TwoSlopeNorm
-    else:
-        from matplotlib.colors import DivergingNorm as TwoSlopeNorm
-    _validate_type(cnorm, (TwoSlopeNorm, None), 'cnorm')
+    from matplotlib.colors import Normalize
+    _validate_type(cnorm, (Normalize, None), 'cnorm')
     if cnorm is not None:
         if vmin is not None:
             warn(f"vmin={cnorm.vmin} is implicitly defined by cnorm, ignoring "


Test patch:
diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py
index 3124b82e4..108b682aa 100644
--- a/mne/viz/tests/test_topomap.py
+++ b/mne/viz/tests/test_topomap.py
@@ -687,6 +687,7 @@ def test_plot_topomap_cnorm():
         from matplotlib.colors import TwoSlopeNorm
     else:
         from matplotlib.colors import DivergingNorm as TwoSlopeNorm
+    from matplotlib.colors import PowerNorm
 
     rng = np.random.default_rng(42)
     v = rng.uniform(low=-1, high=2.5, size=64)
@@ -709,6 +710,9 @@ def test_plot_topomap_cnorm():
     with pytest.warns(RuntimeWarning, match=msg):
         plot_topomap(v, info, vmax=10, cnorm=cnorm)
 
+    # try another subclass of mpl.colors.Normalize
+    plot_topomap(v, info, cnorm=PowerNorm(0.5))
+
 
 def test_plot_bridged_electrodes():
     """Test plotting of bridged electrodes."""