Matplotlib/pyplot: Auto adjust unit of y Axis

后端 未结 2 1185
别跟我提以往
别跟我提以往 2020-12-16 08:12

I would like to modify the Y axis unit of the plot indicated below. Preferable would be the use of units like M (Million), k (Thousand) for large numbers.

相关标签:
2条回答
  • 2020-12-16 08:28

    You were pretty close; one (possibly) confusing thing about FuncFormatter is that the first argument is the tick value, and the second the tick position , which (when named x,y) can be confusing for the y-axis. For clarity, I renamed them in the example below.

    The function should take in two inputs (tick value x and position pos) and return a string

    (http://matplotlib.org/api/ticker_api.html#matplotlib.ticker.FuncFormatter)

    Working example:

    import numpy as np
    import matplotlib.pylab as pl
    import matplotlib.ticker as tick
    
    def y_fmt(tick_val, pos):
        if tick_val > 1000000:
            val = int(tick_val)/1000000
            return '{:d} M'.format(val)
        elif tick_val > 1000:
            val = int(tick_val) / 1000
            return '{:d} k'.format(val)
        else:
            return tick_val
    
    x = np.arange(300)
    y = np.random.randint(0,2000000,x.size)
    
    width = 0.5
    pl.bar(x, y, width, align='center', linewidth=2, color='red', edgecolor='red')
    pl.xlim(0,300)
    
    ax = pl.gca()
    ax.yaxis.set_major_formatter(tick.FuncFormatter(y_fmt))
    

    0 讨论(0)
  • 2020-12-16 08:34

    In principle there is always the option to set custom labels via plt.gca().yaxis.set_xticklabels().

    However, I'm not sure why there shouldn't be the possibility to use matplotlib.ticker.FuncFormatter here. The FuncFormatter is designed for exactly the purpose of providing custom ticklabels depending on the ticklabel's position and value. There is actually a nice example in the matplotlib example collection.

    In this case we can use the FuncFormatter as desired to provide unit prefixes as suffixes on the axes of a matplotlib plot. To this end, we iterate over the multiples of 1000 and check if the value to be formatted exceeds it. If the value is then a whole number, we can format it as integer with the respective unit symbol as suffix. On the other hand, if there is a remainder behind the decimal point, we check how many decimal places are needed to format this number.

    Here is a complete example:

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.ticker import FuncFormatter
    
    def y_fmt(y, pos):
        decades = [1e9, 1e6, 1e3, 1e0, 1e-3, 1e-6, 1e-9 ]
        suffix  = ["G", "M", "k", "" , "m" , "u", "n"  ]
        if y == 0:
            return str(0)
        for i, d in enumerate(decades):
            if np.abs(y) >=d:
                val = y/float(d)
                signf = len(str(val).split(".")[1])
                if signf == 0:
                    return '{val:d} {suffix}'.format(val=int(val), suffix=suffix[i])
                else:
                    if signf == 1:
                        print val, signf
                        if str(val).split(".")[1] == "0":
                           return '{val:d} {suffix}'.format(val=int(round(val)), suffix=suffix[i]) 
                    tx = "{"+"val:.{signf}f".format(signf = signf) +"} {suffix}"
                    return tx.format(val=val, suffix=suffix[i])
    
                    #return y
        return y
    
    
    fig, ax = plt.subplots(ncols=3, figsize=(10,5))
    
    x = np.linspace(0,349,num=350) 
    y = np.sinc((x-66.)/10.3)**2*1.5e6+np.sinc((x-164.)/8.7)**2*660000.+np.random.rand(len(x))*76000.  
    width = 1
    
    ax[0].bar(x, y, width, align='center', linewidth=2, color='red', edgecolor='red')
    ax[0].yaxis.set_major_formatter(FuncFormatter(y_fmt))
    
    ax[1].bar(x[::-1], y*(-0.8e-9), width, align='center', linewidth=2, color='orange', edgecolor='orange')
    ax[1].yaxis.set_major_formatter(FuncFormatter(y_fmt))
    
    ax[2].fill_between(x, np.sin(x/100.)*1.7+100010, np.cos(x/100.)*1.7+100010, linewidth=2, color='#a80975', edgecolor='#a80975')
    ax[2].yaxis.set_major_formatter(FuncFormatter(y_fmt))
    
    for axes in ax:
        axes.set_title("TTL Distribution")
        axes.set_xlabel('TTL Value')
        axes.set_ylabel('Number of Packets')
        axes.set_xlim([x[0], x[-1]+1])
    
    plt.show()
    

    which provides the following plot:

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