关于Matplotlib的愚见
初级中,我只是简单介绍了Matplotlib的使用方法,在中级部分,我系统地说一下我总结的内容。
上图是我画的关于Matplotlib几个对象之间的关系图。它们都来自于一个叫做Artist的基类。我们知道绘制图的基础是Canvas,它是我们真正进行绘图的后端。Artist只是在程序逻辑上的绘图,它必须连接后端绘图程序才能真正在屏幕上绘制出来(或者保存为文件)。我们可以将canvas理解为绘图的物理(或者说硬件)实现。对于每个Artist类的对象,都有findobj()方法,来显示该对象所包含的所有下层对象。
Artist对象的所有属性都通过相应的get_*和set_*函数来读写。在后面的例子中,很多都不是对fig的操作,而是对ax的操作,这里要记住这种方法。
fig.set_alpha(0.5*fig.get_alpha())
如果要同时给多个属性进行设置的话,可以使用set函数:
fig.set(alpha = 0.5, zorder=2)
使用matplotlib.pyplot.getp 函数则可以方便地输出Artist对象的所有属性名和值。
plt.getp(fig.patch)
生成多张画布
比如
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 10)
print(x)
y = x*2+1
z = x**2
plt.figure()
plt.plot(x, y)
plt.figure()
plt.plot(x, z)
plt.show()
运行效果为:
和
如果不写的话,默认是从1开始算。我们也可以在里面指定参数,用来给这个画布编号,比如:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 100)
y = x*2+1
z = x**2
plt.figure()
plt.plot(x, y)
plt.figure(num=3, figsize=(8, 5))
plt.plot(x, z)
plt.show()
运行效果为:
和
坐标轴常用设置
- xlim、ylim方法设置坐标轴刻度取值范围
- xlabel、ylabel方法设置x轴和y轴的标题
- xticks,yticks方法设置x,y轴的刻度标签值
例子:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 100)
y = x*2+1
plt.figure(1)
plt.plot(x, y)
plt.figure(2)
plt.plot(x, y)
# 设置坐标轴取值范围
plt.xlim((-2, 2))
plt.ylim((0, 3))
# 设置坐标轴标题
plt.xlabel("我是横坐标")
plt.ylabel("我是纵坐标")
# 设置刻度标签
new_ticks = np.linspace(-1, 4, 6)
plt.xticks(new_ticks) # 将原有x轴刻度标注修改,图也会随着坐标改变而改变。
plt.yticks([1, 2, 3],
["低", r"$I am middle$", "高"]) # 用第二个参数的值一一替换第一个参数的值。
plt.show()
运行效果为:
和
坐标轴边框设置及位置移动
这里我们需要认识的是:gca,它的意思是get current axis(获取当前轴)。
获取了当前绘图区(轴)之后我们再通过ax.spines对其进行操作,spines是脊柱的意思。
具体的操作如下例:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 100)
y = x*2+1
plt.figure(1)
plt.plot(x, y)
plt.figure(2)
plt.plot(x, y)
# 去掉上和右边的边框线
ax = plt.gca() # 获取当前绘图区
ax.spines["right"].set_color("none")
ax.spines["top"].set_color("none")
# 更改坐标轴位置
ax.xaxis.set_ticks_position("bottom") # 因为绘制的图形中,上下都算是坐标轴,我们如果要绘制坐标系的话需要指定其中一个为坐标轴,这里我们是将下坐标轴来作为X轴。
ax.yaxis.set_ticks_position("left") # 同上,用左轴作为Y轴。
ax.spines["bottom"].set_position(("data", 0)) # data表示用值来选择,这个句话的意思就是用X坐标轴上值为0的点作为坐标系原点。"data"只是一个参数,还有outward,axes等参数,它们都有不同的意义。
ax.spines["left"].set_position(("data", 0)) # 同上
plt.show()
运行结果为:
和
图例的设置
在初级,我们说过简单的设置图例,这里我们在进行一些拓展的说明。
例子:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 100)
y = x*2+1
z = x**2
plt.figure(1)
L1, = plt.plot(x, y, label="$x*2+1$") # L1后面的逗号是matplotlib特殊的写法
L2, = plt.plot(x, z, label="$x^2$")
plt.legend(handles=[L1], labels=["a", "b"], loc="best") # handles是用来控制放入图例的内容。labels是用来给handles对应的图修改图例名。
plt.show()
运行结果为:
在图片中添加注解
在讲解给图片添加注解之前呢,我们需要认识一下scattter函数,我们在绘制图像的时候使用的是plot,它是线的形式,如果我们使用scatter的话,它就会用点的形式来绘制。
下面是示例代码:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 100)
y = x*2+1
plt.figure(1)
plt.plot(x, y)
plt.figure(2)
plt.plot(x, y)
# 去掉上和右边的边框线
ax = plt.gca() # 获取当前绘图区
ax.spines["right"].set_color("none")
ax.spines["top"].set_color("none")
# 更改坐标轴位置
ax.xaxis.set_ticks_position("bottom") # 因为绘制的图形中,上下都算是坐标轴,我们如果要绘制坐标系的话需要指定其中一个为坐标轴,这里我们是将下坐标轴来作为X轴。
ax.yaxis.set_ticks_position("left") # 同上,用左轴作为Y轴。
ax.spines["bottom"].set_position(("data", 0)) # data表示用值来选择,这个句话的意思就是用X坐标轴上值为0的点作为坐标系原点。"data"只是一个参数,还有outward,axes等参数,它们都有不同的意义。
ax.spines["left"].set_position(("data", 0)) # 同上
# 设置标注
x0 = 1
y0 = 2*x0 + 1
plt.scatter(x0, y0, s=50, color="b") # 设置要标注的点,scatter和plot函数一样可以绘制图形,不过它是用点构成的图形。其中s是控制大小,color是控制颜色。
plt.plot([x0, x0], [y0, 0], "k--", lw=1) # 设置一个黑色的虚线。
plt.annotate(r'$2x+1=%s$' % y0, xy=(x0, y0), xycoords='data', xytext=(+30, -30),
textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle='->', connectionstyle="arc3,rad=.2"))
"""
下面是annotate中每个参数设置的意思:
plt.annotate(r"$2x+1 = %s$" % y0, # 标注的文字描述
xy=(x0, y0), # xy是设置要标注的点。
xycoords="data", # xycoords表示前面xy设置的内容以"data"的值为基准。
xytext=(+30, -30), # 标注信息在要标注的点的横坐标加30,纵坐标减30的位置显示。
textcoords="offset point", # xtext设置的内容以"offset point",也就是偏移量为基准。
fontsize=16, # 设置字体的大小
arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=.2") # 设置箭头。arrowstyle的设置表示使用箭头,connectionstyle是用来设置弧度的。
)
"""
plt.text(-3.7, 3, r'$This is the some text. mu sigma_i alpha_t$',
fontdict={'size': 16, 'color': 'r'})
"""
下面是text中每个参数设置的意思:
plt.text(-3.7, 3, # 文字显示的位置
r'$This is the some text. mu sigma_i alpha_t$', # 显示的文字
fontdict={'size': 16, 'color': 'r'}) # 文字的格式
"""
plt.show()
运行结果如下:
和
当线遮挡刻度标注解决办法
示例代码:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 50)
y = 0.1*x
plt.figure()
# 在 plt 2.0.2 或更高的版本中, 设置 zorder 给 plot 在 z 轴方向排序
plt.plot(x, y, linewidth=10, zorder=1)
plt.ylim(-2, 2)
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data', 0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontsize(12) # 设置字体大小
label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.7, zorder=2))
"""
set_bbox的参数说明:
label.set_bbox(dict(facecolor='white', # 背景颜色
edgecolor='None', # 边框
alpha=0.7, # 背景透明度
zorder=2)) # 在 plt 2.0.2 或更高的版本中, 设置 zorder 给 plot 在 z 轴方向排序
"""
plt.show()
运行结果:
同画布绘制多图
方式1:使用subplot来绘制
在初级中,我们说过我们画一个图是要在一个画布上,我们画的图在Matplotlib中被称为轴或者绘图区,一个画布中可以包含多个绘图区,每个绘图区都拥有自己坐标系统的绘图区域。我们可以通过subplot()函数来快速绘制有多个绘图区的画布。
它的格式为:
plt.subplot(numRows, numCols, plotNum)
subplot将整个绘图区域划分为numRows行和numCols列个区域,然后按照从左到右,从上到下的顺序对每个子区域进行编号,如下图所示。
在使用的时候,有的人可能会看到subplot(324)这样的设置,这样也是正确的写法,它等效于subplot(3, 2, 3)。如果numRows、numCols、plotNum这三个数都小于10的话,可以把它们缩写为一个整数。
如果新建的轴和之前的轴位置重叠的话,原来的轴会被覆盖。
使用例子如下:
import matplotlib.pyplot as plt
plt.figure()
plt.subplot(2, 2, 1)
plt.plot([0, 1], [0, 1])
plt.subplot(2, 2, 2)
plt.plot([0, 1], [0, 2])
plt.subplot(223)
plt.plot([0, 1], [0, 3])
plt.subplot(224)
plt.plot([0, 1], [0, 4])
plt.show()
运行效果为:
我们还可以自定义它们的跨列显示
import matplotlib.pyplot as plt
plt.figure()
plt.subplot(2, 1, 1)
plt.plot([0, 1], [0, 1])
plt.subplot(2, 3, 4)
plt.plot([0, 1], [0, 2])
plt.subplot(235)
plt.plot([0, 1], [0, 3])
plt.subplot(236)
plt.plot([0, 1], [0, 4])
plt.show()
运行代码如下:
说明:
使用subplot是比较别扭的,要大概了解它的绘制顺序。在上面例子中,
(2, 1, 1),第一个参数表示figure分为两行,第二个参数表示分为一列,第三个表示它使用第一个图绘制。
(2, 3, 4),第一个参数表示figure分为两行,第二个参数表示分为三列,第三个表示它使用第四个图绘制(因为第一行是跨3列,所以这个是从第四个开始计算)。(2, 3, 5)和(2, 3, 6)同理。
不同figure切换加figure切分的例子:
import numpy as np
import matplotlib.pyplot as plt
plt.figure(1)
plt.figure(2)
ax1= plt.subplot(221)
ax2 = plt.subplot(222)
ax3 = plt.subplot(212)
x = np.linspace(0, 3, 100)
for i in range(5):
plt.figure(1)
plt.plot(x, np.exp(i*x/3))
plt.sca(ax1)
plt.plot(x, np.sin(i*x))
plt.sca(ax2)
plt.plot(x, np.cos(i*x))
plt.sca(ax3)
plt.plot(x, np.exp(i*x/3))
plt.show()
它的效果如图:
从上面的例子中我们可以看出:使用subplot()会返回它所创建的绘图区,通过变量保存后,我们可以使用sca()函数交替让它们成为当前绘图区,然后调用plot()函数在这个绘图区里面绘制图形。如果有多个画布,我们可以用figure()函数来指定一个画布。如果这个画布存在则将该画布变为当前画布,如果不存在则创建一个对应的画布。
方式2:使用subplot2grid来绘制
subplot2grid使用起来要比第一种subplot的方式要更直观和清晰些。我们来看看例子:
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3, rowspan=1) # 第一个参数是整个figure分为几个块,第一个为行,第二个为列;第二个参数为这个图形的起始位置,位置是从0开始计算的;第三个和第四个分别设置这个图形占据几列和几行。
ax1.plot([1, 2], [2, 2])
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))
运行结果:
方式3:使用gridspec来绘制
使用它之前我们要先导入:
import matplotlib.gridspec as gridspec
让我们再看看实际的使用:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure()
gs = gridspec.GridSpec(3, 3) # 将一个figure分成3*3的格子。
ax1= plt.subplot(gs[0, :]) # 占第0行和所有列
ax2 = plt.subplot(gs[1, :2]) # 占第1行和第2列前的所有列
ax3 = plt.subplot(gs[1:, 2]) # 占第1行后的所有行和第2列
ax4 = plt.subplot(gs[-1, 0]) # 占倒数第1行和第0列
ax5 = plt.subplot(gs[-1, -2]) # 占倒数第1行和倒数第2列.
运行结果:
方式4:使用subplots来绘制
注意,这个和第一种方法不同,第一种创建方式我们使用的是subplot,这里我们使用的是subplots。
使用例子:
import matplotlib.pyplot as plt
f,((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex=True, sharey=False)
plt.tight_layout() # 不加会紧凑显示
ax1.plot([1, 2], [2, 2]) # ax1中绘制图像。
plt.show()
"""
说明:
使用plt.subplots建立一个2行2列的图像窗口,
sharex=True表示共享x轴坐标,
sharey=True表示共享y轴坐标。
((ax11, ax12), (ax13, ax14))表示第1行从左至右依次放ax1和ax2, 第2行从左至右依次放ax3和ax4.
"""
运行效果:
绘制图内图
代码:
# 导入pyplot模块
import matplotlib.pyplot as plt
# 初始化figure
fig = plt.figure()
# 创建数据
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 4, 2, 5, 8, 6]
left, bottom, width, height = 0.1, 0.1, 0.8, 0.8 # 4个值都是占整个figure坐标系的百分比。在这里,假设figure的大小是10x10,那么大图左边距为1,底边距为1,宽8,高8的坐标系内。
# 绘制外围图
ax1 = fig.add_axes([left, bottom, width, height])
ax1.plot(x, y, 'r')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_title('title')
# 绘制内图
left, bottom, width, height = 0.2, 0.5, 0.3, 0.3
ax2 = fig.add_axes([left, bottom, width, height]) # 假设figure的大小是10x10,那么大图左边距为2,底边距为5,宽3,高3的坐标系内。
ax2.plot(y, x, 'b')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('title inside 1')
运行结果为: