参考链接: Python | 使用openpyxl模块在Excel工作表中绘制图表 3
Matplotlib 是一个非常强大的 Python 画图工具,通过绘制线图、散点图、等高线图、条形图、柱状图、3D 图形、甚至是图形动画等,可以更加直观的呈现科学计算等遇到的大量数据。
1、基本元素
在matplotlib的层级结构中,最高层是由matplotlib.pyplot模块提供的“状态机环境”(state-machine environment),通过它来为当前维度绘制图像元素,例如曲线、文本、图片等。接下来一层是第一级的面向对象接口,在这一层中用户可以使用pyplot创建并追踪图像对象,并由此创建一个或多个数据轴系,这些轴系在之后用于图的绘制。
如下所示为图像的各个元素:
Figure指的是整张图片对象,用于包含一个或多个数据轴系(Axes),如果没Axes,则图像为空。
fig = plt.figure() # 创建一个图像对象
fig.suptitle('No axes on this figure') # 添加一个标题
fig, ax_lst = plt.subplots(2, 2) # 创建包含2×2个子图的图像
Aexs是轴(Axis)的复数,翻译为轴系、坐标系,可以将其理解为整个图像(Figure)的一个子图,一个图形可以包含多个子图,我们在子图中进行具体图表的绘制。通过plt.plot()函数可以创建子图对象,通过set_title()可以设置子图的标题,set_xlabel()、set_ylabel()对图的横纵坐标的标签进行设置。
Axis指具体的坐标轴,其中trick代表坐标轴的刻度,通过Location对象控制刻度的位置,Formatter格式化刻度显示的字符串,通过这两个对象可以精确地控制刻度的显示。
Artist对象:在图像中任何元素都可以看作Artist,包括2D线条(Line)、文本、以及Axis和图像Figure,当图片被渲染时,这些对象被绘制到图片中。大多数Artist对象绑定于一个具体轴Axis,这样的对象不可以在轴系之间共享。
matplot的输入数据最好转化为np.array类型,使用其他的数据类型例如np.matrix、pandas等可能会报错,因此在传入参数前先将其他类型通过numpy转换一下。
matplotlib建议的引入必要库与编码方式如下,绘图时首先通过plt接口创建Figure对象与子图对象ax,之后通过ax调用函数绘图,通过定义函数来进行图像绘制可以避免许多重复操作,而且便于代码维护。例如下面定义了my_plotter()完成图像绘制,传入子图ax,两个数据data1,data2,以及图像绘制参数param_dic。
import matplotlib.pyplot as plt # 引入必要的库
import numpy as np
%matplotlib inline
# 定义函数进行图像绘制操作
def my_plotter(ax,data1,data2,param_dic):
ax.plot(x, y,**param_dic)
x = np.arange(0, 10, 0.2) # 准备数据
y = np.sin(x)
fig, ax = plt.subplots(1) # 创建子图
my_plotter(ax,x, y,{ 'linestyle':'dashed','label':'sinx'}) # 调用函数绘制图像
2、基本用法
创建图像并绘制函数
首先需要导入必须的函数库matplotlib.pyplot与numpy。如果使用jupyternotebook则需要添加%matplotlib inline设置以显示图片
通过plt.figure()创建一个图像,其中num属性指出显示图像的窗口,figsize属性可以指定图像的大小,facecolor属性指定图像的背景颜色。
通过plt.plot()进行曲线的绘制,前两个数组参数为图像x、y的坐标值。之后的可选参数对可以对线条的颜色(color)、宽度(linewidth)、样式(linestyle)、不透明度(alpha)进行设置
plt.scatter()绘制单独的一个点,而不是连起来的曲线,同样前两个参数为数组,对应点的x、y坐标。属性s设置点的大小,color设置颜色
最后通过plt.show()输出图像,或者通过plt.savefig()保存为指定图片,注意要在show()之前使用,否则图片会被输出,从而保存的图片为空。
如果需要频繁进行绘图,则需要关闭一些没用的图片以节约内存。通过plt.cla()可以关闭当前Axes,plt.clf()关闭当前图像,plt.close()关闭当前绘图窗口。
import numpy as np
import matplotlib.pyplot as plt
# 设置行内显示
%matplotlib inline
x = np.linspace(-5, 5, 50) # 在-5~5内均匀取50个数
y1 = 2*x + 1 # 定义一个线性函数
y2 = x**2 # 定义一个二次函数
plt.figure(num=3, figsize=(10, 5), facecolor='white') # 创建一个图形对象
plt.plot(x, y2, alpha=0.5) # 绘制曲线
plt.plot(x, y1, color='red', linewidth=1.0, linestyle='--')
plt.scatter([1],[1],s=200,color='orange') # 绘制一个点
plt.savefig('./myplt.jpg') # 保存图片
plt.show() # 显示图像
plt.close() # 关闭绘图窗口
调整坐标轴
通过plt.xlim可以调整x坐标轴的范围,plt.xlabel设置坐标轴的名字,同理plt.ylim与ylabel对y轴进行设置。matlab中无法使用中文,需要对字符集进行设置。
plt.xticks()可以自定义坐标刻度,将刻度以数组的形式传入,并且可以传入自定义显示的刻度值
设置坐标轴刻度的位置:通过axes对象.xaxis获取到x轴,并通过set_ticks_position()方法来设置位置,可选位置有:top,bottom ,both,default,none,对应的y轴刻度有:left,right,both,default,none
# 设置行内显示
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置字符集显示中文
plt.rcParams['axes.unicode_minus'] = False # 设置负号正确显示
x = np.linspace(-5, 5, 50)
y1 = 2*x + 1
y2 = x**2
plt.figure(num=3, figsize=(10, 5))
plt.plot(x, y2)
plt.plot(x, y1, color='red', linewidth=1.0, linestyle='--')
plt.xlabel('x轴') # 设置显示x轴标签
plt.xlim(-2,2) # 设置x轴的范围为-2~2
plt.xticks([-2,-1,0,1,2]) # 设置x轴刻度
axes=plt.gca()
axes.xaxis.set_ticks_position('top') # 设置x坐标显示在上边框
plt.ylabel('y轴')
plt.ylim(-5,5) # 设置y轴的范围为-5~5
plt.yticks([-3,-1,0,1,3],['低','较低','中等','高','较高']) # 自定义刻度值
axes.yaxis.set_ticks_position('left') # 设置y坐标显示在左边框
我们还可以对图片的四个边框进行设置,通过plt.gca()获取轴系对象axes,通过.spines['right']获取右边框,之后可以set_color来设置其颜色,用set_position()来设置边框位置
axes.spines['right'].set_color('none') # 获取并设置右边框透明
axes.spines['top'].set_color('none')
axes.spines['bottom'].set_position(('data', 0)) # 将底部边框放到x=0的位置
axes.spines['left'].set_position(('data',0)) # 将左边框放到y=0的位置
3、图例和标注
在使用plt.plot()进行函数绘制时可以添加label属性来定义曲线的名称。
通过plt.legend()在图片中显示图例,其loc属性定义图例的位置,值有'best' : 0,'upper right' : 1, 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10,其中best代表自动分配最佳位置
plt.plot(x, y1, label='linear')
plt.plot(x, y2, color='red', linewidth=1.0, linestyle='--', label='square')
plt.legend(loc='upper right')
也可以将plot()结果保存为l1,l2对象,再传入legend函数中的handles属性,这样legend函数可以对图中指定的线条对象添加图例,并通过labels属性设置图例的内容
l1, = plt.plot(x, y1)
l2, = plt.plot(x, y2, color='red', linewidth=1.0, linestyle='--')
# 对指定线条对象添加图例
plt.legend(handles=[l1,l2,], labels=['linear','square'], loc='best')
通过plt.annotate()函数在图片中添加注释,第一个参数为注释的内容,fontsize注释大小,xycoords表示注释点的位置的表示方式,data代表根据数据,xy代表传入的数据,textcoords='offset points'代表注释的位置为根据点偏移,xytext设置偏移值,arrowprops以dict方式传入箭头的类型和弧度属性
通过plt.text()在图片中添加文本,前两个参数为文本的位置,第三个参数为文本内容,fontdict对文本字体、颜色进行设置,ha='center'设置居中对齐,va='bottom'设置向底部对其
plt.scatter([1],[1],s=100,color='orange')
# 添加自定义注释
plt.annotate('x²=2x-1',fontsize=20,xycoords='data',xy=(1,1),
textcoords='offset points',xytext=(20,-30),
arrowprops=dict(arrowstyle='->',connectionstyle='arc3,rad=.2'))
# 添加文本
plt.text(0.5,-1,"This is a text",fontdict={'size':15,'color':'green'},ha='center', va='top')
4、绘制图像
散点图
通过scatter()绘制散点图,s属性为点的大小,c为点的颜色,alpha点的不透明度,marker指定点的形状
n = 1024 # data size
X = np.random.normal(0, 1, n) # 每一个点的X值
Y = np.random.normal(0, 1, n) # 每一个点的Y值
C = np.arctan2(Y,X) # 为每个点生成颜色
plt.scatter(X, Y, s=75, c=C, alpha=0.5, marker='o')
plt.show()
条形图
通过plt.bar()函数绘制条形图,前两个参数传入柱状图的x位置和y的值的数组,facecolor属性设置主题颜色,edgecolor设置边框颜色
n = 10
X = np.arange(n) # X取1~12
Y1 = np.array([8,9,12,6,5,10,11,13,4,2])
Y2 = np.array([5,6,8,3,14,10,3,2,1,4])
plt.bar(X, Y1, facecolor='blueviolet', edgecolor='orange')
plt.bar(X, -Y2)
# 在柱状图顶部添加文本标注
for x, y in zip(X, Y1):
plt.text(x, y , y, ha='center', va='bottom')
plt.show()
如上右图所示,通过plt.hist(data)可以绘制数据的统计分布直方图,参数bins指定划分的统计区间,为数字时代表划分为几个区间,为数组时代表指定具体的区间。
plt.hist(data, bins=10) # 划分区间的个数
plt.hist(data, bins=[0, 500, 1000, 1500, 2000, 2500, 3000]) # 指定具体分组区间
等高线图
等高线的数据是三维数据,X,Y为自变量,Z为因变量函数值,根据函数值的不同显示相同的值作为等高线
通过plt.contourf()函数进行颜色的填充,前三个参数为对应地X、Y、Z的值,cmap为填充的颜色,这里使用了matlabplot提供的一个颜色映射方案,根据值的由小到大映射为从蓝到红色
plt.contour()进行等高线的绘制,colors='black'线条颜色为黑色,linewidth=.5线条宽度为0.5
通过clabel()绘制文本,inline将文本填充到线条内
通过colorbar()函数可以为图片添加一个函数值对应颜色条
n = 256
x = np.linspace(-3, 3, n)
y = np.linspace(-3, 3, n)
X,Y = np.meshgrid(x, y) # 编制为256×256的网格,并把对应的坐标值返回给X、Y数组
def fun(x,y): # 定义函数值Z,是一个三维函数椭圆抛物面
return x**2+y**2
F = plt.contourf(X, Y, fun(X, Y), 8, alpha=.75,cmap='RdBu') # 进行颜色填充
C = plt.contour(X, Y, fun(X, Y), 8, colors='black', linewidth=.5) # 绘制等高线
plt.clabel(C, inline=True, fontsize=10) # 标注文本
plt.colorbar(F) # 添加一个colorbar
plt.show()
三维图像
在绘制3D图形前需要额外引入一个包Axes3D,通过它来将当前的图像转化为3D图
同样3D图需要三维数据,X、Y编制为网格是自变量,Z是函数值
通过plt.plot_surface()绘制3D图形,属性rstride 和 cstride 分别代表 row 和 column 的跨度,跨度越小,图形上的网格越密集,cmap为函数值的配色映射方案
通过等高线的绘制函数plt.contour()可以为3D图形在平面绘制投影,zdir='z'代表沿着Z轴进行投影,这样投影会出现在X-Y平面上,offset代表投影的偏移位置
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D #引入3D绘图库
%matplotlib inline
fig = plt.figure()
ax = Axes3D(fig) # 将当前图像转化为3D
X = np.arange(-10, 10, 0.5)
Y = np.arange(-10, 10, 0.5)
X, Y = np.meshgrid(X, Y) # 编制 X-Y 平面的网格
Z = np.sqrt(X ** 2 + Y ** 2) # Z值,定义为抛物面图形
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='hsv') # 绘制3D图
ax.contourf(X, Y, Z, zdir='z', offset=0, cmap='hsv') # 添加沿Z轴方向的投影
plt.show()
绘制动画
绘制动画需要从matplotlib中额外引入animation库
之后先绘制其中一帧的图像,比如这里要绘制sinx的动画,先在0~2Π取自变量x数组,再绘制其sinx的曲线line
接着定义动画初始帧的显示函数init(),返回初始的line对象。然后定义其每一帧的图像显示函数animate,其参数i代表第i帧,根据i返回不同的line对象。
最后通过animation.FuncAnimation()绘制图像,其参数fig为之前创建的图片对象,init_func为初始化显示函数,func为每一帧的显示函数,interval为更新频率(单位ms),frames为动画一共有多少帧,blit为True代表每次只更新有变化的点
最后将返回的ani对象的save()方法将其保存为gif,这里使用的writer为imagemagick
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation # 引入动画库
%matplotlib inline
fig,ax=plt.subplots() # 创建画布
x=np.arange(0,2*np.pi,0.01) # 从0~2Π每隔0.01取一个数
line,=ax.plot(x,np.sin(x)) # 绘制sinx的图像
def init(): # 定义初始帧显示函数
line.set_ydata(np.sin(x))
return line,
def animate(i): # 定义每一帧的显示函数
line.set_ydata(np.sin(x+i/10))
return line,
# 绘制动画
ani=animation.FuncAnimation(fig=fig,init_func=init,func=animate,interval=20,frames=100,blit=True)
ani.save('sinx.gif',writer='imagemagick') # 保存动画为gif
plt.show()
5、多张图片
通过plt.subplot()可以划分为多个子图并选中其中的某个位置,例如subplot(2,2,1)代表创建2×2的子图,并且选中其中的第一个子图,也可以省略中间的逗号:subplot(224)代表选中2×2个子图中的第四个。除了直接通过plt对象绘制子图外,subplot可以返回子图对象,可以通过子图对象调用plot()来绘图。
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure()
# 选中2×2子图中的第一个并画一条线
plt.subplot(2,2,1)
plt.plot([0,1],[2,3])
# 选中2×2子图的第四个画一条线
ax2=plt.subplot(224)
ax2.plt.plot([2,1],[3,4])
plt.show()
注意这里的划分并不是真正的分割图像,只是为了定位子图而假定将整个图像进行划分。例如下面的代码首先将画布认为2行1列选中第一块,就是上面的一行大的,再将其认为2行3列选择第四块,那就是第二行的第一个。通过不同的划分和选择可以得到不同大小与不同位置的子图。如果涉及到冲突的区域,后面的子图会将之前的子图覆盖掉。
# 划分为2行1列,选中第一块
plt.subplot(2,1,1)
plt.plot([0,1],[2,3])
# 划分为2行3列,选中第四块
plt.subplot(234)
plt.plot([2,1],[3,4])
通过subplot2grid()方法可以更加方便的划分和选择子图,如下所示subplot2grid((3, 3), (0, 0), colspan=3)表示将图片分为3×3区域,从第0行0列开始,横向跨越colspan列数为3,纵向跨越rowspan行数默认为1。
注意在一整张图中可以通过title()、xlabel()、ylabel()来设置标题和坐标名,但是在子图中需要在函数名之前加一个set_
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax1.plot([1, 2], [1, 2]) # 画小图
ax1.set_title('ax1_title') # 设置小图的标题
ax2 = plt.subplot2grid((3,3), (2,1),colspan=2)
ax2.plot([2,1],[1,0])
ax2.set_xlabel('ax2_x') # 设置小图的坐标轴名
ax2.set_ylabel('ax2_y')
gridspec()函数可以让我们像使用python数组那样方便地选择子图区域。使用前先从matplotlib中引入该模块,通过Gridspec()函数将图像划分为区域,然后返回gs对象,通过gs对象可以像数组那样选择子区域。例如gs[1, :2],逗号之前代表对行序号的选择,如果只有一个数字代表选中全部序号为1的行,逗号之后为列,这里0:2代表选中从0到2列之前的所有列,其中从0开始可以省略。对应地如果选中指导结尾的话也可以省略,即gs[1, : ]。如果序号为-2,代表选中倒数第二个
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec # 引入库
%matplotlib inline
plt.figure()
gs = gridspec.GridSpec(3, 3) # 将图像划分为3×3个区域
ax1 = plt.subplot(gs[0, :]) # 选中第0行,所有列
ax2 = plt.subplot(gs[-1, -2]) # 选中倒数第1行,倒数第2列
plt.show()
通过subplots()可以一次性创建并返回多个子图对象,如下所示为划分四个子图并且依次返回给ax11~ax22四个对象,并且四个对象的位置不能乱。其中sharex代表图像共享X坐标轴。plt.tight_layout()代表紧凑显示
f, ((ax11, ax12), (ax21, ax22)) = plt.subplots(2, 2, sharex=True, sharey=True)
ax11.scatter([1,2], [1,2])
plt.tight_layout()
plt.show()
画中画:通过figure对象的add_axes()可以在图片中添加另一个图片,其传入的参数为图片所在画布的四个边框的位置比例,返回创建的子图对象ax,利用ax进行绘制
x=[0,1]
y=[2,3]
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # 创建一个大图对象
ax1.plot(x, y, 'r') # 大图绘制
ax2 = fig.add_axes([0.2, 0.6, 0.25, 0.25]) # 创建小图
ax2.plot(x, y, 'b') # 小图绘制
plt.show()
子图拷贝:通过子图对象ax的twinx()函数可以在相同位置拷贝x轴相同的另一个重叠的ax对象,只不过y轴放到了对称的位置,最后两个子图会重叠显示
fig, ax1 = plt.subplots()
ax2 = ax1.twinx() # 拷贝ax对象
ax1.plot([0,1], [2,3], 'g-') # 在原图中绘制
ax1.set_xlabel('X data')
ax1.set_ylabel('Y1 data', color='g') # 原图的y轴在左侧
ax2.plot([0,1], [3,2], 'b-') # 拷贝对象中绘图
ax2.set_ylabel('Y2 data', color='b') # 拷贝子图的y轴在右侧
plt.show()