Matplotlib可能是Python2D-绘图领域使用最广泛的套件。它能让使用者很轻松地将数据图形化,并且提供多样化的输出格式。这里将会探索matplotlib的常见用法。
IPython是Python的一个增强版本。它在下列方面有所增强:命名输入输出、使用系统命令(shellcommands)、排错(debug)能力。我们在命令行终端给IPython加上参数-pylab(0.12以后的版本是--pylab)之后,就可以像Matlab或者Mathematica那样以交互的方式绘图。
pylab是matplotlib面向对象绘图库的一个接口。它的语法和Matlab十分相近。也就是说,它主要的绘图命令和Matlab对应的命令有相似的参数。
下载包包含两个目录:
这一节中,我们将从简到繁:先尝试用默认配置在同一张图上绘制正弦和余弦函数图像,然后逐步美化它。
第一步,是取得正弦函数和余弦函数的值:
frompylabimport*X=np.linspace(-np.pi,np.pi,256,endpoint=True)C,S=np.cos(X),np.sin(X)X是一个numpy数组,包含了从π到+π等间隔的256个值。C和S则分别是这256个值对应的余弦和正弦函数值组成的numpy数组。
你可以在IPython的交互模式下测试代码,也可以下载代码(下载链接就是这些示例图),然后执行:
importnumpyasnpimportmatplotlib.pyplotaspltX=np.linspace(-np.pi,np.pi,256,endpoint=True)C,S=np.cos(X),np.sin(X)plt.plot(X,C)plt.plot(X,S)plt.show()
下面的代码中,我们展现了matplotlib的默认配置并辅以注释说明,这部分配置包含了有关绘图样式的所有配置。代码中的配置与默认配置完全相同,你可以在交互模式中修改其中的值来观察效果。
#导入matplotlib的所有内容(nympy可以用np这个名字来使用)frompylabimport*#创建一个8*6点(point)的图,并设置分辨率为80figure(figsize=(8,6),dpi=80)#创建一个新的1*1的子图,接下来的图样绘制在其中的第1块(也是唯一的一块)subplot(1,1,1)X=np.linspace(-np.pi,np.pi,256,endpoint=True)C,S=np.cos(X),np.sin(X)#绘制余弦曲线,使用蓝色的、连续的、宽度为1(像素)的线条plot(X,C,color="blue",linewidth=1.0,linestyle="-")#绘制正弦曲线,使用绿色的、连续的、宽度为1(像素)的线条plot(X,S,color="green",linewidth=1.0,linestyle="-")#设置横轴的上下限xlim(-4.0,4.0)#设置横轴记号xticks(np.linspace(-4,4,9,endpoint=True))#设置纵轴的上下限ylim(-1.0,1.0)#设置纵轴记号yticks(np.linspace(-1,1,5,endpoint=True))#以分辨率72来保存图片#savefig("exercice_2.png",dpi=72)#在屏幕上显示show()
首先,我们以蓝色和红色分别表示余弦和正弦函数,而后将线条变粗一点。接下来,我们在水平方向拉伸一下整个图。
...figure(figsize=(10,6),dpi=80)plot(X,C,color="blue",linewidth=2.5,linestyle="-")plot(X,S,color="red",linewidth=2.5,linestyle="-")...
当前的图片边界设置得不好,所以有些地方看得不是很清楚。
更好的方式是这样:
xmin,xmax=X.min(),X.max()ymin,ymax=Y.min(),Y.max()dx=(xmax-xmin)*0.2dy=(ymax-ymin)*0.2xlim(xmin-dx,xmax+dx)ylim(ymin-dy,ymax+dy)
我们讨论正弦和余弦函数的时候,通常希望知道函数在±π和±π2的值。这样看来,当前的设置就不那么理想了。
xticks([-np.pi,-np.pi/2,0,np.pi/2,np.pi])yticks([-1,0,+1])...
记号现在没问题了,不过标签却不大符合期望。我们可以把3.142当做是π,但毕竟不够精确。当我们设置记号的时候,我们可以同时设置记号的标签。注意这里使用了LaTeX。
...xticks([-np.pi,-np.pi/2,0,np.pi/2,np.pi],[r'$-\pi$',r'$-\pi/2$',r'$0$',r'$+\pi/2$',r'$+\pi$'])yticks([-1,0,+1],[r'$-1$',r'$0$',r'$+1$'])...
坐标轴线和上面的记号连在一起就形成了脊柱(Spines,一条线段上有一系列的凸起,是不是很像脊柱骨啊~),它记录了数据区域的范围。它们可以放在任意位置,不过至今为止,我们都把它放在图的四边。
实际上每幅图有四条脊柱(上下左右),为了将脊柱放在图的中间,我们必须将其中的两条(上和右)设置为无色,然后调整剩下的两条到合适的位置——数据空间的0点。
...ax=gca()ax.spines['right'].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))...
我们在图的左上角添加一个图例。为此,我们只需要在plot函数里以「键-值」的形式增加一个参数。
...plot(X,C,color="blue",linewidth=2.5,linestyle="-",label="cosine")plot(X,S,color="red",linewidth=2.5,linestyle="-",label="sine")legend(loc='upperleft')...
我们希望在2π/3的位置给两条函数曲线加上一个注释。首先,我们在对应的函数图像位置上画一个点;然后,向横轴引一条垂线,以虚线标记;最后,写上标签。
...t=2*np.pi/3plot([t,t],[0,np.cos(t)],color='blue',linewidth=2.5,linestyle="--")scatter([t,],[np.cos(t),],50,color='blue')annotate(r'$\sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$',xy=(t,np.sin(t)),xycoords='data',xytext=(+10,+30),textcoords='offsetpoints',fontsize=16,arrowprops=dict(arrowstyle="->",connectionstyle="arc3,rad=.2"))plot([t,t],[0,np.sin(t)],color='red',linewidth=2.5,linestyle="--")scatter([t,],[np.sin(t),],50,color='red')annotate(r'$\cos(\frac{2\pi}{3})=-\frac{1}{2}$',xy=(t,np.cos(t)),xycoords='data',xytext=(-90,-50),textcoords='offsetpoints',fontsize=16,arrowprops=dict(arrowstyle="->",connectionstyle="arc3,rad=.2"))...
坐标轴上的记号标签被曲线挡住了,作为强迫症患者(雾)这是不能忍的。我们可以把它们放大,然后添加一个白色的半透明底色。这样可以保证标签和曲线同时可见。
...forlabelinax.get_xticklabels()+ax.get_yticklabels():label.set_fontsize(16)label.set_bbox(dict(facecolor='white',edgecolor='None',alpha=0.65))...
到目前为止,我们都用隐式的方法来绘制图像和坐标轴。快速绘图中,这是很方便的。我们也可以显式地控制图像、子图、坐标轴。Matplotlib中的「图像」指的是用户界面看到的整个窗口内容。在图像里面有所谓「子图」。子图的位置是由坐标网格确定的,而「坐标轴」却不受此限制,可以放在图像的任意位置。我们已经隐式地使用过图像和子图:当我们调用plot函数的时候,matplotlib调用gca()函数以及gcf()函数来获取当前的坐标轴和图像;如果无法获取图像,则会调用figure()函数来创建一个——严格地说,是用subplot(1,1,1)创建一个只有一个子图的图像。
所谓「图像」就是GUI里以「Figure#」为标题的那些窗口。图像编号从1开始,与MATLAB的风格一致,而于Python从0开始编号的风格不同。以下参数是图像的属性:
这些默认值可以在源文件中指明。不过除了图像数量这个参数,其余的参数都很少修改。
你在图形界面中可以按下右上角的X来关闭窗口(OSX系统是左上角)。Matplotlib也提供了名为close的函数来关闭这个窗口。close函数的具体行为取决于你提供的参数:
和其他对象一样,你可以使用setp或者是set_something这样的方法来设置图像的属性。
你可以用子图来将图样(plot)放在均匀的坐标网格中。用subplot函数的时候,你需要指明网格的行列数量,以及你希望将图样放在哪一个网格区域中。此外,gridspec的功能更强大,你也可以选择它来实现这个功能。
坐标轴和子图功能类似,不过它可以放在图像的任意位置。因此,如果你希望在一副图中绘制一个小图,就可以用这个功能。
良好的记号是图像的重要组成部分。Matplotlib里的记号系统里的各个细节都是可以由用户个性化配置的。你可以用TickLocators来指定在那些位置放置记号,用TickFormatters来调整记号的样式。主要和次要的记号可以以不同的方式呈现。默认情况下,每一个次要的记号都是隐藏的,也就是说,默认情况下的次要记号列表是空的——NullLocator。
下面有为不同需求设计的一些Locators。
这些Locators都是matplotlib.ticker.Locator的子类,你可以据此定义自己的Locator。以日期为ticks特别复杂,因此Matplotlib提供了matplotlib.dates来实现这一功能。
接下来的内容是练习。请运用你学到的知识,从提供的代码开始,实现配图所示的效果。具体的答案可以点击配图下载。
frompylabimport*n=256X=np.linspace(-np.pi,np.pi,n,endpoint=True)Y=np.sin(2*X)plot(X,Y+1,color='blue',alpha=1.00)plot(X,Y-1,color='blue',alpha=1.00)show()
frompylabimport*n=1024X=np.random.normal(0,1,n)Y=np.random.normal(0,1,n)scatter(X,Y)show()
frompylabimport*n=12X=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(X,+Y1,facecolor='#9999ff',edgecolor='white')bar(X,-Y2,facecolor='#ff9999',edgecolor='white')forx,yinzip(X,Y1):text(x+0.4,y+0.05,'%.2f'%y,ha='center',va='bottom')ylim(-1.25,+1.25)show()
frompylabimport*deff(x,y):return(1-x/2+x**5+y**3)*np.exp(-x**2-y**2)n=256x=np.linspace(-3,3,n)y=np.linspace(-3,3,n)X,Y=np.meshgrid(x,y)contourf(X,Y,f(X,Y),8,alpha=.75,cmap='jet')C=contour(X,Y,f(X,Y),8,colors='black',linewidth=.5)show()
frompylabimport*deff(x,y):return(1-x/2+x**5+y**3)*np.exp(-x**2-y**2)n=10x=np.linspace(-3,3,4*n)y=np.linspace(-3,3,3*n)X,Y=np.meshgrid(x,y)imshow(f(X,Y)),show()
frompylabimport*n=20Z=np.random.uniform(0,1,n)pie(Z),show()
frompylabimport*n=8X,Y=np.mgrid[0:n,0:n]quiver(X,Y),show()
frompylabimport*axes=gca()axes.set_xlim(0,4)axes.set_ylim(0,3)axes.set_xticklabels([])axes.set_yticklabels([])show()
frompylabimport*subplot(2,2,1)subplot(2,2,3)subplot(2,2,4)show()
frompylabimport*axes([0,0,1,1])N=20theta=np.arange(0.0,2*np.pi,2*np.pi/N)radii=10*np.random.rand(N)width=np.pi/4*np.random.rand(N)bars=bar(theta,radii,width=width,bottom=0.0)forr,barinzip(radii,bars):bar.set_facecolor(cm.jet(r/10.))bar.set_alpha(0.5)show()
frompylabimport*frommpl_toolkits.mplot3dimportAxes3Dfig=figure()ax=Axes3D(fig)X=np.arange(-4,4,0.25)Y=np.arange(-4,4,0.25)X,Y=np.meshgrid(X,Y)R=np.sqrt(X**2+Y**2)Z=np.sin(R)ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap='hot')show()
importnumpyasnpimportmatplotlib.pyplotasplteqs=[]eqs.append((r"$W^{3\beta}_{\delta_1\rho_1\sigma_2}=U^{3\beta}_{\delta_1\rho_1}+\frac{1}{8\pi2}\int^{\alpha_2}_{\alpha_2}d\alpha^\prime_2\left[\frac{U^{2\beta}_{\delta_1\rho_1}-\alpha^\prime_2U^{1\beta}_{\rho_1\sigma_2}}{U^{0\beta}_{\rho_1\sigma_2}}\right]$"))eqs.append((r"$\frac{d\rho}{dt}+\rho\vec{v}\cdot\nabla\vec{v}=-\nablap+\mu\nabla^2\vec{v}+\rho\vec{g}$"))eqs.append((r"$\int_{-\infty}^\inftye^{-x^2}dx=\sqrt{\pi}$"))eqs.append((r"$E=mc^2=\sqrt{{m_0}^2c^4+p^2c^2}$"))eqs.append((r"$F_G=G\frac{m_1m_2}{r^2}$"))plt.axes([0.025,0.025,0.95,0.95])foriinrange(24):index=np.random.randint(0,len(eqs))eq=eqs[index]size=np.random.uniform(12,32)x,y=np.random.uniform(0,1,2)alpha=np.random.uniform(0.25,.75)plt.text(x,y,eq,ha='center',va='center',color="#11557c",alpha=alpha,transform=plt.gca().transAxes,fontsize=size,clip_on=True)plt.xticks([]),plt.yticks([])#savefig('../figures/text_ex.png',dpi=48)plt.show()