Matplotlib imshow - 'speed up' colour change in certain value ranges

后端 未结 1 1386
醉梦人生
醉梦人生 2021-01-16 05:56

I\'m currently working to plot some labels over the x,y axis in imshow, but over 95% of the points sit in the 0-0.2 range, whereas less than 10% sit in the 0.2-1.0 range. Us

相关标签:
1条回答
  • 2021-01-16 06:21

    In case you want to emphazise small values in your data in an image plot, I would never change the actual data itself. That can lead to a lot of confusion. Instead, as I said in the comments, change the colormap.

    Ways of doing so are documented in the Matplotlib Color Normalization Tutorial as well as here on SO. Especially this article and the answers within are really illustrative of the possibilities one has.

    I combined two concepts in the example below to show the options.

    • One is to rescale the colormap such that the value that initially was at the middle (midpoint) of your colormap is shifted down. In this way more variation is added between 0 and the new midpoint, while everything above is stretched. One can think of this as two linear colormaps spliced together.
    • The other is to simply use a logarithmic scaling of the colors.

    This is the example code

    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    import matplotlib.colors as colors
    
    
    def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
        '''
        function taken from
        https://stackoverflow.com/questions/7404116/...
            ...defining-the-midpoint-of-a-colormap-in-matplotlib
        Function to offset the "center" of a colormap. Useful for
        data with a negative min and positive max and you want the
        middle of the colormap's dynamic range to be at zero
    
        Input
        -----
          cmap : The matplotlib colormap to be altered
          start : Offset from lowest point in the colormap's range.
              Defaults to 0.0 (no lower ofset). Should be between
              0.0 and `midpoint`.
          midpoint : The new center of the colormap. Defaults to 
              0.5 (no shift). Should be between 0.0 and 1.0. In
              general, this should be  1 - vmax/(vmax + abs(vmin))
              For example if your data range from -15.0 to +5.0 and
              you want the center of the colormap at 0.0, `midpoint`
              should be set to  1 - 5/(5 + 15)) or 0.75
          stop : Offset from highets point in the colormap's range.
              Defaults to 1.0 (no upper ofset). Should be between
              `midpoint` and 1.0.
        '''
        cdict = {  'red': [],  'green': [], 'blue': [],  'alpha': []  }
    
        # regular index to compute the colors
        reg_index = np.linspace(start, stop, 257)
    
        # shifted index to match the data
        shift_index = np.hstack([
            np.linspace(0.0, midpoint, 128, endpoint=False), 
            np.linspace(midpoint, 1.0, 129, endpoint=True)
        ])
    
        for ri, si in zip(reg_index, shift_index):
            r, g, b, a = cmap(ri)
    
            cdict['red'].append((si, r, r))
            cdict['green'].append((si, g, g))
            cdict['blue'].append((si, b, b))
            cdict['alpha'].append((si, a, a))
    
        newcmap = matplotlib.colors.LinearSegmentedColormap(name, cdict)
        plt.register_cmap(cmap=newcmap)
    
        return newcmap
    
    
    x = np.linspace(-3, 3, num=601)
    X,Y = np.meshgrid(x,x)
    Z = np.sinc( (X*np.cos(1)+Y*np.sin(1))**2 +(-X*np.sin(1)+0.2*Y*np.cos(1))**2 )**2 
    
    orig_cmap = matplotlib.cm.viridis 
    shifted_cmap = shiftedColorMap(orig_cmap, midpoint=0.05, name='shifted')
    
    
    fig = plt.figure(figsize=(4,9))
    ax = [fig.add_subplot(3,1,n+1) for n in range(3)]
    
    # normal cmap
    im0 = ax[0].imshow(Z, interpolation="none", cmap=orig_cmap)
    fig.colorbar(im0, ax=ax[0])
    ax[0].set_title('Default behavior (hard to see small values)', fontsize=10)
    
    #example using the custom shiftedColorMap function
    #taken from https://stackoverflow.com/questions/7404116/defining-the-midpoint-of-a-colormap-in-matplotlib
    im1 = ax[1].imshow(Z, interpolation="none", cmap=shifted_cmap)
    fig.colorbar(im1, ax=ax[1])
    ax[1].set_title('Center of colormap shifted to 0.05', fontsize=10)
    
    #example using colors.LogNorm()
    #taken from http://matplotlib.org/users/colormapnorms.html
    im2 = ax[2].imshow(Z, interpolation="none", norm=colors.LogNorm(vmin=10e-5, vmax=Z.max()), cmap=orig_cmap)
    fig.colorbar(im2, ax=ax[2])
    ax[2].set_title('Logarithmically scaled Colormap', fontsize=10)
    
    for axis in ax:
        axis.set_yticks([])
        axis.set_xticks([])
    plt.tight_layout()    
    plt.show() 
    

    producing

    0 讨论(0)
提交回复
热议问题