Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diverging colormaps for scalp plots: Always have "white" as zero #5693

Closed
sappelhoff opened this issue Nov 4, 2018 · 11 comments
Closed

Diverging colormaps for scalp plots: Always have "white" as zero #5693

sappelhoff opened this issue Nov 4, 2018 · 11 comments
Milestone

Comments

@sappelhoff
Copy link
Member

sappelhoff commented Nov 4, 2018

Problem description

I want to compare scalp maps of ERPs across three different tasks. To allow for comparability between the subplots, they all need to share the same colorbar, which I can achieve by the following:

  1. find the maximum value across all tasks and the minimum value across all tasks
  2. pass max and min from step 1. into the vmin and vmax arguments when plotting

The result is as follows (note the colorbar):

figure_1

The problem is, that the white color of the "RdBu_r" colormap should be centered on zero, but this is not the case, as soon as vmin != vmax.

A workaround is to perform an intermediate step between 1. and 2. from above:

  1. find the maximum value across all tasks and the minimum value across all tasks
  2. find the absolute maximum of the max and min identified in step 1.
  3. pass abs_max from step 2. into both the vmin and vmax arguments when plotting

The result is as follows (note the colorbar ... vmin == vmax):

figure_2

While it is great to have the white color centered on the zero value, here the problem is that we lose some of the color range into one direction of the scale. In this example, this is visible in the red colors of the positive scale ... dark red is at 6 ... but the maximum is actually 4, so dark red is never reached.

Potential solutions

One solution to get the full color range and still the zero value centered on white color is described here (also provides a minimal working example to play around with):

http://chris35wills.github.io/matplotlib_diverging_colorbar/

Basically, it is possible by subclassing mpl.color.Normalize

# set the colormap and centre the colorbar
class MidpointNormalize(mpl.colors.Normalize):
    """Normalise the colorbar."""
    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
        self.midpoint = midpoint
        mpl.colors.Normalize.__init__(self, vmin, vmax, clip)

    def __call__(self, value, clip=None):
        x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(value, x, y), np.isnan(value))

... and then passing this class as an argument to plt.imshow's parameter called norm:

im = ax.imshow(mydata, vmin=mymin, vmax=mymax, norm=MidpointNormalize(mymin, mymax, 0.))

What are your thoughts on implementing something like this in MNE-Python? How have other people solved this problem?

cc @cbrnr @jona-sassenhagen

@jona-sassenhagen
Copy link
Contributor

Also see matplotlib/matplotlib#11377

@cbrnr
Copy link
Contributor

cbrnr commented Nov 5, 2018

Yes, I need that all the time, and I have included a function in my ERDS example, which doesn't subclass and is pretty simple. If it is needed in more places, we could make it available somewhere in the viz package.

@sappelhoff
Copy link
Member Author

sappelhoff commented Nov 5, 2018

@jona-sassenhagen is Clemens' function also a solution for your problem?

At least for my part it's exactly what I regularly need and I'd be +1 for including it in mne.viz.

Edit: Just saw the following note linked at a matplotlib example:

As above, non-symmetric mapping of data to color is non-standard practice for quantitative data, and should only be used advisedly

What are your thoughts on using non-symmetric mapping in the case of ERPs?

@jona-sassenhagen
Copy link
Contributor

I think our zero should be white :)

Clemens‘ function isn’t really something I’d do myself, but if you can convince Alex, sure. Feel free to also add it to Eg plot_erpimage.

@cbrnr
Copy link
Contributor

cbrnr commented Nov 5, 2018

Feel free to use the other approach, but I think we agree that something should be added.

@sappelhoff
Copy link
Member Author

I think our zero should be white :)

I agree - my question was targeting whether a colorbar SHOULD be symmetric ... or can be asymmetric, see:
image

Apparently, it is "a bad idea" to have them asymmetric, see the note in here. I was wondering whether you ( all MNE people/scientists) also think that it's a bad idea to use asymmetric colorbar limits as shown in the upper panel of the example.

Clemens‘ function isn’t really something I’d do myself

Why not?

Feel free to use the other approach, but I think we agree that something should be added.

Yes, let's figure out the best way to do it

@cbrnr
Copy link
Contributor

cbrnr commented Nov 5, 2018

In some cases, asymmetric color bars make sense. For example, ERDS values are bounded by -100% in the negative direction, but are unbounded in the positive direction. I usually choose a sensible upper bound, e.g. 250%, because most values lie within that range. I still want 0% to be white, so in the case of ERDS asymmetric ranges totally make sense.

I'm not sure about ERPs though - there is no inherent reason why one direction is scaled differently than the other. In your example, it looks more like an interpolation artifact because these negative values occur at the outermost electrodes.

@jona-sassenhagen
Copy link
Contributor

Ok, I guess my intuition would be that truncation of the colorbar is ok, but for gaussian data like MEEG, a nonlinear colormap is not. I strongly prefer the second image.

@larsoner
Copy link
Member

larsoner commented Nov 5, 2018

I also prefer the second image. But preferences aside, I think that it's fine for advanced users to be able to do this sort of tweaking when they know what they are doing -- but that we should not promote it because a) it's likely to be abused more often than used properly and b) complicates the code base.

What I we could consider doing is adding the asymmetric cmap conversion function in https://github.com/mne-tools/mne-python/blob/master/examples/time_frequency/plot_time_frequency_erds.py#L54 to mne.viz.utils with tests, and then use it in the example. That's a sort of in-between that allows us not to pollute our viz functions with more arguments, but also facilitates code reuse and simplifies things for advanced users.

@sappelhoff
Copy link
Member Author

@cbrnr do you want to do it?

@cbrnr
Copy link
Contributor

cbrnr commented Nov 6, 2018

Sure, I'll go ahead and make a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants