【问题标题】:Matplotlib/pyplot: Auto adjust unit of y AxisMatplotlib/pyplot:自动调整y轴的单位
【发布时间】:2016-11-12 18:47:55
【问题描述】:

我想修改下图的 Y 轴单位。对于大数,最好使用 M(百万)、k(千)等单位。例如,y 轴应如下所示:50k、100k、150k 等。

下图由以下代码sn-p生成:

plt.autoscale(enable=True, axis='both')
plt.title("TTL Distribution")
plt.xlabel('TTL Value')
plt.ylabel('Number of Packets')
y = graphy  # data from a sqlite query
x = graphx  # data from a sqlite query
width = 0.5
plt.bar(x, y, width, align='center', linewidth=2, color='red', edgecolor='red')
fig = plt.gcf()
plt.show()

我看到this 的帖子并认为我可以编写自己的格式化函数:

def y_fmt(x, y):
    if max_y > 1000000:
        val = int(y)/1000000
        return '{:d} M'.format(val)
    elif max_y > 1000:
        val = int(y) / 1000
        return '{:d} k'.format(val)
    else:
        return y

但我错过了我正在使用的条形图没有可用的plt.yaxis.set_major_formatter(tick.FuncFormatter(y_fmt)) 函数。

如何更好地格式化 Y 轴?

[]

【问题讨论】:

    标签: matplotlib


    【解决方案1】:

    原则上,始终可以通过plt.gca().yaxis.set_xticklabels() 设置自定义标签。

    但是,我不确定为什么不能在这里使用matplotlib.ticker.FuncFormatterFuncFormatter 旨在根据刻度标签的位置和值提供自定义刻度标签。 matplotlib 示例集合中其实有一个nice example

    在这种情况下,我们可以根据需要使用 FuncFormatter 在 matplotlib 图的轴上提供单位前缀作为后缀。为此,我们遍历 1000 的倍数并检查要格式化的值是否超过它。如果该值是整数,我们可以将其格式化为整数,并以相应的单位符号作为后缀。另一方面,如果小数点后面有余数,我们检查需要多少小数位来格式化这个数字。

    这是一个完整的例子:

    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()
    

    提供以下情节:

    【讨论】:

    • 很好的例子,比我的更完整。但是你写“如果小数点后面有余数,我们检查需要多少个小数位”,但是截断还是有点随机?例如。 y_fmt(100100, 0) 返回 100.1 ky_fmt(100010, 0) 返回 100 k
    • @Bart 正确!我确实想过这个问题,但后来忘记了。我更新了示例以包含此类情况,其中数字很大但彼此也很接近。
    • 还有内置的EngFormatter,它做一些非常相似的开箱即用matplotlib.org/examples/api/engineering_formatter.html
    • @tacaswell 太好了!是在哪个版本中引入的? normal api doc 还没有(还没有?)。
    【解决方案2】:

    你已经很接近了;关于FuncFormatter 的一个(可能)令人困惑的事情是第一个参数是刻度值,第二个参数是刻度位置,它(当命名为x,y 时)可能会混淆y 轴。为了清楚起见,我在下面的示例中重命名了它们。

    该函数应接受两个输入(刻度值 x 和位置 pos)并返回一个字符串

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

    工作示例:

    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))
    

    【讨论】:

    • 感谢您的回答!但是,ImportanceOfBeingErnest 的答案还扩展了后缀,因此我将其标记为“已接受”。
    猜你喜欢
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 2018-07-04
    • 2021-10-09
    • 2013-08-04
    • 2011-01-04
    相关资源
    最近更新 更多