Pyplot教程

本文为译文,原文载于此,本文载于此。欢迎转载,但请保留本段文字,尊重作者和译者的权益。спасибо。

matplotlib.pyplot 包含一系列 MATLAB 风格的绘图函数。每个 matplotlib.pyplot 中的函数对当前图像进行一些修改,例如:产生新的图像,在图像中产生新的绘图区域,在绘图区域中画线,给绘图加上标记,等等。matplotlib.pyplot会自动记住当前的图像和绘图区域,因此这些函数会直接作用在当前的图像上,并且绘图函数会指向当前的坐标系(请注意这里的“坐标系”在大多数情况下是指某一图像的坐标系而非严格数学意义中的多坐标系)。

1
2
3
4
import matplotlib.pyplot as plt
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
plt.show()

你可能想知道为什么坐标 x 的范围是 0-3 而坐标 y 的范围是 1-4 。当你提供一个单独的列表(list)或数组(array)给plot()时,matplotlib 会认为这是 y 的一个序列,同时自动生成 x 值。因为 python 区间从 0 开始,所以默认 x 向量和 y 长度相同且从 0 开始。因此 x 的数值范围是 [0,1,2,3]。

plot()是一个多用途的命令,并且可以输入任意数量的变脸。比如,画 x-y 图,你可以这样写命令:

1
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

対任意一对 x,y 变量,有一个可以控制线条的颜色和类型的格式字符串作为可选参数。格式字符串的代码和标志来源于 MATLAB ,你可以同时设置颜色和线条样式。默认的格式字符串是 ‘b-‘ ,也就是蓝色实线。举个栗子,用红圆点画上面的图像,你可以这样输入代码:

1
2
3
4
import matplotlib.pyplot as plt
plt.plot([1,2,3,4], [1,4,9,16], 'ro')
plt.axis([0, 6, 0, 20])
plt.show()

plot()官方文档有完整线条样式和格式字符串列表。在上面的例子中axis()命令传入了 [xmin, xmax, ymin, ymax] 列表,指定了坐标系的视口(ViewPort)。

matplotlib 函数并不仅仅局限于操作列表数据。通常,我们会用到 numpy 数组。事实上,所有的序列在函数内部都会转换成 numpy 数组。下面的例子展示了如何使用数组数据用一行代码画出不同样式的线条:

1
2
3
4
5
6
7
8
9
import numpy as np
import matplotlib.pyplot as plt

# evenly sampled time at 200ms intervals
t = np.arange(0., 5., 0.2)

# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()

控制线条属性

图像的线条有很多可以设置的属性:线条宽度,线条样式,抗锯齿等等。具体可以参考 matplotlib.lines.Line2D。有以下几种方法来设置线条属性:

  • 使用关键字

plt.plot(x, y, linewidth=2.0)

  • 使用 Line2D 对象的 setter 方法。plot 返回一个由 Line2D 对象组成的列表。比如,line1, line2 = plot(x1, y1, x2, y2)。在下面的代码中我们认为我们只设置一条线所以列表长度为1。我们用 line 解压元组以获取列表的第一个元素:
    1
    2
    line, = plt.plot(x, y, '-')
    line.set_antialiased(False) # turn off antialising
  • 使用 setp() 命令。下面的例子用 MATLAB 风格的命令来设置线条的诸多属性。很明显, step 可以传入单一对象,也可以传入对象列表。我们可以用 Python 的关键字变量也可以用 MATLAB 风格的键/值对:
    1
    2
    3
    4
    5
    lines = plt.plot(x1, y1, x2, y2)
    # use keyword args
    plt.setp(lines, color='r', linewidth=2.0)
    # or MATLAB style string value pairs
    plt.setp(lines, 'color', 'r', 'linewidth', 2.0)
    具体的 Line2D 属性参阅这里

为了获得线条属性列表,可以用一条或多条线为参数调用 setp() 函数

1
2
3
4
5
6
7
In [69]: lines = plt.plot([1, 2, 3])

In [70]: plt.setp(lines)
alpha: float
animated: [True | False]
antialiased or aa: [True | False]
...snip

子图和多坐标系

MATLAB 和 pyplot 有当前图像和当前坐标系的概念。所有的绘图命令都应用于当前坐标系。gca() 命令返回当前的坐标系(axes)(一个 axes 例子),gcf()会返回当前图像(figure)(一个 figure 例子)。通常情况下我们不用顾虑这些,因为这些操作都在后台自动执行。下面是一个创建两个子图的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
import matplotlib.pyplot as plt

def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)

t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)

plt.figure(1)
plt.subplot(211)
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')

plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()

figure() 命令在这里是可选的,因为 figure(1) 会默认被创建。同样,如果不指定任何坐标系,subplot(111) 会被默认创建。当 fignum的范围是1到 numrows*numcols时, subplot() 命令会指定 numrowsnumcolsfignum。如果 numrows*numcols<10subplot 命令中的逗号是可有可无的,所以 subplot(211)subplot(2, 1, 1) 效果是一样的。我们可以创建任意数量的子图和坐标系。如果想手动配置坐标系而不是使用默认的矩形网格,可以用 axes()axes([left, bottom, width, height]) 命令来定位坐标系,此时所有坐标值在0到1之间。手动定位坐标系的例子可以看这里,创建多子图的例子看这里

我们可以调用多个 figure() 函数来创建多个图像,调用时注意图像编号。当然,每个图像可以包含多个子图和多个坐标系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt
plt.figure(1) # the first figure
plt.subplot(211) # the first subplot in the first figure
plt.plot([1, 2, 3])
plt.subplot(212) # the second subplot in the first figure
plt.plot([4, 5, 6])


plt.figure(2) # a second figure
plt.plot([4, 5, 6]) # creates a subplot(111) by default

plt.figure(1) # figure 1 current; subplot(212) still current
plt.subplot(211) # make subplot(211) in figure1 current
plt.title('Easy as 1, 2, 3') # subplot 211 title
plt.show()

我们可以用 clf() 清空当前图像,用 cla() 清空当前坐标系。我们可能对状态(尤其是当前图像和当前坐标系)在后台保留感到烦躁,不过这只是一种有关对象指向 API 的弱状态封装(thin stateful wrapper),可以用其他的命令代替(参阅Artist tutorial)。

如果要生成许多图像,我们还需要留意:图像占用的内存不会完全释放直到图像被 close() 命令明确无误地关闭。删除所有指向图像的引用,或者用 Windows 的程序管理器结束正在屏幕的图像进程是不够的,除非调用 close(),否则 pyplot 一直在后台运行。

图像中加入文本

text() 命令可以在任意位置加入文本,xlabel() , ylabel()title() 会在指定位置加入文本(参阅 Text introduction查看具体实例)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)

mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)

# the histogram of the data
n, bins, patches = plt.hist(x, 50, normed=1, facecolor='g', alpha=0.75)


plt.xlabel('Smarts')
plt.ylabel('Probability')
plt.title('Histogram of IQ')
plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
plt.axis([40, 160, 0, 0.03])
plt.grid(True)
plt.show()

所有的 text() 命令都会返回一个 matplotlib.text.Text 对象。正如上面的例子,我们可以传递关键字参数或者用 step() 定制文本属性:

1
t = plt.xlabel('my data', fontsize=14, color='red')

更多的有关文字属性的内容参阅Text properties and layout

在文本中用数学表达式

matplotlib 文本接受任何形式的 TeX 方程式。举例来说,如果要以为标题,那么我们的代码可以这样写:

1
plt.title(r'$\sigma_i=15$')

在标题字符串之前的 r 很重要——它表示这是一个原始字符串,不要把反斜杠当作 Python 转移字符。 matplotlib 有一个内建的 TeX 解析器和渲染引擎,并且输出自己的数学字体——更多细节参阅Writing mathematical expressions。因此你可以跨平台使用数学表达式而不需要预先安装 TeX 。如果已经安装了 LaTeX 和 dvipng ,可以用 LaTeX 来格式化文本,或者直接输出合并的图像或保存脚本——可以参阅Text rendering With LaTeX

注释文本

text() 命令基本的用法是可以在坐标系任意位置生成文本,如上所述。更普遍的用法是在图像中作注释标记。annotate() 函数提供了一个更简单的生成注释的方法。生成一个注释需要传入两个参数:注释所指向的位置参数 xy 和注释所生成的位置参数 xytext。两个参数都是 (x,y) 元组类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt

ax = plt.subplot(111)

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = plt.plot(t, s, lw=2)

plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),
)

plt.ylim(-2,2)
plt.show()

在个基础的例子中,xy (arrow tip) 和 xytext 位置都是数据坐标。有许多其他的坐标表示方法可以选择。详情可以参阅Basic annotation 和 [Advanced Annotation](Advanced Annotation)。更多的例子可以参阅这里

对数和其他非线性坐标系

matplotlib.pyplot 除了支持线性尺度外,也支持对数尺度和回归尺度(logit scale),通常在数据跨越许多数量级时要用到。改变坐标系的尺度很容易:
plt.xscale('log')
一个用相同数据不同 y 轴尺度的四个子图对比的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import numpy as np
import matplotlib.pyplot as plt

from matplotlib.ticker import NullFormatter # useful for `logit` scale

# Fixing random state for reproducibility
np.random.seed(19680801)

# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))

# plot with various axes scales
plt.figure(1)

# linear
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)


# log
plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)


# symmetric log
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.01)
plt.title('symlog')
plt.grid(True)

# logit
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
# Format the minor tick labels of the y-axis into empty strings with
# `NullFormatter`, to avoid cumbering the axis with too many labels.
plt.gca().yaxis.set_minor_formatter(NullFormatter())
# Adjust the subplot layout, because the logit one may take more space
# than usual, due to y-tick labels like "1 - 10^{-3}"
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
wspace=0.35)

plt.show()

我们也可以加入自己的坐标尺度,详情可参阅Developer’s guide for creating scales and transformations