matplotlib API
包含有三层:
backend_bases.FigureCanvas
: 图表的绘制领域backend_bases.Renderer
: 知道如何在FigureCanvas
上如何绘图artist.Artist
: 知道如何使用Renderer
在FigureCanvas
上绘图
FigureCanvas
和 Renderer
需要处理底层的绘图操作,例如使用wxPython在界面上绘图,
或者使用PostScript绘制PDF。Artist
则处理所有的高层结构,例如处理图表、文字和曲线等的绘制和布局。
通常只和 Artist
打交道,而不需要关心底层的绘制细节。
Artists
分为简单类型和容器类型两种。简单类型的 Artists
为标准的绘图元件,
例如 Line2D
、 Rectangle
、Text
、AxesImage
等等。
而容器类型则可以包含许多简单类型的 Artists
,使它们组织成一个整体,
例如Axis、 Axes、Figure等。
使用 Artists
创建图表的流程
直接使用 Artists
创建图表的标准流程如下:
- 创建
Figure
对象 - 用
Figure
对象创建一个或者多个Axes
或者Subplot
对象 - 调用
Axies
等对象的方法创建各种简单类型的Artists
下面首先调用 pyplot.figure
辅助函数创建 Figure
对象,
然后调用 Figure
对象的 add_axes
方法在其中创建一个 Axes
对象,
add_axes
的参数是一个形如 [left, bottom, width, height]
的列表,
这些数值分别指定所创建的 Axes
对象相对于fig的位置和大小,取值范围都在0到1之间:
%matplotlib inline
import matplotlib.pyplot as plt
plt.ioff()
plt.ion()
<contextlib.ExitStack at 0x7f4924a76c00>
fig = plt.figure()
ax = fig.add_axes([0.15, 0.1, 0.7, 0.3])
然后我们调用 ax
的 plot()
方法绘图,创建一条曲线,并且返回此曲线对象(Line2D)。
fig = plt.figure()
ax = fig.add_axes([0.15, 0.1, 0.7, 0.3])
line, = ax.plot([1,2,3],[1,2,1])
ax.lines
<Axes.ArtistList of 1 lines>
fig = plt.figure()
ax = fig.add_axes([0.15, 0.1, 0.7, 0.3])
line, = ax.plot([1,2,3],[1,2,1])
ax.lines
line
<matplotlib.lines.Line2D at 0x7f4924bf6870>
ax.lines
是一个为包含 ax
的所有曲线的列表,后续的 ax.plot
调用会往此列表中添加新的曲线。
如果想删除某条曲线的话,直接从此列表中删除即可。
Axes
对象还包括许多其它的 Artists
对象,
例如可以通过调用 set_xlabel
设置其X轴上的标题:
fig = plt.figure()
ax = fig.add_axes([0.15, 0.1, 0.7, 0.3])
line, = ax.plot([1,2,3],[1,2,1])
ax.set_xlabel("time")
Text(0.5, 0, 'time')
如果我们查看 set_xlabel
的源代码的话,会发现它是通过调用下面的语句实现的:
self.xaxis.set_label_text(xlabel)
如果一直跟踪下去,会发现 Axes
的 xaxis
属性是一个 XAxis
对象:
ax.xaxis
<matplotlib.axis.XAxis at 0x7f49249334d0>
XAxis
的 label
属性是一个 Text
对象:
ax.xaxis.label
Text(0.5, 24.0, 'time')
而 Text
对象的 _text
属性为我们设置的值:
ax.xaxis.label._text
'time'
这些对象都是 Artists
,因此也可以调用它们的属性获取函数来获得相应的属性:
ax.xaxis.label.get_text()
'time'
图表中的每个元素都用一个 matplotlib
的 Artist
对象表示,
而每个 Artist
对象都有一大堆属性控制其显示效果。
例如 Figure
对象和 Axes
对象都有 patch
属性作为其背景,它的值是一个 Rectangle
对象。
通过设置此它的一些属性可以修改 Figrue
图表的背景颜色或者透明度等属性,
下面的例子将图表的背景颜色设置为绿色:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0.15, 0.1, 0.7, 0.3])
fig.show()
fig.patch.set_color("g")
fig.canvas.draw()
fig.show()
plt.draw()
<Figure size 640x480 with 0 Axes>
patch
的 color
属性通过 set_color
函数进行设置,属性修改之后并不会立即反映到图表的显示上,
还需要调用 fig.canvas.draw()
函数才能够更新显示。
下面是 Artist
对象都具有的一些属性:
alpha
: 透明度,值在0到1之间,0为完全透明,1为完全不透明animated
: 布尔值,在绘制动画效果时使用axes
: 此Artist
对象所在的Axes
对象,可能为None
clip_box
: 对象的裁剪框clip_on
: 是否裁剪clip_path
: 裁剪的路径contains
: 判断指定点是否在对象上的函数figure
: 所在的Figure
对象,可能为None
label
: 文本标签picker
: 控制Artist
对象选取transform
: 控制偏移旋转visible
: 是否可见zorder
: 控制绘图顺序
Artist对象的所有属性都通过相应的 get_*
和 set_*
函数进行读写,
例如下面的语句将 alpha
属性设置为当前值的一半:
fig.set_alpha(0.5*fig.get_alpha())
如果想用一条语句设置多个属性的话,可以使用 set
函数:
fig.set(alpha=0.5, zorder=2)
[None, None]
使用前面介绍的 matplotlib.pyplot.getp
函数可以方便地输出 Artist
对象的所有属性名和值。
plt.getp(fig.patch)
agg_filter = None alpha = None angle = 0.0 animated = False antialiased or aa = False bbox = Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0) capstyle = butt center = [0.5 0.5] children = [] clip_box = None clip_on = True clip_path = None corners = [[0. 0.] [1. 0.] [1. 1.] [0. 1.]] data_transform = BboxTransformTo( TransformedBbox( Bbox... edgecolor or ec = (0.0, 0.5, 0.0, 1.0) extents = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0) facecolor or fc = (0.0, 0.5, 0.0, 1.0) figure = Figure(640x480) fill = True gid = None hatch = None hatch_linewidth = 1.0 height = 1 in_layout = False joinstyle = miter label = linestyle or ls = solid linewidth or lw = 0.0 mouseover = False patch_transform = CompositeGenericTransform( BboxTransformTo( ... path = Path(array([[0., 0.], [1., 0.], [1.,... path_effects = [] picker = None rasterized = False sketch_params = None snap = None tightbbox = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0) transform = CompositeGenericTransform( CompositeGenericTra... transformed_clip_path_and_affine = (None, None) url = None verts = [[ 0. 0.] [640. 0.] [640. 480.] [ 0. 480.... visible = True width = 1 window_extent = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0) x = 0 xy = (0, 0) y = 0 zorder = 1
现在知道如何观察和修改已知的某个 Artist
对象的属性,接下来要解决如何找到指定的 Artist
对象。
前面介绍过 Artist
对象有容器类型和简单类型两种,接下来看看容器类型的内容。
最大的 Artist
容器是 matplotlib.figure.Figure
,它包括组成图表的所有元素。
图表的背景是一个 Rectangle
对象,用 Figure.patch
属性表示。
当通过调用 add_subplot
或者 add_axes
方法往图表中添加轴(子图时),
这些子图都将添加到 Figure.axes
属性中,同时这两个方法也返回添加进 axes
属性的对象,
注意返回值的类型有所不同,实际上 AxesSubplot
是 Axes
的子类。
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3])
ax1
<Axes: >
ax2
<Axes: >
fig.axes
[<Axes: >, <Axes: >]
为了支持 pylab
中的 gca ()
等函数,Figure
对象内部保存有当前轴的信息,
因此不建议直接对 Figure.axes
属性进行列表操作,而应该使用 add_subplot
,
add_axes
, delaxes
等方法进行添加和删除操作。
但是使用 for
循环对 axes
中的每个元素进行操作是没有问题的,
下面的语句打开所有子图的栅格。
for ax in fig.axes: ax.grid(True)
Figure
对象可以拥有自己的文字、线条以及图像等简单类型的 Artist
。
缺省的坐标系统为像素点,但是可以通过设置 Artist
对象的 transform
属性修改坐标系的转换方式。
最常用的 Figure
对象的坐标系是以左下角为坐标原点 (0,0)
,右上角为坐标 (1,1)
。
下面的程序创建并添加两条直线到 fig
中:
from matplotlib.lines import Line2D
fig = plt.figure()
line1 = Line2D([0,1],[0,1], transform=fig.transFigure, figure=fig, color="r")
line2 = Line2D([0,1],[1,0], transform=fig.transFigure, figure=fig, color="g")
fig.lines.extend([line1, line2])
fig.show()
注意为了让所创建的 Line2D
对象使用 fig
的坐标,
将 fig.TransFigure
赋给 Line2D
对象的 transform
属性;
为了让 Line2D
对象知道它是在 fig
对象中,还设置其 figure
属性为 fig
;
最后还需要将创建的两个 Line2D
对象添加到 fig.lines
属性中去。
Figure
对象有如下属性包含其它的 Artist
对象:
axes
:Axes
对象列表patch
: 作为背景的Rectangle
对象images
:FigureImage
对象列表,用来显示图片legends
:Legend
对象列表lines
:Line2D
对象列表patches
:patch
对象列表texts
:Text
对象列表,用来显示文字
Axes
容器
Axes
容器是整个 matplotlib
库的核心,它包含了组成图表的众多 Artist
对象,
并且有许多方法函数帮助我们创建、修改这些对象。和 Figure
一样,它有一个 patch
属性作为背景,
当它是笛卡尔坐标时,patch
属性是一个 Rectangle
对象,而当它是极坐标时,patch
属性则是 Circle
对象。
例如下面的语句设置 Axes
对象的背景颜色为绿色:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.patch.set_facecolor("green")
当调用 Axes
的绘图方法(例如 plot
),它将创建一组 Line2D
对象,
并将所有的关键字参数传递给这些 Line2D
对象,并将它们添加进 Axes.lines
属性中,
最后返回所创建的 Line2D
对象列表:
import numpy as np
x, y = np.random.rand(2, 100)
line, = ax.plot(x, y, "-", color="blue", linewidth=2)
line
<matplotlib.lines.Line2D at 0x7f492498ab70>
ax.lines
<Axes.ArtistList of 1 lines>
注意 plot
返回的是一个 Line2D
对象的列表,因为可以传递多组X,Y轴的数据,一次绘制多条曲线。
与 plot
方法类似,绘制直方图的方法 bar
和绘制柱状统计图的方法 hist
将创建一个 Patch
对象的列表,
每个元素实际上都是 Patch
的子类 Rectangle
,
并且将所创建的 Patch
对象都添加进 Axes.patches
属性中:
ax = fig.add_subplot(111)
n, bins, rects = ax.hist(np.random.randn(1000), 50, facecolor="blue")
rects
<BarContainer object of 50 artists>
rects[0]
<matplotlib.patches.Rectangle at 0x7f4924745850>
ax.patches[0]
<matplotlib.patches.Rectangle at 0x7f4924745850>
一般不会直接对 Axes.lines
或者 Axes.patches
属性进行操作,
而是调用 add_line
或者 add_patch
等方法,
这些方法帮助我们完成许多属性设置工作:
import matplotlib
fig = plt.figure()
ax = fig.add_subplot(111)
rect = matplotlib.patches.Rectangle((1,1), width=5, height=12)
rect
的 axes
属性为空
# print(rect.get_axes())
rect
的 transform
属性为缺省值
rect.get_transform()
<matplotlib.transforms.CompositeGenericTransform at 0x7f49246478c0>
将 rect
添加进 ax
ax.add_patch(rect)
<matplotlib.patches.Rectangle at 0x7f492463f9b0>
于是 rect
的 axes
属性就是 ax
# rect.get_axes()
rect
的 transform
属性和 ax
的 transData
相同
rect.get_transform()
<matplotlib.transforms.CompositeGenericTransform at 0x7f49249e39b0>
ax.transData
<matplotlib.transforms.CompositeGenericTransform at 0x7f492463fe00>
ax
的X轴范围为0到1,无法显示完整的 rect
ax.get_xlim()
(np.float64(0.0), np.float64(1.0))
数据的范围和 rect
的大小一致
# ax.dataLim._get_bounds()
自动调整坐标轴范围
ax.autoscale_view()
于是 X
轴可以完整显示 rect
ax.get_xlim()
(np.float64(0.75), np.float64(6.25))
plt.show()
通过上面的例子我们可以看出,add_patch
方法帮助我们设置了 rect
的 axes
和 transform
属性。
下面详细列出 Axes
包含各种 Artist
对象的属性:
artists
:Artist
对象列表patch
: 作为Axes
背景的Patch
对象,可以是Rectangle
或者Circle
collections
:Collection
对象列表
images
:AxesImage
对象列表legends
:Legend
对象列表lines
:Line2D
对象列表patches
:Patch
对象列表texts
:Text
对象列表xaxis
:XAxis
对象yaxis
:YAxis
对象
下面列出 Axes
的创建 Artist
对象的方法:
Axes
的方法所创建的对象 添加进的列表
annotate Annotate texts
bars Rectangle patches
errorbar Line2D, Rectangle lines,patches
fill Polygon patches
hist Rectangle patches
imshow AxesImage images
legend Legend legends
plot Line2D lines
scatter PolygonCollection Collections
text Text texts
下面以绘制散列图(scatter)为例,验证一下:
返回值为 CircleCollection
对象
fig = plt.figure()
ax = fig.add_subplot(111)
t = ax.scatter(np.random.rand(20), np.random.rand(20))
t
<matplotlib.collections.PathCollection at 0x7f4924bc6cf0>
返回的对象已经添加进了 collections
列表中
ax.collections
<Axes.ArtistList of 1 collections>
fig.show()
获得 Collection
的点数
t.get_sizes()
array([36.])
Axis
容器包括坐标轴上的刻度线、刻度文本、坐标网格以及坐标轴标题等内容。
刻度包括主刻度和副刻度,分别通过 Axis.get_major_ticks
和 Axis.get_minor_ticks
方法获得。
每个刻度线都是一个 XTick
或者 YTick
对象,它包括实际的刻度线和刻度文本。
为了方便访问刻度线和文本, Axis
对象提供了 get_ticklabels
和 get_ticklines
方法分别直接获得刻度线和刻度文本:
import numpy as np
import matplotlib.pyplot as plt
plt.plot([1,2,3],[4,5,6])
[<matplotlib.lines.Line2D at 0x7f48f7c314c0>]
plt.show()
axis = plt.gca().xaxis
获得刻度的位置列表
axis.get_ticklocs()
array([0. , 0.2, 0.4, 0.6, 0.8, 1. ])
获得刻度标签列表
axis.get_ticklabels()
[Text(0.0, 0, '0.0'), Text(0.2, 0, '0.2'), Text(0.4, 0, '0.4'), Text(0.6000000000000001, 0, '0.6'), Text(0.8, 0, '0.8'), Text(1.0, 0, '1.0')]
获得刻度的文本字符串
[x.get_text() for x in axis.get_ticklabels()]
['0.0', '0.2', '0.4', '0.6', '0.8', '1.0']
获得主刻度线列表,图的上下刻度线共10条
axis.get_ticklines()
<a list of 12 Line2D ticklines objects>
获得副刻度线列表
axis.get_ticklines(minor=True)
<a list of 0 Line2D ticklines objects>
获得刻度线或者刻度标签之后,可以设置其各种属性,下面设置刻度线为绿色粗线,文本为红色并且 旋转45度:
for label in axis.get_ticklabels():
label.set_color("red")
label.set_rotation(45)
label.set_fontsize(16)
for line in axis.get_ticklines():
line.set_color("green")
line.set_markersize(25)
line.set_markeredgewidth(3)
plt.show()
手工配置X轴的刻度线和刻度文本的样式。
上面的例子中,获得的副刻度线列表为空,这是因为用于计算副刻度的对象缺省为 NullLocator
,
它不产生任何刻度线;而计算主刻度的对象为 AutoLocator
,它会根据当前的缩放等配置自动计算刻度的位置:
计算副刻度的对象
axis.get_minor_locator()
<matplotlib.ticker.NullLocator at 0x7f48f7c90770>
计算主刻度的对象
axis.get_major_locator()
<matplotlib.ticker.AutoLocator at 0x7f48f7c5f3e0>
可以使用程序为 Axis
对象设置不同的 Locator
对象,用来手工设置刻度的位置;
设置 Formatter
对象用来控制刻度文本的显示。
下面的程序设置X轴的主刻度为 pi/4
,副刻度为 pi/20
,并且主刻度上的文本以 pi
为单位:
# -*- coding: utf-8 -*-
import matplotlib.pyplot as pl
from matplotlib.ticker import MultipleLocator, FuncFormatter
import numpy as np
x = np.arange(0, 4*np.pi, 0.01)
y = np.sin(x)
pl.figure(figsize=(8,4))
pl.plot(x, y)
ax = pl.gca()
def pi_formatter(x, pos):
m = np.round(x / (np.pi/4))
n = 4
if m%2==0: m, n = m/2, n/2
if m%2==0: m, n = m/2, n/2
if m == 0:
return "0"
if m == 1 and n == 1:
return "$\pi$"
if n == 1:
return r"$%d \pi$" % m
if m == 1:
return r"$\frac{\pi}{%d}$" % n
return r"$\frac{%d \pi}{%d}$" % (m,n)
# 设置两个坐标轴的范围
pl.ylim(-1.5,1.5)
pl.xlim(0, np.max(x))
# 设置图的底边距
pl.subplots_adjust(bottom = 0.15)
pl.grid() #开启网格
# 主刻度为pi/4
ax.xaxis.set_major_locator( MultipleLocator(np.pi/4) )
# 主刻度文本用pi_formatter函数计算
ax.xaxis.set_major_formatter( FuncFormatter( pi_formatter ) )
# 副刻度为pi/20
ax.xaxis.set_minor_locator( MultipleLocator(np.pi/20) )
# 设置刻度文本的大小
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(16)
pl.show()
<>:16: SyntaxWarning: invalid escape sequence '\p' <>:16: SyntaxWarning: invalid escape sequence '\p' /tmp/ipykernel_2607/1185872389.py:16: SyntaxWarning: invalid escape sequence '\p' return "$\pi$"
关于刻度的定位和文本格式的东西都在 matplotlib.ticker
中定义,程序中使用到如下两个类:
MultipleLocator
: 以指定值的整数倍为刻度放置刻度线FuncFormatter
: 使用指定的函数计算刻度文本,会传递给所指定的函数两个参数: 刻度值和刻度序号,程序中通过比较笨的办法计算出刻度值所对应的刻度文本
此外还有很多预定义的 Locator
和 Formatter
类,详细内容请参考相应的API文档。