matplot模块之坐标轴刻度
先看以下两段代码,他们实现的是同样的功能:绘制正余弦两个子图:
代码一:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,10,1000) plt.subplot(2, 1, 1) plt.plot(x, np.sin(x)) #用plt绘制第一个子图 plt.subplot(2,1,2) plt.plot(x,np.cos(x)) #用plt绘制第二个子图 plt.show()
代码二:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,10,1000) fig, ax = plt.subplots(2,1) ax[0].plot(x, np.sin(x)) #用对应的坐标轴对象画第一个子图 ax[1].plot(x, np.cos(x)) #用对应的坐标轴对象画第二个子图 plt.show()

可以看出,这两段代码所达到的效果是一样的。代码段一采用的是MATLAB风格的接口,这是由于Matplotlib最初是作为MATLAB替代品的历史缘故。MATLAB风格的工具位于pyplot 即plt接口中,因此我们可以采用plt.plot函数来进行绘图。这种接口最重要的特征就是“有状态”:他表征当前所位于的子图状态,并持续跟踪当前的图形和坐标轴。并可以通过plt.gcf()获取当前的活动Figure对象,利用plt.gca()获取当前活动的axes坐标轴对象。
而代码段二所采用的是完全不同的第二种模式,即面向对象接口,他在绘图的过程之中不再受到当前所谓“活动”图形及坐标轴的限制,因为他一次性就从subplots函数中获取了表征所有坐标轴的ax数组和Figure对象。
补充一点,坐标轴对象ax包含了横轴、纵轴所包围的区域内的一切(各坐标轴、刻度、标签、图形等)
我最后来说说plt和ax二者的联系,plt的绝大多数方法都有ax的对应版本,有的一模一样,有的稍作改变:
plt.plot() = ax.plot() plt.legend() = ax.legend() plt.xlabel()/ylabel()/xlim()/ylim()/title() = ax.setxlabel()/setylabel()/setxlim()/set_ylim()/set_title()
当然,如果用ax来设置这些参数的话,可以放在一个函数中,用多个关键字进行表征和设置
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,10,1000) ax = plt.axes() ax.plot(x,np.sin(x)) ax.set(xlim=(0,10), ylim=(-2,2), xlabel='x', ylabel='sin(x)',title='plot sin(x)') plt.show()
值得一提的是,在代码段一中我们还可以通过ax = plt.subplot(2, 1, 1)或ax = plt.gca()这两种方法来获得当前的坐标轴对象ax。
强化了坐标轴ax的概念之后,我们再进入今天另一个重要的话题,坐标轴的主次刻度。
每一个坐标轴都有主要刻度线与次要刻度线。主要刻度更大更显著,而次要刻度往往更小。主刻度都显示为一个较大的刻度线和标签,而次要刻度都显示为一个较小的刻度线,而不显示标签。主次刻度这件事儿,大家想想我们用过的尺子就知道了,厘米的地方刻度要长而明显,并且有数字标识,而毫米的地方刻度则要短,并且没有数字标识。这么做既能满足刻度线的完整性,又能突出刻度标识的重点。
我们先举一个例子:
import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import MultipleLocator, FormatStrFormatter fig = plt.figure() xMajorLocator = MultipleLocator(20) # 将x主刻度标签设置为20的倍数 xMajorFormatter = FormatStrFormatter('%1.1f') # 设置x轴标签文本的格式 一位小数的浮点数 xMinorLocator = MultipleLocator(5) # 将x轴次刻度标签设置为5的倍数 yMajorLocator = MultipleLocator(0.5) # 将y主刻度标签设置为20的倍数 yMajorFormatter = FormatStrFormatter('%1.1f') # 设置y轴标签文本的格式 一位小数的浮点数 yMinorLocator = MultipleLocator(0.1) # 将y轴次刻度标签设置为5的倍数 ax = plt.subplot(111) ax.xaxis.set_major_locator(xMajorLocator) ax.xaxis.set_major_formatter(xMajorFormatter) ax.yaxis.set_major_locator(yMajorLocator) ax.yaxis.set_major_formatter(yMajorFormatter) ax.xaxis.set_minor_locator(xMinorLocator) ax.yaxis.set_minor_locator(yMinorLocator) ax.xaxis.grid(True, which='major') # x坐标轴的网格使用主刻度 ax.yaxis.grid(True, which='minor') # y坐标轴的网格使用次刻度 t = np.arange(100) s = np.sin(0.1*np.pi*t)*np.exp(-t*0.01) plt.plot(t, s) plt.show()

这个例子中,我们设置了主刻度线和次刻度线,其中x轴主刻度线是20的整数倍,并且标明了刻度值,而次要刻度线是5的整数倍,省去了刻度值。y轴同理。
这里的核心是定位器和格式生成器两个概念,我们先分别生成X轴的主定位器、主格式生成器,再生成X轴的次定位器(由于X轴次要坐标没有刻度值,所以省去了次要格式生成器)
然后再利用坐标轴ax的xaxis.set_major_locator、xaxis.set_major_formatter和xaxis.set_minor_locator方法,分别对定位器和格式生成器进行赋值。
总结一下:我们就是通过定义每个坐标轴的locator和formatter对象,来完成刻线位置和标签这些属性的设置。
我们再看一个更复杂的例子
我们之前在绘制正余弦曲线图的时候,横坐标的刻度值都是整数1,2,3等等,按道理来说我们更想以 为度量,那么我们仿照上面的例子来做:
import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import MultipleLocator def format_func(value, tick_number): N = int(np.round(2 * value / np.pi)) if N == 0: return '0' elif N == 1: return r"$\pi/2$" elif N == 2: return r"$\pi$" elif N % 2 > 0: return r"${}\pi/2$".format(N) else: return r"${}\pi$".format(N // 2) x = np.linspace(0, 3 * np.pi, 100) plt.plot(x, np.sin(x)) ax = plt.axes() ax.xaxis.set_major_locator(MultipleLocator(np.pi / 2)) ax.xaxis.set_minor_locator(MultipleLocator(np.pi / 4)) ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func)) ax.grid(True) plt.show()

基本上是那么个意思,主刻度是π/2的倍数,次刻度是π/4的刻度,但是用的是小数显示,如果想更直观的直接在图上显示π,怎么做?肯定还是要在格式生成器类上做文章,因为没有内置的合适生成器满足我们的要求,这里我们要用到自定义函数设置不同刻度的标签显示。这里我们读取实际的刻度值,用自定义函数将其转换为相应的π表达式。
日期型主次刻度
一般来说,我们在处理时间序列的数据时,常常需要处理日期型X坐标值,比如一整年的特定数据,如果我们在X轴上将每天的刻度都标识出来,一来太拥挤,二来也没太大必要,一般我们会选取一些大的时间节点作为主刻度线,比如每月1日,或者固定的每周几。我们分别举两个例子来实现这两种情况
首先我们把每月1日设置为主刻度,可以利用MonthLocator作为定位器,DateFormatter作为格式生成器。
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import datetime fig = plt.figure() start = datetime.datetime(2017, 1, 1) end = datetime.datetime(2018, 1, 1) delta = datetime.timedelta(days=1) dates = mpl.dates.drange(start, end, delta) #生成matplotlib指定的日期横轴 y = np.random.rand(len(dates))*0.4+0.5 ax = plt.subplot(111,ylim=(0,1)) ax.plot_date(dates,y,linestyle='-',marker='.') ax.xaxis.set_major_locator(mpl.dates.MonthLocator()) ax.xaxis.set_major_formatter(mpl.dates.DateFormatter('%Y-%m')) ax.grid(True) fig.autofmt_xdate() # 自动旋转日期标记,用以适应横轴空间 plt.show()

再补充强调几个细节,一是我们把日期格式只设置成了年月的形式,表示每个月的第一天。并且设置了自动旋转角度来适应横轴空间。同时我们发现,我们需要mpl.dates.drange方法来专门形成matplotlib绘图时可以识别的日期序列。
最后一个例子中我们以周为观测单元,我们设置主刻度为每周一,次要刻度为每天。
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import datetime from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY start = datetime.datetime(2017, 1, 1) end = datetime.datetime(2017, 4, 1) delta = datetime.timedelta(days=1) dates = mpl.dates.drange(start, end, delta) y = np.random.rand(len(dates))*0.4+0.5 fig,ax = plt.subplots(1) ax.plot_date(dates,y,linestyle='-',marker='.') mondays = WeekdayLocator(MONDAY) alldays = DayLocator() ax.xaxis.set_major_locator(mondays) ax.xaxis.set_minor_locator(alldays) mondayFormatter = DateFormatter('%Y-%m-%d') ax.xaxis.set_major_formatter(mondayFormatter) ax.grid(True) fig.autofmt_xdate() plt.show()

从代码中我们可以看出,这里使用了周定位器WeekdayLocator,和日定位器DayLocator,主刻度格式设置为完整的年月日,其他的原理和之前都是一样的。

浙公网安备 33010602011771号