这篇笔记主要来自《利用python进行数据分析》的第八章第一节,对matplotlib的基本使用介绍的特别清晰。关于matplotlib的基本概念(figure, subplot, axes)的介绍还可以看这里。
- Figure和Subplot
- 颜色、标记和线型
- 刻度、标签和图例
- 注解
- 保存到文件
pyplot模块的API文档,这里。
1. Figure和Subplot
plt.figure()
matplotlib的图像都位于Figure对象中,可以使用plt.figure()创建一个新的figure:
>>> fig = plt.figure()
在ipython中,执行后会弹出一个空窗口:
该窗口的大小可以通过在plt.figure()中传递参数figsize=(width,height)来指定。
可以看到窗口的标题为“Figure 1”,要想设定Figure编号为3,则可执行:
>>> fig = plt.figure(3)
可通过plt.gcf()来获得最后一次创建的Figure对象的引用。
Figure.add_subplot()
现在虽然有了一个Figure对象fig,但是还不能在其上画图。必须使用add_subplot()在fig上创建一个或多个subplot才行:
# 等价于fig.add_subplot(221)
# 意思是将fig划分为2x2,指定第一个为画图位置
# 这里的subplot从1开始编号
# 其实就是在fig中固定位置创建了一个Axes对象。
>>> ax1 = fig.add_subplot(2, 2, 1)
执行后会得到:
若接着执行:
# 执行后fig中就有了三个Axes对象
>>> ax2 = fig.add_subplot(2, 2, 2)
>>> ax3 = fig.add_subplot(2, 2, 3)
得到:
fig.add_subplot(2, 2, 1)并不意味着对于该fig,固定划分了2x2个subplot,这只是指出一个固定的位置来确定要创建的Axes对象。比如还可以接着执行下面的代码再创建个小的Axes对象:
>>> ax4 = fig.add_subplot(339)
结果为:
可以看到Figure.add_subplot()的功能是将Axes对象创建于规则的网格(grid)上的,如果想要在Figure中的任意位置创建任意大小的Axes对象可使用Figure.add_axes()。
plot()
如果这时发出一条绘图命令,matplotlib会在最后一个用过的Axes对象上(如果没有则自动创建一个)进行绘制。
# 绘制黑色'k'虚线'--'
>>> plt.plot(np.random.randn(50).cumsum(), 'k--')
结果为:
如果想要在其他的axes中绘图,则可以调用之前的Axes对象中的绘图方法:
# hist方法有三个返回值,不想显示就将其赋给以变量
>>> _ = ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)
>>> ax2.scatter(np.arange(30), np.arange(30) + 3*np.random.randn(30))
结果如下:
plt.subplots()
有一条语句可以快速的建立Figure和一组subplots,也就是plt.subplots()。它可以创建一个新的Figure,并返回一个含有已创建的Axes对象的numpy数组:
# 创建一个Figure对象fig2,并在其上绘制2x3个subplots
# fig2为新创建的Figure对象
# axess为一2x3数组,其中元素为Axes对象。
>>> fig2, axess = plt.subplots(2,3)
执行上述语句,会弹出一个新的Figure窗口:
之前使用plt.figure()创建Figure对象时,可以传递figsize参数来指定Figure对象的大小。在plt.subplots()中也可以传递该参数,见文档中对**fig_kw参数的描述。
但是如果在创建完Figure对象后想改变figure size该怎么办呢?可调用Figure类的方法set_figheight()和set_figweight()来分别设置Figure对象的长和宽。可见Figure类的文档说明。
可以看axess的内容:
>>> axess
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x1178f9240>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117920438>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117947710>],
[<matplotlib.axes._subplots.AxesSubplot object at 0x117b28438>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117b50748>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117b7a9e8>]], dtype=object)
可以通过指定参数sharex=True使各列Axes对象使用同一个x轴(这样,调整某列的xlim将会影响对应的axes),而设置sharey=True使各行Axes对象使用同一个y轴(这样,调整某行的ylim将会影响对应的axes)。
>>> plt.close(2)
>>> fig2, axess = plt.subplots(2,3, sharex=Ture, sharey=Ture)
结果为:
plt.subplots_adjust()
默认情况下(matplotlib有默认的配置文件),matplotlib会在subplot外围留下一定的边距,并在subplot之间留下一定的间距。利用plt.subplots_adjust()或Figure.subplots_adjust()可以修改这些间距。两个函数的参数都是一样的(见文档),但前者是在当前Figure中操作,而后者是在指定的Figure中操作的。
# 操作的是fig2
>>> plt.subplots_adjust(wspace=0, hspace=0)
结果为:
可以看到matplotlib中的画图是很自由的,并不会检查坐标的重叠。
# 操作的是上面的Figure 1
>>> fig.subplots_adjust(wspace=0, hspace=0)
结果为:
当画完图之后,要记得使用plt.close()关掉对应的Figure,释放内存。有多种关闭方式,可看文档。
# 关闭所有Figure。
>>> plt.close('all')
总结一下,Figure对象就相当于一个巨大的画板,可以在画板上放置多个画纸,各个画纸也就是Axes对象,画图就是画在Axes对象上的。各个Axes对象都有自己的坐标轴,可以分别控制。文档中对这几个概念的描述在这里。其中有张图可以很好的理解Figure与Axes的各部分。
下面都以plot画线来介绍。
2. 颜色、标记和线型
上一节中调用plot()画线时(plt.plot()或axes.plot()),传递了一个参数'k--',指出画的线为黑色虚线,这是对线的颜色和形式一种便捷的设置,这种简便写法中还可以加入一个标记的形式控制(也就是(x,y)点的形式)。
注意,plot()的字典参数**kwargs用于指定要画的Line2D线对象的各种属性。
控制格式为,注意顺序不能变:
fmt = '[color][marker][line]'
如:
>>> fig, ax = plt.subplots(1,1)
# 相当于,这种显示控制:
# ax.plot(np.random.randn(20), linestyle='-', color='r', marker='o')
>>> ax.plot(np.random.randn(20), 'ro-')
得到下图:
fmt中的三项都是可选的,各项可配置的值可看这里的Notes部分。
但是要注意,fmt这种简便设置方式与显示参数的设置还是不同的。
# 只设置颜色和marker形式,则只画点,不画线
>>> ax.plot(np.random.randn(20), 'bo‘)
# 虽只设置颜色和marker形式,依然画出了线
>>> ax.plot(np.random.randn(20), color='g', marker='o')
另外使用color参数设置颜色时,或fmt中只设置颜色时,可以使用更多的颜色设置方式,具体可看这里。
在线型图中,非实际的数据点默认是按照线性方式插值的。可以通过plot()的参数drawstyle来修改插值方式:
# 清除axes上的绘制
>>> ax.clear()
>>> ax.plot(np.random.randn(20), 'ko-', drawstyle='steps-post')
3. 刻度、标签和图例
这一节讲解的就是对坐标轴的控制,包括轴标签、轴刻度的标签、轴刻度、轴刻度范围、图例。下面都是显示指定要控制的Axes对象,使用Axes类中的方法。
先绘制一个简单的图像,并在其上举例。
>>> fig, ax = plt.subplots(1,1)
>>> np.random.seed(985)
>>> ax.plot(np.random.randn(30), 'ro-')
- 设置该Axes对象的标题为'random plot',并设置轴标签为'x','y',分别使用Axes.set_title(),Axes.set_xlabel(),Axes.set_ylabel()。
# 设置标题
>>> ax.set_title('random plot')
# 设置xy轴标签
>>> ax.set_xlabel('x')
>>> ax.set_ylabel('y')
对应的,可以通过Axes.get_title(),Axes.get_xlabel(),Axes.get_ylabel()来得到对应的值。
>>> ax.get_title()
'random plot'
>>> ax.get_xlabel()
'x'
>>> ax.get_ylabel()
'y'
-
轴的范围
由于没有指定要画线的x值,所以默认为range(0,30),而看图上的x轴坐标范围,其在左右明显要小于0并大于29,我们可以使用Axes.get_xlim()和Axes.get_ylim()来分别得到x轴和y轴的坐标范围。
>>> ax.get_xlim()
(-1.4500000000000002, 30.449999999999999)
>>> ax.get_ylim()
(-1.271508964835969, 2.6919009419421762)
可见画图时默认增加了些冗余,当然我们可以对其更改。
可以使用Axes.set_xlim()和Axes.set_ylim()来分别设置轴的范围:
>>> ax.set_xlim(-10, 40)
同样也可以缩小:
>>> ax.set_xlim(10, 20)
两次缩放了轴的范围,与原始图对比下可发现,在缩放中,x坐标(只操作了x轴)刻度也在自动变化。这是因为我们没有显示设置x轴的坐标刻度值,所以matplotlib自动设置的。如果显示设置了,那么会一直使用我们设置好的,不论如何缩放轴范围。
-
轴刻度的控制
刻度,也就是tick,可通过Axes.get_xticks()和Axes.get_yticks()来看看tick当前的轴刻度是什么:
# 接着上面执行代码。
>>> ax.get_xticks()
array([ 10., 12., 14., 16., 18., 20.])
>>> ax.get_yticks()
array([-1.5, -1. , -0.5, 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. ])
可见也就是轴上的坐标值,同样的可以使用Axes.set_xticks()和Axes.set_yticks()来更改:
# 会返回一个列表,包含11个创建的XTick对象。
>>> ticks = ax.set_xticks(range(0,33,3))
此时x轴的坐标刻度也就固定了,再放大x轴范围也不会改变坐标刻度,而只是缩放了图像。如:
# 设置为最初的x轴范围
>>> ax.set_xlim(-1.4500000000000002, 30.449999999999999)
-
轴刻度标签的控制
目前在x轴上有11个刻度,都是数字表示的,还可以使用Axes.set_xticklabels()将其他任何值用做标签:
# 改为其他数值
>>> labels = ax.set_xticklabels(range(10,43,3))
# 改为字符串
>>> labels = ax.set_xticklabels([i for i in 'abcdefghijk'])
# 指定的字符串还可以为LateX数学表达式。
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,12)])
此外,可以对函数Axes.set_xticklabels()的**kwargs传入其他参数来控制Text对象的属性。如字体角度rotation和字体大小fontsize(其他属性可看文档):
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,12)], rotation = 30, fontsize='large')
此外,因为我们使用了Axes.set_xticks()设置了11个坐标刻度,所以在Axes.set_xticklabels()中需要传递长度11的列表指定这11刻度的label。如果列表长度小于11的话,如5,那么,只改变前5个刻度的label,后6个刻度的label为空,也就啥也没有。如果列表中的元素多于11个,那么只用前11个。
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,5)], rotation = 30, fontsize='large')
-
图例
可以使用Axes.legend()在Axes中添加图例,对绘制进行说明。
对于上面画的红线,现在加入一个图例:
# 返回Legend对象
>>> l1 = ax.legend(['red'])
这种方法是不推荐的,因为当Axes中绘制了多条线后,需要在legend()中传入对应着画线顺序的label,而该顺序很容易搞混的。
常用的一种加入图例方法是在画图是传入label参数,然后在使用legend():
>>> np.random.seed(211)
>>> ax.plot(np.random.randn(30), 'ko--', label='black')
>>> ax.legend()
还用一种更为全面的控制方式,即指出需要图例的那条线和其对应的字符串,清除掉这两条线,重画下:
>>> ax.clear()
>>> np.random.seed(233)
# 注意,plot()返回的是包含Line2D对象的列表,如果不加',‘,那么l1是列表。
>>> l1, = ax.plot(np.random.randn(30), 'ko-')
>>> np.random.seed(666)
>>> l2, = ax.plot(np.random.randn(30), 'ro--')
# 将Line2D对象放入第一个列表中,第二个列表中的字符串顺序要一致。
>>> ax.legend([l1,l2], ['black', 'red'])
关于图例的控制(位置、字体等),可看文档Axes.legend()。
4. 注解
- 文本的绘制
在Axes对象中绘制文本可使用函数Axes.text()。该函数的使用很简单,传入坐标和文本,就会将文本绘制在对应的坐标上。可以向函数传入Text属性来控制文本的显示。
>>> fig, ax = plt.subplots(1,1)
>>> x = np.linspace(-np.pi,np.pi,100, endpoint=True)
>>> y = np.sin(x)
>>> ax.plot(x,y, 'k-')
>>> ax.set_xticks(np.arange(-np.pi,np.pi+1,np.pi/4))
# 这些tick labels本身就是Text对象。该函数会返回包含对应的9个Text对象的列表。
>>> ax.set_xticklabels([r'$-\pi$', r'$-\frac{3}{4}\pi$', r'$-\frac{1}{2}\pi$', r'$-\frac{1}{4}\pi$', r'0', r'$\frac{1}{4}\pi$', r'$\frac{1}{2}\pi$',r'$\frac{3}{4}\pi$', r'$\pi$'])
# 在原点绘制文本,后面的参数设置文本的显示属性。
# ha和va设置的是文本相对(0,0)的位置,‘center'是以点为中心。
>>> ax.text(0,0, r'hello world!', color='r', ha='center', va='center',rotation=30, size='medium', style = 'italic' )
- 注释
在图上绘制注释使用的是Axes.annotate)()。关于该函数的使用例子可看这里。
# 在坐标(-1,0.5)处写Text对坐标(0,0)进行注释。
>>> ax.annotate(s=r'origin', xy=(0,0), xytext=(-1,0.5), arrowprops={'arrowstyle':'->'})
5. 保存到文件
使用plt.save()可将当前Figure中的内容保存到文件(也可以是file-like object)中。
文件的类型可以通过给定的文件名扩展名推断出来,或者指定函数参数format。