【Matplotlib】利用Python进行绘图

【Matplotlib】

  教程:https://morvanzhou.github.io/tutorials/data-manipulation/plt/

  官方文档:https://matplotlib.org/api/pyplot_summary.html

  这个模块是一个Python上用于进行绘图的模块。做科研的人经常会使用的matlab就是这个模块的一个竞品。就我个人而言,matplotlib感觉更像是一个echarts.js之类前端库的一个后端版本。和echarts一样,它可以支持通过python画出散点图,条状图,饼状图甚至是3D图和动画。

  由于靠这个画图的话一般都会遇到比较复杂的数据处理,所以numpy也经常和matplotlib一起出现一起使用。

  ■  基本安装和使用

  在linux上安装可以通过包管理工具比如yum(我的虚拟机里面已经安装了matplotlib,但是已经忘了当时是用pip还是yum装的了…)

  windows的话目测用pip也可以一步搞定。如果不行可以参考上面给出的参考文档连接。

  下面是一个matplotlib的helloworld

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-1,1,50)
y = 2*x + 1

plt.figure()
plt.plot(x,y)
plt.show()

 

  如果是在windows下的某个脚本中运行这段程序,那么完了之后会跳出一个窗口,显示一幅图片如图所示:

  可以看到这个显示的图的组件本身还有一些功能比如跟随鼠标显示坐标值,放大局部图像等。

  然后我们来看看这段代码。首先x是numpy中的一个array对象,其内容是-1到1之间均等地取了大约50个值。这些值其实就是我们图中后来的横坐标。然后通过array加减乘除时特别的性质,可以将类似于函数表达式那样的式子写在代码里。即2*x + 1,很接近函数表达式的y=2x+1了。那么y也是一个array,一一对应x中各个值进行2*x+1后的值。这样的一个array,x和array y,两个坐标集组成了一幅图。那么如何将这幅图显示出来,用到的就是后三行代码。

  这里需要注意的是,绘制这个图的过程其实是用直线将各个点之间连接起来的操作。由于这个图最终本身就是一个直线,所以看起来似乎最初x的样本量取50个点和两个点结果都一样。但是如果表达式换成x**2 + 1那么取点多少就会影响到这个二次函数图是否光滑了。下面是分别取10个点和50个点时二次函数的图。

 

 (10个点)    (50个点)

 

  ■  图像(plot)的控制

  ●  线条的控制

  上面代码中的plt.figure()方法声明的其实是一个figure窗口,即windows中弹出来的这个窗口。这个窗口中可以包含很多个图。上面只是添加了一个图。实际上还可以尝试添加更多。

  一个图的概念其实在这里叫plot,正如你所见,plot方法用于向一个figure窗口中添加一个图。

x = np.linspace(-10,10,50)

y1 = 2*x + 1
y2 = x**2

plt.figure()
plt.plot(x,y1)
plt.plot(x,y2)
plt.show()

  因为出现了两个图,matplotlib自动为两个图设置了不同的颜色以示区分。如果想要自定义颜色,线条样式等等属性的话,可以将这些参数写在plot方法中比如:

  plot(x,y,linestyle='dashed',linewidth=0.5,color='#3479f7')  其余更多参数以及参数如何取值可以参考https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot

 

  ●  坐标轴控制

  上面的所有图中,坐标轴都是根据我们给出的数据范围自动生成的。如果想要手动指定坐标轴的范围呢

  使用plt.xlim/ylim两个方法可以分别设置x轴和y轴的范围。用法就是xlim(min,max)。使用plt.xlabel和ylabel可以设置x/y轴的说明文字。使用plt.xticks/yticks两个方法可以分别设置X和Y轴的坐标点的值是多少。参数的话是numpy.linspace的返回值。

  比如沿用上面用到的y1和y2,进行这样的代码画图:

plt.figure()
plt.plot(x,y1)
plt.plot(x,y2,linestyle='dashed',linewidth=0.5,color='red',marker='.',)
plt.xlim(1,3)
plt.xlabel('value of X')
plt.xticks(np.linspace(1,3,11))
plt.show()

  需要额外注意的一点是,加入xlim和xticks互相冲突了,那么最终显示的图是以后设置者为准。比如这里xticks再xlim后面设置,那么最终画出的图以xticks中定的范围为准。

  除了简单的数值设置外,xticks/yticks还支持label设置。比如xticks([1,2,3,4],['bad','ok','good','verygood'])这样的方式,来将数值坐标值和文字描述的坐标值一一对应起来。

  plt.gca()  这个方法返回一个包含所有坐标轴的对象。gca的全称估计是get current axes,也就是获取到当前所有坐标轴。

  其中比较重要的一条属性是spines,这个属性是一个字典,可以有left,right,top,bottom的取值分别对应一个图的四条边。比如令ax = plt.gca(),然后

  ax.spines['top'].set_color('none') 以及 ax['right'].set_color('none')  这样子的话那么就可以让显示出来的图没有上边框和右边框。

  除了set_color,还可以set_position。比如ax.spines['bottom'].set_position(('data',0)),首先bottom指的是X轴的那个边,把它set_position到data是0,即Y轴的值为0的高度。类似的将left也设置一下。然后将top和right设置不可见之后,这样就得到一个经典的坐标系的图了。

 

  上面说的是plt.gca().spines这个属性。其实gca返回的对象中还有一些属性比如xaxis/yaxis这两个就是坐标轴的对象(spines的本意是脊柱、干,所以指的更多是构成坐标轴的那条线。而xaxis这种很明显就是指坐标轴本身,指的更多是坐标轴的数字、文字标签的部分。)

  同样的,令 ax = plt.gca() 的话,ax.xaxis就是X轴对象,可以有下列方法:

  ax.xaxis.set_ticks_position('top')  可以设置X轴的数字和坐标点出现在图的哪条边上。如果是top就是出现在上边。如果配合spines['top'].set_color('none')的话,那么就可以得到只有数据标点(且在图上方位置),没有轴线的图了。

  

  ●  图例

  在上面画两条线的方法plot中,还可以添加一个参数label,用来通过文字说明这条线的内容。有了这个label之后就可以将其关联到图例上。这样图例就可以显示说明。

  关联图例的方法是plt.legend,不过需要注意的是legend方法一定要在plot完成之后调用,否则将无法呈现图例(这其实呼应了之前xlim和xticks取值的后设置后决定,这强调了plt对于图像绘制是线性执行的特点)。比如如下代码:

plt.figure()
plt.plot(x,y1,label='straight line')
plt.plot(x,y2,linestyle='dashed',linewidth=0.5,color='red',marker='.',label='square line')
plt.legend()
plt.ylim(-1,4)
plt.xlim(-3,3)
ax = plt.gca()
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.show()

  图例出现的位置默认是在左下方,可以在legend方法中用loc参数指明具体的位置。比如plt.legend(loc='upper right')是出现在右上角。类似的参数值还有lower right,center left等等。此外还有一个best,这个值是说把位置自动交给电脑去判断,尽量选择一个不挡住任何东西的好的位置。

 

  ●  划线标点 标注

  上面说过,plot方法其实是将给出的参数分别解释为X坐标和Y坐标的集合,然后一一将这些点用直线连接起来组成的图形。那么要在图上额外画一条线段就不难了。

  比如plt.plot([1,1],[0,2],linestyle='dashed')这个画的线段就是(1,0)点和(1,2)点之间的连线,就是一条垂直于X轴的线。

  至于标点,其实用的是用来做散点图的plt.scatter方法。由于散点图后面还会详细说这里就不多说了。这里只给出一个调用方法:

  plt.scatter([1,],[2,],s=20,color='blue')

  

  除了标点,另外一个重要的标注就是文字标注了。文字标注大概可以通过这样的方法来实现:

  plt.annotate('emphasize point',xy=(1,3),xytext=(15,-15),textcoords='offset pixels',arrowprops=dict(arrowstyle='<|-', connectionstyle="arc3,rad=.2"))

  emphasize point是描述文字。xy则是本条标注针对的图中的点。xytext配合textcoords指出的定位方式,指出了标注文字应该放在什么地方,如果按照上面的这种组合的话,就是说文字的左上角点是标注点的右下方各15个像素的地方。arrowprops参数比较有意思,它指出了是否需要一个标注文字和标注点之间的箭头标识。arrowstyle可以有<-,<|-,->,-|>,是不是很形象? 当箭头指向左边的意思是,图上的箭头是从标注点指向标注文字的,向右则相反。connectionstyle是一个完整的字符串,不过它也有很多信息,比如rad指出了箭头线的弧度。

  以上是对于针对某个点的标注文字,如没有特定的目标点,只是想写点文字的话可以使用plt.text方法

  plt.text(-2.4, 6, 'This is some text',fontdict={'size': 16, 'color': 'r'})

  这个方法中(-2.4,6)这个点是文字左上角的坐标,fontdict指定一个指定字体格式的字典。比较好懂就不说了。

  总结上面说到的一些东西再给一个示例:

plt.figure()
plt.plot(x,y1,label='straight line')
plt.plot(x[:35],y2[:35],linestyle='dashed',linewidth=0.5,color='red',label='square line')
plt.plot([1,1],[0,3],'k--')
plt.scatter([1,],[3,],s=20,color='blue')
plt.annotate('emphasize point',xy=(1,3),xytext=(15,-15),textcoords='offset pixels',arrowprops=dict(arrowstyle='<|-', connectionstyle="arc3,rad=.2"))
plt.text(-2.4, 6, 'This is some text',fontdict={'size': 16, 'color': 'r'})
plt.legend(loc='best')
ax = plt.gca()
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.show()
View Code

 

  

  

 

   ■  分图类型示例

  ●  散点图

n = 1024    # data size
X = np.random.normal(0, 1, n) # 每一个点的X值
Y = np.random.normal(0, 1, n) # 每一个点的Y值
T = np.arctan2(Y,X) # for color value

plt.figure()
ax = plt.gca()
ax.spines['bottom'].set_position(('data',0))
ax.spines['left'].set_position(('data',0))
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
plt.scatter(X,Y,c=T,s=20)
plt.xlim(-0.5,0.5)
plt.show()

 

   np.random.normal方法是生成一个平均值为0,方差为1,总数为1024的数据集。这样两个数据集分别作为X坐标和Y坐标构成了1024个点的集合。再通过调用arctan2将所有随机点对应的一个弧度求出,把这个弧度当做是一个颜色的替代值。

  随后就是生成图形的过程,对于gca返回的处理部分是把上右边去掉,下左边移到中间形成坐标系的图。然后scatter方法一次性将所有的点画到图上。X是横坐标的array,Y是纵坐标的array,c是color指定颜色的值,s是每个点的size。最后为了方便看(由于散点图在X和Y轴上都符合正则分布,导致原点附近的点特别密集,如果scale比较大中间就是一片糊),limit一下X轴的坐标。

  最终看到的图形是这样的:

  ●  其他图种,都是依葫芦画瓢的事,就不多说了,可以网上随便搜一个看一看就行了。

 

■  多图合一显示

  上面的所有实例中,plt.figure()得到的窗口对象里面都只显示了一幅图,图种可能有多条线。

  如果要多张图显示在同一个窗口中,那么需要用到plt.subplot这个方法。这个方法的调用方式通常是这样的:

  plt.subplot(2,2,1)或者plt.subplot(221)。221的意思是,如果将整个窗口分隔成2*2格式的话,那么现在切换plt到从左到右从上至下第1个网格内进行编辑。直到再次调用subplot切换网格之前,plt做的所有操作都会这个网格内进行。

  matplotlib严格按照代码中画图的顺序来画图,如果后来画的图和前面的图有重叠,那么前面的图就会被整个删除,接下来界面上就会只剩下后来画的图了。

  另外subplot并不要求前后调用的几次都是统一规格网格的图,比如下面这段代码显示出的图是这样的:

def dicars(plt):
    ax = plt.gca()
    ax.spines['top'].set_color('none')
    ax.spines['right'].set_color('none')
    ax.spines['left'].set_position(('data',0))
    ax.spines['bottom'].set_position(('data',0))

plt.figure()
# 第一幅图
plt.subplot(221)
plt.plot([1,1],[0,1])
dicars(plt)
# 第二幅图
plt.subplot(222)
plt.plot([2,2],[0,1])
dicars(plt)
# 第三幅图
plt.subplot(212)
plt.plot([3,3],[0,1])
dicars(plt)

plt.show()

  另外进行画图的布局还可以用grid方式的流式布局。参考https://morvanzhou.github.io/tutorials/data-manipulation/plt/4-2-subplot2/

  多图布局除了同一窗口多个图外,还可以有图中图这种模式化,也可以参考上面链接中的教程。

  

■  多坐标轴显示

  多坐标轴也是一种常见的需求。常见的就是需要在相对小的区域分显示两个值域出入比较大的函数的时候,即多坐标轴多指多Y轴的情况。

  matplotlib实现多坐标轴的代码如下:

  

import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(-2,2,0.1)
y1 = map(math.exp,x)
y2 = -1 * np.array(y1)

fig, left_ax = plt.subplots()    # 获取当前的坐标,是第二个返回对象
right_ax = left_ax.twinx()    # twinx生成另一个坐标对象
left_ax.plot(x,y1,color='blue')
left_ax.set_ylabel('value of Y1',color='blue')
right_ax.plot(x,y2,color='green')
right_ax.set_ylabel('value of Y2',color='green')
plt.show()

 

  生成图:

 

posted @ 2018-07-05 18:00  K.Takanashi  阅读(32912)  评论(0编辑  收藏  举报