2016-11-12 348 views
0

我想修改下面指示的图的Y轴单位。对于大数量,优选使用诸如M(百万),k(千)的单位。例如,Y轴应该是这样的:50K,100K,150K等Matplotlib/pyplot:自动调整y轴的单位

以下信息由下面的代码片段生成的情节:

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轴格式?

[TTL Distribution Plot]

回答

2

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

但是,我不确定为什么不应该有可能在这里使用matplotlib.ticker.FuncFormatterFuncFormatter专门用于根据ticklabel的位置和价值提供自定义标签。 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() 

它提供了以下情节:

enter image description here

+0

不错的例子,比我的完整得多。但是你写*“如果有一个小数点后面的余数,我们检查需要多少小数位”*,但是截断点仍然有点随机?例如。 'y_fmt(100100,0)'返回'100.1 k',但'y_fmt(100010,0)'返回'100 k'。 – Bart

+1

@Bart正确!我确实想到了这个问题,但之后就忘记了。我更新了这个例子,以包括这样的情况,其中数字很大但彼此接近。 – ImportanceOfBeingErnest

+1

还有内置的'EngFormatter',它可以做一些非常类似于开箱即用的工具http://matplotlib.org/examples/api/engineering_formatter.html – tacaswell

0

你是相当接近;关于FuncFormatter的一个(可能)令人困惑的事情是,第一个参数是刻度值,第二个是tick位置,这个位置(当名称为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)) 

enter image description here

+1

谢谢您的回答!但是,来自ImportanceOfBeingErnest的答案还扩展了后缀,因此我将其标记为“已接受”。 – Patrick