can't plot custom colorbar in mne with add_data

Hello!

  • MNE-Python version: 0.23.0
  • operating system: MacOS Catalina 10.15.7

When I try to use my own colormap (saved under cmap) with:

brain.add_data(L ,fmin=both_min, fmax=both_max, thresh=None, colormap=cmap, alpha=1, 
           time=None, colorbar=True, hemi='lh',  fmid=fmid, center=center, transparent=False, 
               verbose=None,colorbar_kwargs={'label_font_size':13})

    
brain.add_data(R,fmin=both_min, fmax=both_max, thresh=None,colormap=cmap, alpha=1, 
           time=None, colorbar=True, hemi='rh',  fmid=fmid, center=center, transparent=False, 
           verbose=None,vector_alpha=1) 

… I get the following error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/f3/rb5vg5j96dl8r447m2508smh0000gr/T/ipykernel_64842/3156049947.py in <module>
    311 
    312 
--> 313 brain.add_data(L ,fmin=both_min, fmax=both_max, thresh=None, colormap=cmap, alpha=1, 
    314            time=None, colorbar=True, hemi='lh',  fmid=fmid, center=center, transparent=False,
    315                verbose=None,colorbar_kwargs={'label_font_size':13})# vertices=L_verts)

<decorator-gen-173> in add_data(self, array, fmin, fmid, fmax, thresh, center, transparent, colormap, alpha, vertices, smoothing_steps, time, time_label, colorbar, hemi, remove_existing, time_label_size, initial_time, scale_factor, vector_alpha, clim, src, volume_options, colorbar_kwargs, verbose)

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/_brain.py in add_data(self, array, fmin, fmid, fmax, thresh, center, transparent, colormap, alpha, vertices, smoothing_steps, time, time_label, colorbar, hemi, remove_existing, time_label_size, initial_time, scale_factor, vector_alpha, clim, src, volume_options, colorbar_kwargs, verbose)
   1955         self._data['fmid'] = fmid
   1956         self._data['fmax'] = fmax
-> 1957         self.update_lut()
   1958 
   1959         # 1) add the surfaces first

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/_brain.py in update_lut(self, fmin, fmid, fmax, alpha)
   2704         self._data.update(lims)
   2705         self._data['ctable'] = np.round(
-> 2706             calculate_lut(colormap, alpha=1., center=center,
   2707                           transparent=transparent, **lims) *
   2708             255).astype(np.uint8)

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/colormap.py in calculate_lut(lut_table, alpha, fmin, fmid, fmax, center, transparent)
    114         raise ValueError('Must have fmin (%s) <= fmid (%s) <= fmax (%s)'
    115                          % (fmin, fmid, fmax))
--> 116     lut_table = create_lut(lut_table)
    117     assert lut_table.dtype.kind == 'i'
    118     divergent = center is not None

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/colormap.py in create_lut(cmap, n_colors, center)
     13     from .._3d import _get_cmap
     14     assert not (isinstance(cmap, str) and cmap == 'auto')
---> 15     cmap = _get_cmap(cmap)
     16     lut = np.round(cmap(np.linspace(0, 1, n_colors)) * 255.0).astype(np.int64)
     17     return lut

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_3d.py in _get_cmap(colormap)
   1202         colormap = mne_analyze_colormap([0, 1, 2], format='matplotlib')
   1203     else:
-> 1204         colormap = plt.get_cmap(colormap)
   1205     return colormap
   1206 

~/anaconda3/envs/mne/lib/python3.9/site-packages/matplotlib/cm.py in get_cmap(name, lut)
    188     if isinstance(name, colors.Colormap):
    189         return name
--> 190     _api.check_in_list(sorted(_cmap_registry), name=name)
    191     if lut is None:
    192         return _cmap_registry[name]

~/anaconda3/envs/mne/lib/python3.9/site-packages/matplotlib/_api/__init__.py in check_in_list(_values, _print_supported_values, **kwargs)
    124         if val not in values:
    125             if _print_supported_values:
--> 126                 raise ValueError(
    127                     f"{val!r} is not a valid value for {key}; "
    128                     f"supported values are {', '.join(map(repr, values))}")

ValueError: array([[ 49,  93, 191, 255],
       [184, 230, 254, 255]]) is not a valid value for name; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'

I’ve tried to follow the docs which say:

colormap : str, list of color, or array
Name of matplotlib colormap to use, a list of matplotlib colors,
or a custom look up table (an n x 4 array coded with RBGA values
between 0 and 255), the default “auto” chooses a default divergent
colormap, if “center” is given (currently “icefire”), otherwise a
default sequential colormap (currently “rocket”).

So, I created a colormap like so:

import matplotlib.colors
c0 = (matplotlib.colors.to_rgba_array('#315dbf')*255).astype(int) #convert decimals to ints 0-255
c1 = (matplotlib.colors.to_rgba_array('#b8e6fe')*255).astype(int)

cmap = np.row_stack((c0,c1)) #to get to shape (n x 4)

But, even if I have the right format, I can’t seem to plot with custom colors.

Any help would be appreciated!

Thanks!

if I’m reading your example correctly, your cmap array has shape (2, 4). That means you’ll only get the 2 endpoints, with no intermediate colors interpolated between them. Maybe this will work better:

c0 = (matplotlib.colors.to_rgba_array('#315dbf'))
c1 = (matplotlib.colors.to_rgba_array('#b8e6fe'))
my_cmap = matplotlib.colors.LinearSegmentedColormap.from_list('my_cmap', [c0, c1])

The default of from_list is 256 levels, which should be fine. I’m not sure if we accept a colormap object (we probably should, but the docstring suggests we don’t), but you can get the n x 4 array like this:

my_color_array = (my_cmap(np.linspace(0, 1, n)) * 255).astype(int)

for some suitable value of n (you can use 256 if you want to match the default granularity of built-in colormaps, but in practice anything over ~100 should be fine)

1 Like

Thanks so much for getting back to me!!!

So, two discrete colors is what I want in this particular case (trying to color two specific subclusters of an ROI), although I have tried with no success using custom colormaps for continuous values. I’d love to be able to use my own colormaps, but no success yet with mne. More suggestions would be insanely appreciated! How can I modify the source code to let me do this?

I tried running the code you suggested with n=256 but I’m getting the same error:

I ran using:

cmap = my_color_array

…or with cmap=‘my_cmap’

and then what I get is:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/f3/rb5vg5j96dl8r447m2508smh0000gr/T/ipykernel_64842/1456123665.py in <module>
    316 
    317 
--> 318 brain.add_data(L ,fmin=both_min, fmax=both_max, thresh=None, colormap=cmap, alpha=1, 
    319            time=None, colorbar=True, hemi='lh',  fmid=fmid, center=center, transparent=False,
    320                verbose=None,colorbar_kwargs={'label_font_size':13})# vertices=L_verts)

<decorator-gen-173> in add_data(self, array, fmin, fmid, fmax, thresh, center, transparent, colormap, alpha, vertices, smoothing_steps, time, time_label, colorbar, hemi, remove_existing, time_label_size, initial_time, scale_factor, vector_alpha, clim, src, volume_options, colorbar_kwargs, verbose)

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/_brain.py in add_data(self, array, fmin, fmid, fmax, thresh, center, transparent, colormap, alpha, vertices, smoothing_steps, time, time_label, colorbar, hemi, remove_existing, time_label_size, initial_time, scale_factor, vector_alpha, clim, src, volume_options, colorbar_kwargs, verbose)
   1955         self._data['fmid'] = fmid
   1956         self._data['fmax'] = fmax
-> 1957         self.update_lut()
   1958 
   1959         # 1) add the surfaces first

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/_brain.py in update_lut(self, fmin, fmid, fmax, alpha)
   2704         self._data.update(lims)
   2705         self._data['ctable'] = np.round(
-> 2706             calculate_lut(colormap, alpha=1., center=center,
   2707                           transparent=transparent, **lims) *
   2708             255).astype(np.uint8)

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/colormap.py in calculate_lut(lut_table, alpha, fmin, fmid, fmax, center, transparent)
    114         raise ValueError('Must have fmin (%s) <= fmid (%s) <= fmax (%s)'
    115                          % (fmin, fmid, fmax))
--> 116     lut_table = create_lut(lut_table)
    117     assert lut_table.dtype.kind == 'i'
    118     divergent = center is not None

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_brain/colormap.py in create_lut(cmap, n_colors, center)
     13     from .._3d import _get_cmap
     14     assert not (isinstance(cmap, str) and cmap == 'auto')
---> 15     cmap = _get_cmap(cmap)
     16     lut = np.round(cmap(np.linspace(0, 1, n_colors)) * 255.0).astype(np.int64)
     17     return lut

~/anaconda3/envs/mne/lib/python3.9/site-packages/mne/viz/_3d.py in _get_cmap(colormap)
   1202         colormap = mne_analyze_colormap([0, 1, 2], format='matplotlib')
   1203     else:
-> 1204         colormap = plt.get_cmap(colormap)
   1205     return colormap
   1206 

~/anaconda3/envs/mne/lib/python3.9/site-packages/matplotlib/cm.py in get_cmap(name, lut)
    188     if isinstance(name, colors.Colormap):
    189         return name
--> 190     _api.check_in_list(sorted(_cmap_registry), name=name)
    191     if lut is None:
    192         return _cmap_registry[name]

~/anaconda3/envs/mne/lib/python3.9/site-packages/matplotlib/_api/__init__.py in check_in_list(_values, _print_supported_values, **kwargs)
    124         if val not in values:
    125             if _print_supported_values:
--> 126                 raise ValueError(
    127                     f"{val!r} is not a valid value for {key}; "
    128                     f"supported values are {', '.join(map(repr, values))}")

ValueError: array([[ 49,  93, 191, 255],
       [ 49,  93, 191, 255],
       [ 50,  94, 191, 255],
       ...,
       [182, 228, 253, 255],
       [183, 229, 253, 255],
       [184, 230, 254, 255]]) is not a valid value for name; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'

What’s going on?

hmm… working with this tutorial Plotting with mne.viz.Brain — MNE 1.0.dev0 documentation

I modified the second code cell to include

import matplotlib
my_cmap = matplotlib.colors.LinearSegmentedColormap.from_list('my_cmap', ['#315dbf', '#b8e6fe'], N=2)

and then added colormap=my_cmap to the kwargs dict. Then after the first (left-hemi) call to .add_data() I see this:

So to me it seems like it’s working if you just pass the LinearSegmentedColormap object directly to .add_data(), and you can get just the binary colormap by passing N=2 when you instantiate the colormap object.

That said, in my brief tests it seems like it doesn’t work to pass it a list of colors or an array of 0-255 integers, so the docstring does not match the behavior of the code here. Can you open a bug report on our GitHub repo briefly describing the problem and linking back to this thread? It would be great also if you could assemble a short script based on the tutorial I linked to that illustrates the problem (so other devs can easily copy-paste to reproduce the bug).