用过Matlab的小伙伴应该对Matlab内的绘图工具记忆犹新,python作为一门强大的胶水语言,自然也有对应的绘图库。Matplotlib是一个非常有用的绘图库,常用数据可视化,Matplotlib依赖于Numpy的存在,在此之前你需要安装Numpy.
直接使用python自带pip工具:
pip install numpy # 如果没有安装Numpy
pip install matplotlib
# pip3 install matplotlib # 如果是python3的话
先来一个demo,绘制一条直线,输入下面的小段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(1,10,50) # 在1到10之间产生50组数据(数据之间呈等差数列)
y= 2 * x + 1
plt.plot(x, y)
plt.show()
这里我们使用的np.linspace函数产生等一组等差的数据(即x,也就是自变量),接下来运算出应变量y,然后使用plt.plot绘制该曲线。
程序运行,输出下面的’Figure 1’:
注意到在输出的图像中,还有一些分析图像的操作:局部放大操作,修改边框等(类似于Matlab,参见上图)
在知道plot的基本使用场景后,下面我们考虑一下这个问题:如何同时绘制多条曲线?
这又可以分为两种情况:
基于上面的思考,我们看看如果在matplotlib上实现。
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
# 在1到10之间产生50组数据(数据之间呈等差数列)
x = np.linspace(1,10,50)
y1= 2 * x + 1
y2 = x**2
# 创建一个fig对象,在下一个fig对象创建前管理所有绘图资源
plt.figure()
plt.plot(x, y1)
plt.show()
# 创建新fig对象,管理下面的操作的绘图资源
plt.figure(num=3, figsize=(8, 5)) # 设置当前图像序号和大小
plt.plot(x, y1)
plt.plot(x, y2, color='red',linewidth=3.0,linestyle='--') # 线条为红色,线宽为3,style为虚线
plt.show()
程序运行,输出下面两张图像:
可以看到,我们可以创建多个figure对象,从而创建不同的图像。同时可以主要到figure接收多个属性设置,plot也接收多个属性设置。
class matplotlib.figure.Figure(num=None,figsize=None, dpi=None, facecolor=None, edgecolor=None, linewidth=0.0, frameon=None, subplotpars=None, tight_layout=None)
参数 | description |
---|---|
num | figure对象的标识符 |
figsize | w,h tuple in inches. 当前图像的大小 |
dpi | 每英寸的点数 |
facecolor | 图像的背景颜色 |
edgecolor | 图像的边框颜色 |
linewidth | 线宽 |
matplotlib.pyplot.plot(*args, **kwargs)
可以看到,plot接收参数是一个可变元组args,和一个列表kwargs.
参数args
args就是我们要绘制的data,args可以为x,y对,也可以是单个y,常见的形式如下:
plot(x, y) # plot x and y using default line style and color
plot(x, y, 'bo') # plot x and y using blue circle markers
plot(y) # plot y using x as index array 0..N-1
plot(y, 'r+') # ditto, but with red plusses
如果x and/or y是2维的,则会直接绘制对应的列。
参数kwargs
kwargs是用来控制我们绘图线的属性.例如label属性、color属性、linestyle属性、linewidth属性、antialiased(抗锯齿)属性等.这里设置的属性就是Line2D内包含的属性。
color属性常见取值
标识 | 含义 | 标识 | 含义 | 标识 | 含义 | 标识 | 含义 |
---|---|---|---|---|---|---|---|
b | 蓝色 | g | green | m | magenta | y | yellow |
r | red | c | cyan | k | black | w | white |
(1,2,3) | 即rgb | (1,2,3,4) | 即rgba |
linestyle属性常见取值
标识 | 含义 | 标识 | 含义 | 标识 | 含义 | 标识 | 含义 |
---|---|---|---|---|---|---|---|
‘-‘ | solid line style | ‘–’ | dashed line style | ‘-.’ | dash-dot line style | ‘:’ | dotted line style |
‘.’ | point marker | ‘,’ | pixel marker | ‘<’ | triangle_left marker | ‘>’ | triangle_right marker |
‘1’ | tri_down marker | ‘2’ | tri_up marker | ‘*’ | star marker | ‘_’ | hline marker |
有的时候,我们需要在一张图片上绘制多个图像。
输入下面一段程序:
#coding:utf8
# 绘制子图
import numpy as np
import matplotlib.pyplot as plt
plt.figure()
# 绘制一个子图,一共分为row=2,col=2 ,该子图占第1个位置
plt.subplot(2, 2, 1)
plt.plot([0, 1], [0, 1])
# 绘制一个子图,一共分为row=2,col=2 ,该子图占第2个位置
plt.subplot(2, 2, 2)
plt.plot([0, 1], [2, 2])
plt.subplot(2, 2, 3)
plt.plot([1, 1], [2, 3])
plt.subplot(2, 2, 4)
plt.plot([1, 2], [2, 1])
plt.show()
需要导入gridspec包。
#coding:utf8
# 绘制子图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure()
# 创建一个3*3大小的子图格,该子图的起始位置为(0,0) 列跨度为3 行跨度为1
ax1 = plt.subplot2grid((3,3),(0,0),colspan=3,rowspan=1)
ax1.plot([1,2],[1,2])
ax1.set_title('ax1_title')
# 创建一个3*3大小的子图格,该子图的起始位置为(1,0) 列跨度为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),)
plt.show()
上面说完了如何创建图像和绘制不同style的线,本小节我们看看如何设置图像的坐标轴。
先看下面程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,7,50)
y1= x
plt.figure()
plt.plot(x, y1)
plt.xlim((-1, 2)) # 设置x轴显示的范围
plt.ylim((-2, 3))
plt.xlabel('I am x !') # 设置x轴的label
plt.ylabel('y am I !')
new_ticks = np.linspace(-1, 2 , 10)
plt.xticks(new_ticks) # 设置x轴的刻度
plt.yticks([-2, -1, 0, 1, 2, 3], # 设置y轴的刻度
['level0','level1', # 可以看到坐标轴的刻度可以设置为字符串
r'$ \ mid$', # 这里设置的对应的字符串规则就是常见Latex格式
r'$ \ \alpha $', # 这样的规则很适合在论文表现数据
r'$ \ \pi $',
r'$ \frac{\alpha} {\theta} $']) # 设置一个分子式
plt.show()
输出图像为:
分析一下上面调用的一系列plt方法.
先看一下的一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,7,50)
y1= x
plt.figure()
plt.plot(x, y1)
plt.xlim((-1, 2)) # 设置x轴显示的范围
plt.ylim((-2, 3))
plt.xlabel('I am x !') # 设置x轴的label
plt.ylabel('y am I !')
new_ticks = np.linspace(-1, 2 , 10)
print('new_ticks:',new_ticks)
plt.xticks(new_ticks) # 设置x轴的刻度
plt.yticks([-2, -1, 0, 1, 2, 3], # 设置y轴的刻度
['level0','level1', # 可以看到坐标轴的刻度可以设置为字符串
r'$ \ mid$', # 这里设置的对应的字符串规则就是常见Latex格式
r'$ \ \alpha $', # 这样的规则很适合在论文表现数据
r'$ \ \pi $',
r'$ \frac{\alpha} {\theta} $']) # 设置一个分子式
# gca = 'get current axis'
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',-1)) # 将x轴绑定在y轴的-1的位置 data,outward,axes
ax.spines['left'].set_position(('data',0)) # 将y轴绑定在x轴的0的位置
plt.show()
输出:
如果你对ax.Xaxis.set_ticks_position不是很理解,将上面程序对应部分改为下面的程序:
# gca = 'get current axis'
ax = plt.gca()
ax.xaxis.set_ticks_position('top') # 设置当前坐标的默认x轴
ax.yaxis.set_ticks_position('right') # 设置当前坐标的默认y轴
ax.spines['top'].set_position(('data',-1)) # 将x轴绑定在y轴的-1的位置 data,outward,axes
ax.spines['right'].set_position(('data',0)) # 将y轴绑定在x轴的0的位置
plt.show()
在绘制曲线时候,有时候会因为曲线过多或者过于宽阔,覆盖了坐标轴的刻度(ticks).示意图如下。
又或是如果我们想修改坐标轴的ticks样式,该怎么办?
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-4,5,10)
y1= x
y2 =x**2 -2
plt.figure()
l1, = plt.plot(x, y1)
l2, = plt.plot(x, y2, label='you',linewidth=10)
ax = plt.gca()
plt.xlim((-2, 2)) # 设置x轴显示的范围
plt.ylim((-2, 2))
ax.spines['right'].set_color('none') # 设置右边的坐标轴不显示
ax.spines['top'].set_color('none') # 设置上边的坐标轴不显示
ax.spines['bottom'].set_position(('data',-1)) # 将x轴绑定在y轴的-1的位置
ax.spines['left'].set_position(('data',0)) # 将y轴绑定在x轴的0的位置
# 下面设置x/y轴的ticks属性
for label in ax.get_xticklabels() +ax.get_yticklabels(): # 获取到x/y轴的ticks对象,并设置属性
label.set_fontsize(12)
label.set_bbox(dict(facecolor='r',edgecolor='None',alpha=0.2)) # bbox就是ticks的工作区域
plt.show()
程序输出图像如下:
如果我们在同一张figure上绘制多条曲线,需要对每条曲线做一个标注,就和每条马路都有对应的路牌。曲线标注示意图如下:
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1,5,10)
y1= x
y2 =x**2 -1
plt.figure()
l1, = plt.plot(x, y1)
l2, = plt.plot(x, y2, label='you')
# 如果要对一个数据线做标注,需要使用legend
#plt.legend(handles=[l1,l2]) # plot时候已经标明的label传入legend不需要再配对labels
plt.legend(handles=[l1,], labels=['me',], loc='best')
plt.show()
程序输出图像:
可以注意到:这里应用了一个新的函数pyplot.legend()。这里的**legend翻译过来是图例,不是LOL里面的legend**.
**matplotlib.pyplot.legend(*args, **kwargs)**
在axes对象上放置一个图例。说白了就是为曲线添加一个图例(这需要通过在axes对象上已存在的plot对象来操作),通常在绘制多条曲线时候,我们使用如下方法获取每条曲线的实例,并将实例传到legend中.
lines1, = plt.plot(x,y,label='I am line1')
lines2, = plt.plot(x,z,label='I am line2')
plt.legend() 适用于原曲线已经设定label了
# plt.legend(handles=[lines1,line2],labels=['line1','line2'])
我们可以在legend()函数内配置参数,为所有绘制的曲线统一配置label,还可以设定label的样式.常见的设置属性如下:
属性 | description |
---|---|
loc | 设置图例的显示位置,一般选择’best’,程序会帮你找个合适的位置。默认是:’upper right’,该参数的可选项有 {‘best’, ‘upper right’, ‘upper left’, ‘lower left/right’, ‘right’, ‘center left/right’, ‘lower/upper center’, ‘center’ } |
title | 图例的标题 |
ncol | 图例中每行显示多少个 |
fontsize | 图例的字体大小,可设置为int or float or {‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’} |
facecolor/edgecolor | 背景/边框颜色 |
看下面的程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1,5,10)
y1= x
y2 =x**2 -1
plt.figure()
l1, = plt.plot(x, y1)
l2, = plt.plot(x, y2, label='line2')
plt.legend(handles=[l1,l2], labels=['me','you'], title='I am legend',
loc='lower right',ncol=2, fontsize='large',facecolor='r',edgecolor='b')
plt.show()
上面我们说了如何对图像做图例注解,如果我们需要坐标系内做一些标注,或者是对图上的某些点做解释,该怎么办? 该需求示意图如下:
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,2,10)
y1= x
y2 =x**2
plt.figure()
l1, = plt.plot(x, y1)
l2, = plt.plot(x, y2, label='you')
# 限制坐标轴显示范围
plt.ylim(0,2)
plt.xlim(0,2)
# 找到需要标注的点
y0 = x0 = 1
plt.scatter(x0, y0, s=50, color='b') # 设置大小为50,蓝色
plt.plot([1,1],[0,1],'k--') # 绘制标注点到x轴之间的虚线 'k--'是'black'和'--'简写
plt.plot([0,1],[1,1],'k--') # 绘制标注点到y轴之间的虚线
# 使用annotate进行注解描述
plt.annotate(r'$ k_i=%s $'% y0, # 指定注解内容 可以使用Latex
xy=(x0, y0), xycoords='data', xytext=(+30, -30), # xy为基准坐标 coords设置偏移坐标
textcoords='offset points', fontsize=16, # 设置字体大小
arrowprops=dict(arrowstyle='->', # 设置箭头类型
connectionstyle='arc3,rad=.2'))
# 使用text指定注解
x1 = 0.25
y1 = 1.5
plt.text(x1, y1, # 设置基准坐标
r'$function : y={x_i}^2 $', # 设置显示内容,可以使用Latex
fontdict={'size':18, 'color':'r'}) # 设置字体
plt.show()
输出图像:
上面说了figure,坐标轴设置、图例与注解等,这大多数都是和曲线相关。在许多科学计算中,需要使用到散点图,柱状图等其他不同形式的图形。下面就看看这些图形如果在matplotlib中实现。
使用matplotlib绘制散点图的方法类似于绘制曲线图,关键的在于使用的绘制函数由pyplot.plot()转为pyplot.scatter().看看一个实例就知道怎么整了。
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
n = 1024
X = np.random.normal(0, 1, n) # 散点数据
Y = np.random.normal(0, 1, n)
T = np.arctan2(Y,X) # 设置一个点对应的color值,只是为了好看
plt.scatter(X, Y, s=75, c=T, alpha=0.5) # 绘制散点图,并设置对应的颜色关系
plt.xlim(-1.5,1.5) # 设置x轴显示刻度范围
plt.ylim(-1.5,1.5)
plt.xticks(()) # 设置不显示刻度
plt.yticks(())
plt.show()
输出图像如下:
这里主要看scatter()函数.
matplotlib.pyplot.scatter(x, y, s=None, c=None, marker=None, cmap=None, norm=None,
vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, edgecolors=None,
hold=None, data=None, **kwargs)
绘制一个x和y的散点图。散点的大小由s决定,散点的颜色由c的映射(mapping)决定。
参数 | description |
---|---|
x, y | 要绘制的数据 |
s | 要绘制的点的大小 |
c | color, sequence, or sequence of color, optional, default: ‘b’. 可以为单一颜色,也可以为一组序列,如果为一组序列,配合cmap和norm获得映射颜色关系 |
marker | 绘制点风格 |
cmap,norm | 配置参数c使用 |
vmin, vmax | 配置参数norm完成标准化亮度 |
alpha | 透明度设置 |
linewidths | 线宽 |
verts | 用于构成marker |
edgecolors | 边框色 |
按照散点图的套路,使用matplotlib绘制柱状图的方法是使用的绘制函数为pyplot.bar().下面看一个实例。
输入一下一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
# 产生数据
n = 12
X = np.arange(n)
Y1 = (1-X/float(n)) * np.random.uniform(0.5, 1.0, n)
Y2 = -((1-X/float(n)) * np.random.uniform(0.5, 1.0, n))
# 使用bar函数绘制多个柱状图,并设置不同的颜色
plt.bar(X, Y1, facecolor='#9999ff', edgecolor='white')
plt.bar(X, Y2, facecolor='#ff9999', edgecolor='white')
# 为所有柱状图标注图解 这里使用text来绘制(感觉text方便点)
for x,y in zip(X,Y1): # 使用zip同时传递X,Y1到x,y上
plt.text(x, y+0.05, '%.2f'%y, ha='center',va='bottom') # ha: horizontal alignment
for x,y in zip(X,Y2):
plt.text(x, y-0.05, '%.2f'%y, ha='center',va='top') # ha: horizontal alignment
plt.xlim(-0.5,n) # 设置x轴显示刻度范围
plt.ylim(-1.25,1.25)
plt.xticks(()) # 设置不显示刻度
plt.yticks(())
plt.show()
程序输出图像:
matplotlib.pyplot.bar(left, height, width=0.8, bottom=None, hold=None, data=None, **kwargs)
绘制一个柱状图。
绘制的区域为left, left + width, bottom, bottom + height (left, right, bottom and top edges)
参数 | description |
---|---|
left,height | 需要绘制的序列 |
width,bottom | 绘制的柱状图的矩形区域大小参数 |
color | 矩形区域的颜色 |
edgecolor | 边缘颜色 |
linewidth | 边缘线宽 |
tick_label | 刻度label |
xerr,yerr,ecolor | 和error bar相关 |
align | {‘center’, ‘edge’}, optional bar的对齐方式 |
orientation | {‘vertical’, ‘horizontal’}, optional bars的方向 |
使用matplotlib绘制饼状图的方法是使用的绘制函数为pyplot.pie().下面看一个实例。
输入下面一段程序:
#coding:utf8
import numpy as np
import matplotlib.pyplot as plt
X = [1,2,3,4]
plt.pie(X, # 要绘制的数据
labels=['me','you','cat','dog'], # 输入数据对应的labels
explode=(0.2,0,0,0), # 每个部分离中心点的距离
#shadow=True, # 饼状图是否有阴影
autopct='percent:%1.1f%%', # 每个部分所占的比例标签 支持字符串格式
pctdistance=0.6, # 每个部分所占比例的标签离中心点距离
labeldistance=1.2, # 每个部分labels离中心点的距离
radius = 1.2, # 饼状图的半径
startangle=90) # 第一个部分在饼状图上的起始角
plt.axis('equal') # 防止饼状图被压缩成椭圆
plt.show()
程序输出图像:
matplotlib.pyplot.pie(x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, wedgeprops=None, textprops=None, center=(0, 0), frame=False, hold=None, data=None)
参数 | description |
---|---|
x | 要绘制成饼状图的数据 |
explode | 设置每个部分离中点的偏移距离 |
labels | 每个部分设置的labels |
colors | 每个部分设置颜色 |
autopct | 对每个部分所占的比例进行描述,接受的一个字符串格式,也可以是一个Latex公式 |
pctdistance | 设置的pct描述离中心点的距离 |
shadow | 饼状图是否有阴影 |
labeldistance | 每个部分设置的labels离中心点的距离 |
startangle | 绘制的第一个部分在饼状图上的起始角 |
radius | 饼状图的半径大小 |
使用matplotlib可以绘制等高线,参考下面的例子。
输入下面一段程序:
#coding:utf8
# 等高线
import numpy as np
import matplotlib.pyplot as plt
def calcHigh(x, y):
''' 计算高度值 '''
return (1-x/2+ x**5 + y**3 ) * np.exp(-x**2-y**2)
n = 256
x = np.linspace(-3, 3, n)
y = np.linspace(-3, 3, n)
X, Y = np.meshgrid(x,y) # 将xy放置网格中
# 使用contourf填充等高线内颜色
# 颜色映射使用hot 使用8表示分为10部分 0表示2部分
plt.contourf(X, Y, calcHigh(X, Y), 8, alpha=0.75, cmap=plt.cm.hot)
#plt.contourf(X, Y, calcHigh(X, Y), 8, alpha=0.75, cmap=plt.cm.hot) # 颜色映射使用cold
# 使用contour函数绘制等高线
C = plt.contour(X, Y, calcHigh(X, Y), 8, colors='k', linewidth=0.5)
# 使用clabel对等高线做label描述
plt.clabel(C, inline=True, fontsize=10)
#plt.xticks(()) # 隐藏刻度
#plt.yticks(())
plt.show()
输出图像为:
matplotlib.pyplot.contourf(*args, **kwargs)
绘制一个等高线图。使用contour()绘制等高线 and contourf() 填充等高区。
X and Y must both be 2-D with the same shape as Z, or they must both be 1-D such that len(X) is the number of columns in Z and len(Y) is the number of rows in Z
参数 | description |
---|---|
X,Y,Z | 绘制的数据和高度值 |
colors | 颜色 |
alpha | 透明度 |
cmap | 颜色映射关系,有plt.cmp.hot/cold等 |
说了很长时间的二位图像了,matplotlib可以绘制3D图像,这需要引入一个新的模块Axes3D.
输入下面一段程序:
#coding:utf8
# 绘制3D图像
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = Axes3D(fig) # 创建一个三维的ax对象
# 创建数据
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X,Y)
Z = np.sin(np.sqrt(X**2 + Y**2))
# 将数据绘制到坐标轴上 颜色映射为rainbow
ax.plot_surface(X, Y, Z,
rstride=1, cstride=1, cmap=plt.get_cmap('rainbow'))
# 绘制等高线,选择的绘制方向是z轴视角
ax.contourf(X,Y,Z,zdir='z',offset=-2,cmap='rainbow')
ax.set_zlim(-2, 2)
fig.colorbar(suf,shrink=0.5) # 添加颜色bar
plt.show()
输出图像为:
Axes3D.plot_surface(X, Y, Z, *args, **kwargs)
创建一个surface图,支持使用cmap. 使用rstride and cstride参数决定了图像分割的尺度。
参数 | description |
---|---|
X, Y, Z | Data values as 2D arrays |
rstride/cstride | Array row stride (step size)/ Array column stride (step size) |
rcount/ccount | Use at most this many rows, defaults to 50 Use at most this many columns, defaults to 50 |
color | Color of the surface patches |
cmap | A colormap for the surface patches. |
facecolors | Face colors for the individual patches |
norm/vmin/vmax | An instance of Normalize to map values to colors |
shade | Whether to shade the facecolors |
前面我们讲了一堆如何使用matplotlib,这些操作应付一般的绘图任务是没问题的,但是如果想深入了解并系统的学习下Matplotlib,可以看看下面的内容。
学过Java中的Swing、VC中的MFC、Qt的小伙伴们对画布、容器这些词应该不陌生,在Matplotlib同样也有自己的一套对象逻辑。
本节内容参考Vamei-绘图: matplotlib核心剖析。
先看下面的例子:
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
fig = Figure()
canvas= FigureCanvas(fig)
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
line, = ax.plot([0,1], [0,1])
ax.set_title("a straight line (OO)")
ax.set_xlabel("x value")
ax.set_ylabel("y value")
canvas.print_figure('demo.jpg')
print(fig.__class__.__name__)
print(canvas.__class__.__name__)
print(ax.__class__.__name__)
print(line.__class__.__name__)
'''
输出:
Figure
FigureCanvasAgg
Axes
Line2D
'''
运行程序,输出下面的demo.jpg图:
在上面的程序,我们声明了Figure对象,FigureCanvas对象,还有ax对象和line对象。这分别属于Figure类,FigureCanvas类,Axes类和Line2D类。这几个对象之间有啥关系呢?
我们先来看什么是Figure和Axes对象。在matplotlib中,整个图像为一个Figure对象。在Figure对象中可以包含一个,或者多个Axes对象。每个Axes对象都是一个拥有自己坐标系统的绘图区域。其逻辑关系如下
转过头来看demo.jpg,整个图像是fig对象。我们的绘图中只有一个坐标系区域,也就是只有一个ax对象。此外还有以下对象。(括号中表示对象的基本类型)
Title为标题。Axis为坐标轴,Label为坐标轴标注。Tick为刻度线,Tick Label为刻度注释。各个对象之间有下面的对象隶属关系:
虽然Data是数据绘图的关键部分,也就是数据本身的图形化显示,但是必须要有xaxis, yaxis, title一起,才能真正构成一个绘图区域axes。一个单纯的,无法读出刻度的线是没有意义的。xaxis, yaxis, title合起来构成了数据的辅助部分(data guide)。
上面元素又包含有多种图形元素。比如说,我们的data对象是一条线(Line2D)。title, tick label和label都是文本(Text),而tick是由短线(Line 2D)和tick label构成,xaxis由坐标轴的线和tick以及label构成,ax由xaxis, yaxis, title, data构成,ax自身又构成了fig的一部分。上面的每个对象,无论是Line2D, Text还是fig,它们都来自于一个叫做Artist的基类。
OO绘图的原程序还有一个canvas对象。它代表了真正进行绘图的后端(backend)。Artist只是在程序逻辑上的绘图,它必须连接后端绘图程序才能真正在屏幕上绘制出来(或者保存为文件)。我们可以将canvas理解为绘图的物理(或者说硬件)实现。
在OO绘图程序中,我们并没有真正看到title, tick, tick label, xaxis, yaxis对象,而是使用ax.set_*的方法间接设置了这些对象。但这些对象是真实存在的,你可以从上层对象中找到其“真身”。比如,fig.axes[0].xaxis就是我们上面途中的xaxis对象。我们可以通过fig -> axes[0] (也就是ax) -> xaxis的顺序找到它。因此,重复我们刚才已经说过的,一个fig就构成了一个完整的图像。对于每个Artist类的对象,都有findobj()方法,来显示该对象所包含的所有下层对象。
坐标是计算机绘图的基础。计算机屏幕是由一个个像素点构成的。想要在屏幕上显示图像,计算机必须告诉屏幕每个像素点上显示什么。所以,最贴近硬件的坐标体系是以像素为单位的坐标体系。我们可以通过具体说明像素位置来标明显示器上的某一点。这叫做显示坐标(display coordinate),以像素为单位。
然而,像素坐标不容易被纳入绘图逻辑。相同的程序,在不同的显示器上就要调整像素值,以保证图像不变形。所以一般情况下,还会有图像坐标和数据坐标。
图像坐标将一张图的左下角视为原点,将图像的x方向和y方向总长度都看做1。x方向的0.2就是指20%的图像在x方向的总长,y方向0.8的长度指80%的y方向总长。(0.5, 0.5)是图像的中点,(1, 1)指图像的右上角。比如下面的程序,我们在使用add_axes时,传递的参数中,前两个元素为axes的左下角在fig的图像坐标上的位置,后两个元素指axes在fig的图像坐标上x方向和y方向的长度。fig的图像坐标称为Figure坐标,储存在为fig.transFigure
(类似的,每个axes,比如ax1,有属于自己的图像坐标。它以ax1绘图区域总长作为1,称为Axes坐标。也就是ax1.transAxes。(0.5, 0.5)就表示在Axes的中心。Axes坐标和Figure坐标原理相似,只是所用的基准区域不同。)
# object-oriented plot
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
fig = Figure()
canvas = FigureCanvas(fig)
# first axes
ax1 = fig.add_axes([0.1, 0.1, 0.2, 0.2])
line, = ax1.plot([0,1], [0,1])
ax1.set_title("ax1")
# second axes
ax2 = fig.add_axes([0.4, 0.3, 0.4, 0.5])
sca = ax2.scatter([1,3,5],[2,1,2])
ax2.set_title("ax2")
canvas.print_figure('demo.jpg')
我们在绘图,比如使用plot的时候,绘制了两点间的连线。这两点分别为(0, 0)和(1, 1)。(plot中的第一个表为两个x坐标,第二个表为两个y坐标)。这时使用的坐标系为数据坐标系(ax1.transData)。我们可以通过绘出的坐标轴读出数据坐标的位置。
如果绘制的是具体数据,那么数据坐标符合我们的需求。如果绘制的是标题这样的附加信息,那么Axes坐标符合符合我们的需求。如果是整个图像的注解,那么Figure坐标更符合需求。每一个Artist对象都有一个transform属性,用于查询和改变所使用的坐标系统。如果为显示坐标,transform属性为None。
莫烦Pyton教程莫烦教程主页
作者:Vamei 出处:http://www.cnblogs.com/vamei