1

I want to plot 2D data in a small script and let the user choose vmin/vamx ( i call them threshold and -threshold) of the values in the dataset to be plotted. Ideally i want all the values below/above of vmin/vamx to be transparent. The midpoint of the colormap should always be at 0 and white!. I would like to have as much of the dynamic range as i can get. The color should range from blue for negative to white at 0 to red for positive values! The value threshold and number_of_contours is set by the user and the scale would have to be adapted accordingly.

I found a few hints on various forms but could not make them work for me. Here is what i have found so far:

If i use the solution supplied in the third link by user Paul H i get the error r, g, b, a = cmap(ri) TypeError: 'str' object is not callable no matter what i do. I tried to use the solution "midpointnorm" which can also be found in the links above. Here is a small example:

    from numpy import*
    import matplotlib
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib import cm
    import pylab
    from matplotlib.colors import Normalize
    # class to norm a color sacle so the midpoint is ecatcly in the middle of the colormap . This is usefull if a colormap goes form color1 to white to color2
    class MidPointNorm(Normalize):    
        def __init__(self, midpoint=0, vmin=None, vmax=None, clip=False):
            Normalize.__init__(self,vmin, vmax, clip)
            self.midpoint = midpoint

        def __call__(self, value, clip=None):
            if clip is None:
                clip = self.clip

            result, is_scalar = self.process_value(value)

            self.autoscale_None(result)
            vmin, vmax, midpoint = self.vmin, self.vmax, self.midpoint

            if not (vmin < midpoint < vmax):
                raise ValueError("midpoint must be between maxvalue and minvalue.")       
            elif vmin == vmax:
                result.fill(0) # Or should it be all masked? Or 0.5?
            elif vmin > vmax:
                raise ValueError("maxvalue must be bigger than minvalue")
            else:
                vmin = float(vmin)
                vmax = float(vmax)
                if clip:
                    mask = ma.getmask(result)
                    result = ma.array(np.clip(result.filled(vmax), vmin, vmax),
                                      mask=mask)

                # ma division is very slow; we can take a shortcut
                resdat = result.data

                #First scale to -1 to 1 range, than to from 0 to 1.
                resdat -= midpoint            
                resdat[resdat>0] /= abs(vmax - midpoint)            
                resdat[resdat<0] /= abs(vmin - midpoint)

                resdat /= 2.
                resdat += 0.5
                result = ma.array(resdat, mask=result.mask, copy=False)                

            if is_scalar:
                result = result[0]            
            return result

        def inverse(self, value):
            if not self.scaled():
                raise ValueError("Not invertible until scaled")
            vmin, vmax, midpoint = self.vmin, self.vmax, self.midpoint

            if cbook.iterable(value):
                val = ma.asarray(value)
                val = 2 * (val-0.5)  
                val[val>0]  *= abs(vmax - midpoint)
                val[val<0] *= abs(vmin - midpoint)
                val += midpoint
                return val
            else:
                val = 2 * (val - 0.5)
                if val < 0: 
                    return  val*abs(vmin-midpoint) + midpoint
                else:
                    return  val*abs(vmax-midpoint) + midpoint
    # Make some illustrative fake data:

    x = np.arange(0, np.pi, 0.1)
    y = np.arange(0, 2*np.pi, 0.1)
    X, Y = np.meshgrid(x, y)
    Z = np.cos(X) * np.sin(Y) * 10
    # Make the color i want to use


    cdict3 = {'red':  ((0.0, 0.0, 0.0),
                       (0.25, 0.0, 0.0),
                       (0.5, 0.8, 1.0),
                       (0.75, 1.0, 1.0),
                       (1.0, 0.4, 1.0)),

             'green': ((0.0, 0.0, 0.0),
                       (0.25, 0.0, 0.0),
                       (0.5, 0.9, 0.9),
                       (0.75, 0.0, 0.0),
                       (1.0, 0.0, 0.0)),

             'blue':  ((0.0, 0.0, 0.4),
                       (0.25, 1.0, 1.0),
                       (0.5, 1.0, 0.8),
                       (0.75, 0.0, 0.0),
                       (1.0, 0.0, 0.0))
            }

    # Make a modified version of cdict3 with some transparency
    # in the middle of the range.
    cdict4 = cdict3.copy()
    cdict4['alpha'] = ((0.0, 1.0, 1.0),
                    #   (0.25,1.0, 1.0),
                       (0.5, 0.3, 0.3),
                    #   (0.75,1.0, 1.0),
                       (1.0, 1.0, 1.0))


    #########################################MY questions start here###########################################                
                       
    threshold=10        # i only want to plot data in the range [-threshold,threshold]     
    number_of_contours=100  

    ##Instead of just giving a number of contours i thought about giving a predefined list wich contains that speciefies the point of the contours.
    ##The poinst denisty should be heigher at the Ends of the intervall as the data at theses ends is of higher interest for me. The total number of points should be variable.
    #levels = [-threshold, -0.005, -0.001, 0, 0.001,0.005, threshold]                  



    norm = MidPointNorm(midpoint=0,vmin=-threshold, vmax=threshold) # norm the colormap                
                   
                       
                       
    plt.register_cmap(name='BlueRedAlpha', data=cdict4)


    im4 = pylab.contourf(x,y,Z,number_of_contours, cmap='BlueRedAlpha',interpolation='nearest',norm=norm)
    #im4 = pylab.contourf(x,y,Z,number_of_contours, cmap='BlueRedAlpha',interpolation='nearest',vmin=-threshold,vmax=threshold,norm=norm)# is it enough to give the norm parameter after the normalisation or do i have to give vmin/vmas to be sure?
    sm = plt.cm.ScalarMappable(cmap='BlueRedAlpha', norm=plt.Normalize(vmin=-threshold, vmax=threshold)) 
    sm._A = []

    plt.colorbar(sm,extend="both")





    plt.show()
                

This somehow works now. I do not understand the code in the class midpointnorm. Is it enough to pass the norm parameter to contourf in the after the normalization or do i have to give vmin/vmax values to be sure that only values between vmin and vmax are ploted and to be sure? Are vmin and vmax ignored if i give a norm parameter?

But i would like to change the dynamic range in a way, that the color scale is not saturated for the vmin and vmax values.

More importantly, at the moment i am displaying values between -10 and 10 in the code. In my Datas i plot values between -0.03 and 0.03 and everything close to these values is already saturated as it is at the upper end of dynamic range. But this region is of highes interest for me. I need a colorscale wich goes from blue (negative) to red ( positive). I would like to have some control about the behavior were the contourlines are.

I know you can give a list of contourlines to contourf. Does somebody have an idea how to fill a list with length(number_of_contours+1) ranging from the values from "-threshold" to "+threshold" were the spacing of the elements is closer at the positive and negative end of the range? DO you have any other idea?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
NorrinRadd
  • 545
  • 6
  • 21
  • Can you tell us, why "Solution1" did not help? I would be tempted to use exactly this kind of solution. – ImportanceOfBeingErnest May 10 '17 at 09:16
  • Can you add the comments into the question and delete them afterwards? I have problems understanding the dynamic range issue. Would you be able to show a [mcve] using the `MidpointNormalize` solution (just invent some data) from which you can explain what goes wrong. – ImportanceOfBeingErnest May 10 '17 at 11:40
  • Hello I tried to edit my post accordingly. A minimal working example is hard to do in this case. I copied the MidPointNorm class and marked were my code begins. I tried to add comments to highlight what i want to do. – NorrinRadd May 10 '17 at 14:07
  • Sorry, without a running code I cannot see the problem and thus cannot help you. – ImportanceOfBeingErnest May 11 '17 at 07:56
  • Dear user :ImportanceOfBeingErnest you were absolutely correct my question was not asked well. I tried to solve a few Problems myself and changed the Question and included a MWE even if the artificial data is not a good representation of what i want to plot. – NorrinRadd May 17 '17 at 10:36

0 Answers0