matplotlib contour plot: proportional colorbar levels in logarithmic scale

后端 未结 1 787
孤城傲影
孤城傲影 2021-02-04 10:30

Would it be possible to have levels of the colorbar in log scale like in the image below?

\"enter

1条回答
  •  余生分开走
    2021-02-04 11:01

    I propose to generate a pseudo colorbar as follows (see comments for explanations):

    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.colors import LogNorm
    import matplotlib.gridspec as gridspec
    
    delta = 0.025
    
    x = y = np.arange(0, 3.01, delta)
    X, Y = np.meshgrid(x, y)
    Z1 = plt.mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
    Z2 = plt.mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
    Z = 1e6 * (Z1 * Z2)
    
    fig=plt.figure()
    
    #
    # define 2 subplots, using gridspec to control the 
    # width ratios:
    #
    # note: you have to import matplotlib.gridspec for this
    #
    gs = gridspec.GridSpec(1, 2,width_ratios=[15,1])
    
    # the 1st subplot
    ax1 = plt.subplot(gs[0])
    
    lvls = np.logspace(0,4,20)
    
    CF = ax1.contourf(X,Y,Z,
                      norm = LogNorm(),
                      levels = lvls
                     )
    CS = ax1.contour(X,Y,Z,
                     norm = LogNorm(),
                     colors = 'k',
                     levels = lvls
                    )
    
    #
    # the pseudo-colorbar
    #
    
    # the 2nd subplot
    ax2 = plt.subplot(gs[1])        
    
    #
    # new levels!
    #
    # np.logspace gives you logarithmically spaced levels - 
    # this, however, is not what you want in your colorbar
    #
    # you want equally spaced labels for each exponential group:
    #
    levls = np.linspace(1,10,10)
    levls = np.concatenate((levls[:-1],np.linspace(10,100,10)))
    levls = np.concatenate((levls[:-1],np.linspace(100,1000,10)))
    levls = np.concatenate((levls[:-1],np.linspace(1000,10000,10)))
    
    #
    # simple x,y setup for a contourf plot to serve as colorbar
    #
    XC = [np.zeros(len(levls)), np.ones(len(levls))]
    YC = [levls, levls]
    CM = ax2.contourf(XC,YC,YC, levels=levls, norm = LogNorm())
    # log y-scale
    ax2.set_yscale('log')  
    # y-labels on the right
    ax2.yaxis.tick_right()
    # no x-ticks
    ax2.set_xticks([])
    
    plt.show()
    

    This will give you a plot like this:

    pseudo-colorbar

    EDIT

    Or, use something like the new levels and the spacing='proportional' option when calling the colorbar:

    1. replace this line:

      lvls = np.logspace(0,4,20)  
      

      with these:

      lvls = np.linspace(1,10,5)
      lvls = np.concatenate((lvls[:-1],np.linspace(10,100,5)))
      lvls = np.concatenate((lvls[:-1],np.linspace(100,1000,5)))
      lvls = np.concatenate((lvls[:-1],np.linspace(1000,10000,5)))
      
    2. replace this line:

      cbar = plt.colorbar(CF, ticks=lvls, format='%.4f')
      

      with this:

      cbar = plt.colorbar(CF, ticks=lvls, format='%.2f', spacing='proportional')
      

    And you will end up with this plot:

    real-colorbar

    (the format was only changed, because the new ticks do not require 4 decimals)

    EDIT 2
    If you wanted to automatically generate levels like the ones I have used, you can consider this piece of code:

    levels = []
    LAST_EXP = 4
    N_LEVELS = 5
    for E in range(0,LAST_EXP):
        levels = np.concatenate((levels[:-1],np.linspace(10**E,10**(E+1),N_LEVELS)))
    

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