import numpy as np
import matplotlib.pyplot as plt
nbSamples = 128
x = np.linspace(-np.pi, np.pi, nbSamples)
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x, y1, color='g', linewidth=4, linestyle='--', label=r'$y = sin(x)$')
plt.plot(x, y2, '*', markersize=8, markerfacecolor='r',
markeredgecolor='k', label=r'$y = cos(x)$')
plt.legend(loc='best')
plt.show()
首先,在第09和第11行为曲线添加了标签属性,然后在第16行,通过设置plt.legend(loc=‘best’)使图例能够在画布的“最佳”位置显示,这里的“最佳”是由系统自动判别的,通常哪里留白较多,系统就将图例放到哪里,loc参数是location(位置)的简写,表示图例所在位置,通常默认为最佳位置。
当然,我们也可以自行指定图例位置,可供选择的参数有:
Matplotlib在绘图的过程中,可以为各个轴的标题(Label)、图像的标题(Title)、图形的图例(Legend)等元素添加LaTeX风格的公式。添加公式并不复杂,只要在LaTeX公式的文本前后各增加一个$符号,Matplotlib就可以自动进行解析。如代码第9行和第11行所示,公式前面通常添加字母r,它是raw(原始的)的首字母,表示后面的字符串(即LaTeX公式)以原始字符形式存在,不需要进行转义解析。例如,字符串r’\n’就表示两个字符,一个是“\”另一个是“n”。如果去掉字符串前面的标识r,’\n’就被解析为一个字符,即换行符。
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 1) # 构造X轴坐标向量
y = 2 * x # 构造Y轴坐标向量
for a, b in zip(x, y):
plt.text(a, b, (a, b), ha='center', va='bottom', fontsize=10)
plt.plot(x, y, 'bo-')
plt.show()
在本例中,我们使用plt.text()函数给图形添加了文本注释,借此把点的坐标逐个标注到了图形当中。
在第08行代码中,plt.text(a,b,(a,b),ha='center', va='bottom', fontsize=10)
的前两个参数表示要标注的X轴和Y轴的坐标位置。第三个参数表示标注的文本内容,我们在这里打算显示的是坐标点。
在图中,我们可以看到,坐标点文本压在所绘制的曲线上,如果想优化显示文本的位置,我们可以调整前两个参数的位置。例如,plt.text(a- 0.5,b,(a,b))
就表示把文本的X坐标左移0.5个单位。ha、va分别是horizontal alignment(水平对齐)、vertical alignment(垂直对齐)的简写。ha可选的参数有’center’、‘right’、‘left’,va可选的参数有’center’、‘top’、‘bottom’、‘baseline’、‘center_baseline’。
在某些情况下,我们需要给图形设置一个标题,修改坐标轴的刻度值,或关闭坐标轴显示等。这时,我们可以使用plt.title()函数来给图形设置标题,使用plt.xticks()函数设置X轴的刻度值,使用plt.yticks()函数设置Y轴刻度值,使用plt.xlim()函数、plt.ylim()函数分别设置X轴和Y轴的区间范围,使用plt.xlabel()函数、plt.ylabel()函数设置X轴和Y轴的名称。
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-5, 5, 0.05)
y1 = np.sin(x)
y2 = np.cos(x)
# 为在Matplotlib中显示中文,设置特殊字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.title("双曲线")
plt.ylim(-1.2, 1.2)
plt.xlim(-6, 6)
plt.xticks(ticks=np.arange(-1.5 * np.pi, 2 * np.pi, 0.5 * np.pi),
labels=['$-\\frac{3}{2}\pi$', '$-\pi$', '$-\\frac{1}{2}\pi$',
'0', '$\\frac{1}{2}\pi$', '$\pi$', '$\\frac{3}{2}\pi$'])
plt.yticks(ticks=[-1, 0, 1])
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.plot(x, y1, 'r-', label='$y_1 = sin(x)$')
plt.plot(x, y2, 'b:', label='$y_2 = cos(x)$')
plt.legend(loc='best')
plt.show()
在代码第12行,我们使用plt.xlim()设置X轴的坐标范围为(-6, 6)。这里xlim表示X轴的限度(limit)。类似地,ylim表示Y轴的限度(limit)。代码第13行使用plt.ylim()设置Y坐标轴范围为(-1.2, 1.2)。接着,使用plt.xlabel()设置X坐标轴名称’X轴’,使用plt.ylabel设置Y坐标轴名称’Y轴’。
我们使用plt.xticks()(代码第14行~16行)设置X轴的刻度,使用plt.yticks()(代码17行)设置Y轴的刻度。xticks()、yticks()函数分别用于设置X轴和Y轴的刻度与标签。这两个函数都有相同的参数ticks和labels。其中ticks用于设置坐标轴的刻度值,labels用于设置坐标轴的标签值,标签中可以添加LaTeX公式。
import matplotlib.pyplot as plt
import numpy as np
# 在(0,4)区间,以间隔0.2均匀分隔
data = np.arange(0, 4, 0.2)
# 分别使用红色的点划线、蓝色的方块和绿色的三角形来区分这三条曲线
plt.plot(data, data, 'r-.', data, data ** 2, 'bs', data, data ** 3, 'g^')
plt.savefig('mult_lines.png', dpi=600)
第一个小技巧就是前面所说的,我们可以一次性地绘制多条曲线,如代码第08代码按照顺序先后提供了三条线段的X轴数据、Y轴数据和线条样式,实际上实现了 y = x y=x y=x、 y = x 2 y=x^2 y=x2和 y = x 3 y=x^3 y=x3这三条曲线的绘制。
为了区分这三条曲线,要让它们在样式上有所不同,
“r-.”
,其中“r”
表示红色(red),“-.”
是非常形象的点画线。显而易见的是,“--”
表现虚线,读者可自行测试一下。“bs”
,其中“b”
表示的是颜色blue(蓝色),“s”
表示图形为方形(square)。“g^”
,其中“g”
表示的是颜色green(绿色),第二个字符“^”
表示图形是“三角形”.第二个值得关注的小技巧是,如果我们不想在屏幕显示图形,而是想将显示结果另存为一张图片以备后用,就可以使用savefig()方法。在该方法中填写对应的存储路径和文件名(包括扩展名)即可。这个方法神奇的地方在于,它会根据文件名的扩展名不同,自动识别图片并将其存储为对应的格式。
在第09行代码中,参数dpi=600并不是必需的。只有当你觉得生成图片的分辨率“惨不目睹”时,设置这个参数才有必要,可以提高分辨率。这里dpi表示的含义是Dots Per Inch(每英寸点数,简称DPI),它是一个量度单位,用于衡量生成图片的每英寸像素数量。通常,DPI越大,图片的清晰度也就越高,但占据的比特数也越高,不利于网络传输,所以有时候我们为了网络传输质量和传输速度,会对DPI的大小做一个合理的权衡。
import matplotlib.pyplot as plt
import numpy as np
data = np.arange(0, 4, 0.2)
plt.plot(data, data, 'r-.', data, data ** 2, 'bs', data, data ** 3, 'g^')
plt.grid(b=True)
plt.savefig('mult_lines-grid.png', dpi=600)
本例中的核心代码是第07行中的grid()方法,其原型如下。
grid(b = None, which = u'major', axis = u'both', **kwargs)
grid()方法的参数解释如下。
在前面的讨论中,每次我们都绘制一张图片,实际上,有时候我们需要将多个子图绘制在一起进行比较。这时需要利用绘制子图的方法subplot(),其函数原型大致如下。
subplot(nrows, ncols, plot_number)
上述方法的功能为,绘制nrows行ncols列第plot_number个子图。显然,在这种布局下,我们一共有nrows×ncols个子图,参数plot_number指明是第几个子图。
import matplotlib.pyplot as plt
import numpy as np
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)
# 第一种绘制子图的方法
fig = plt.figure() # 创建一个画布
sub_fig1 = fig.add_subplot(211) # 创建一个子图
sub_fig1.grid(True) # 添加网格线
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
# 第二种绘制子图的方法
plt.subplot(2, 1, 2)
plt.plot(t2, np.cos(2 * np.pi * t2), 'r--')
plt.show()
在本例中,我们提供了两种绘制子图的方法,二者是等价的。
第一种方法的思路是,我们先构建一个画布(第11行),然后在该画布上利用add_subplot()方法添加一个子图,其参数的含义同subplot()方法。这里的“211”,表示的就是两行一列第一个子图。构造子图时,我们可以添加网格线。
需要特别注意的是第14行代码。实际上,第14行中的plot()绘制了两个图形。第一个是由蓝色(标记为b)的实心圆(标记为o)标记的,X轴的数据为t1(第07行),Y轴的数据由第04~05行的函数f(t1)构造。第二个图形为黑色(标记符号为k)的曲线,其中,X轴的数据为t2(第08行),Y轴数据由第04~05行的函数f(t2)构造。两个图形叠加在一起构成了点画线。
第二种方法就是利用subplot()方法,它是子模块pyplot下属的方法,用起来更加简单明了。此处绘制的是红色虚线,绘制函数是cos2πx。这里的π是用NumPy模块中的np.pi表示的。
在绘图时,Figure(画布)最大,它有点像绘制实体画所用的画板,例如代码fig=plt.figure()的意思就是创建一个空画布。
在画布里,我们可以创建各种子图。子图主要有两类:一类是规规矩矩、排列整齐的子图,叫作Subplot;另一类是可以不那么规则摆放的子图,叫作Axes。
把Figure想象成Windows操作系统的桌面,在桌面上会有各种图标(icon),如果图标是自动对齐到网格的,就称之为Subplot;如果图标是自由摆放的,甚至可以相互重叠的那种,就称之为Axes。但不管怎么摆放,Subplot和Axex本质上都是Figure内的子图。
但让我们比较困惑的是,在绘图时axis会出来捣乱。其实axis是地地道道的坐标轴。每个子图都有坐标轴。为了获得更好的可读性,每个坐标轴都可以配上标签(label)。例如,X轴有xlabel这个属性,Y轴有ylabel属性等。
可能Matplotlib的设计者认为,任何一个子图都要通过多个轴(axis)来呈现(二维图有两个轴,三维图有三个轴),众轴成图,所以就用“axis”的复数形式“Axes”表示子图。但切不可认为Axes是多个轴(axis)的意思,而应该在整体上把它视为一个在画布中可任意摆放的子图。下面,我们举例说明。
import matplotlib.pyplot as plt
# 生成一个没有子图(Axes)的画布
fig = plt.figure()
plt.show()
这样会没有图片显示出来。
如果没有子图,光有一个画布,是无法构成一个图形显示对象的。但是,如果我们有意识地添加子图,哪怕是一个空子图,它也构成了可显示的图形对象。
# 生成一个画布,其中有2*2分布均匀的子图
fig, axes_lst = plt.subplots(2, 2)
plt.show()
值得注意的是,plt.subplots()返回两个值,一个是Figure(画布),另一个就是Axes对象,此处命名为axes_lst。如前文所述,在这种场景下,Axes就是Subplot。
现在,我们有四个子图(Axes),那该如何区分它们呢?这个区分并不复杂,类似于NumPy的多维数组访问,ax_lst[0,0]就表示第0行第0列的子图(下标从0开始计数,下同),即左上角的图。类似地,ax_lst[0,1]就表示第0行第1列的子图,即右上角的图。以此类推。
如果我们知道这个区分方式,就可以“指哪打哪”了。比如说,我们仅仅想在右下角的子图上绘制特定图形,就可以如下操作。
# 构造x轴和y轴数据
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
# 在第一行第一列的子图中绘图
axes_lst[1, 1].plot(x, y)
plt.show()
上述代码中的axes_lst[1, 1].plot(x, y)完全等价于pyplot模型下的如下两行代码。
我们想绘制一个大图中套小图的图形,即“图中图”,使用Axes就相对容易操控一些,代码如下。
# 创建空画布
fig = plt.figure()
left1, bottom1, width1, height1 = 0.1, 0.1, 0.8, 0.8
# 在画布上添加一个子图
axes_1 = fig.add_axes([left1, bottom1, width1, height1])
axes_1.scatter(x, y)
axes_1.set_xlabel('x')
axes_1.set_ylabel('y')
axes_1.set_title('title')
left2, bottom2, width2, height2 = 0.6, 0.6, 0.25, 0.25
# 在画布上添加另外一个子图
axes_2 = fig.add_axes([left2, bottom2, width2, height2])
axes_2.plot(x, y)
axes_2.set_title('title inside')
plt.show()
在以上代码中,我们使用fig.add_axes([left, bottom, width, height])添加子图时,要事先确定子图在画布的位置,这时需要四个参数来定位:图左下角(即原点的左边坐标和底部坐标)的位置和图形大小(宽和高)。但需要注意的是,这四个值都用占整个Figure坐标系的百分比来表示,即都是小于1的小数
假设Figure的大小是10×10,对于上述代码的配置,我们绘制的大图起点(原点)就是坐标(1, 1),因为left=10*0.1=1,bottom=10 * 0.1=1。类似地,我们也很容易获知大图的width=8,height=8。对小图的计算也是类似的,不再赘述。从图中可以看出,我们同样可以对小图设置X轴和Y轴的标签及图形标题。
由上面的分析可知,用add_axes()方法生成子图灵活性更强,它完全可以实现add_subplot()方法的功能,且更容易控制子图的显示位置,甚至实现相互重叠的效果(参见以下代码)。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig = plt.figure()
axes_1 = fig.add_axes([0.1, 0.1, 0.5, 0.5])
axes_2 = fig.add_axes([0.2, 0.2, 0.5, 0.5])
axes_3 = fig.add_axes([0.3, 0.3, 0.5, 0.5])
axes_4 = fig.add_axes([0.4, 0.4, 0.5, 0.5])
axes_4.plot(x, y)
plt.show()