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等等,按道理来说我们更想以 \pi 为度量,那么我们仿照上面的例子来做:

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,主刻度格式设置为完整的年月日,其他的原理和之前都是一样的。

posted @ 2018-06-30 14:52  purplelavender  阅读(2861)  评论(0)    收藏  举报