继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Python数据绘图包matplotlib由浅入深解析

慕田峪0738999
关注TA
已关注
手记 344
粉丝 88
获赞 494

数无形时少直觉,直接上代码,代码中有详细解析。

注:关于环境的搭建:安装Python, numpy和matplotlib。(可以到python.org下载Python编译器。相关Python包的安装,请参看Python小技巧)。matplotlib的官网是: http://matplotlib.org/  官网有丰富的图例和文档说明。matplotlib在github的地址为:https://github.com/matplotlib

#! /usr/bin/env/python2
# -*- coding:utf-8 -*-
"""
参考:https://blog.csdn.net/feng98ren/article/details/79392747
      http://www.cnblogs.com/vamei/archive/2013/01/30/2879700.html
"""
from math import *
from numpy import *
import matplotlib.pyplot as plt
"""matplotlib.pyplot中的plt.plot()、plt.figure()、plt.subplot()、plt.show()、plt.subplot().axis(list)、
    savefig("demo.jpg")、title("a strait line")、xlabel("x value")、ylabel("y value")"""

# 1、建立一张空白图
# fig = plt.figure()
# 指定所建立图的大小
# fig = plt.figure(figsize=(4, 3))
# fig.show()

# #2、在figure上建立子图
# plt.figure(figsize=(12, 12))
# # subplot()函数中的三个数字,第一个表示Y轴方向的子图个数,第二个表示X轴方向的子图个数,第三个则表示当前要画图的焦点。
# # plt.subplot(231)
# # plt.subplot(232)
# # plt.subplot(2, 3, 3)
# # 图中的x,y轴坐标默认都是从0到1,当然需要其他的坐标起始值时可以使用语句指定:
# plt.subplot(233).axis([0, 10, 0, 10])
# # plt.subplot(233)
# plt.show()
#
# # 3、指定子图中各个坐标轴的范围
# fig = plt.figure(figsize=(6, 6))
# # 图中的x,y轴坐标默认都是从0到1,当然需要其他的坐标起始值时可以使用语句指定:
# ax1 = fig.add_subplot(221)
# list = [-1, 1, -1, 1]
# ax1.axis(list) # axis() = xmin, xmax, ymin, ymax
# ax2 = fig.add_subplot(222)
# ax3 = fig.add_subplot(223)
# ax4 = fig.add_subplot(224)
# plt.show()

# 4、向空白图中添加内容:
# x = [1, 2, 3, 4, 5, 6, 7]
# y = [4, 3.8, 10, 4.9, 1.2, 7.3, 2.1]

# 画散点图 plt.scatter()方法
# 参数意义:
# x为横坐标向量,y为纵坐标向量。
"""
控制颜色:
color为散点的颜色标志
有四种表示颜色的方式其余三种如下:
    1、颜色名称简写;
    2、用颜色名称全名;
    3、用十六进制表示;
    4、用灰度强度表示(RGBA values should be within 0-1 range)
常用color的表示如下:
b---blue c---cyan(青) g---green k----black m---magenta(洋红) r---red w---white y----yellow
"""

"""
控制标记风格:
marker为散点的标记,标记风格有多种:
.:Point marker
 o:Circle marker
 v :Triangle down marker
 ^ :Triangle up marker
<: Triangle left marker
 >: Triangle right marker
 1 :Tripod down marker
 2 :Tripod up marker
3 :Tripod left marker
4 :Tripod right marker
s: Square marker
p :Pentagon marker
* :Star marker
h: Hexagon marker
H :Rotated hexagon
D: Diamond marker
d :Thin diamond marker
|: Vertical line (vlinesymbol) marker
_ :Horizontal line (hline symbol) marker
+: Plus marker
x: Cross (x) marker
"""
# plt.scatter(x, y, color='m', marker='+')  # scatter()方法会根据坐标值自动定义坐标范围
# plt.scatter(x, y, color='red', marker='+')
# plt.scatter(x, y, color='g', marker='.')
# plt.show()

# 5、画函数图(折线图)
"""
参数linestyle,控制的是线型的格式:符号和线型之间的对应关系:
- 实线 -- 短线 -. 短点相间线 : 虚点线
另外除了给出数据画图之外,也可以利用函数表达式进行画图,例如:y=sin(x)
"""
# fig = plt.figure(figsize=(12, 6))
# plt.subplot(121)
# plt.plot(x, y, color='r', linestyle='-.')
# plt.subplot(122)
# plt.plot(x, y, color='g', linestyle=':')
# plt.show()


"""
def arange(start=None, stop=None, step=None, dtype=None): # known case of numpy.core.multiarray.arange
 Values are generated within the half-open interval ``[start, stop)``, step : Spacing between values.

"""
# x = arange(-math.pi, math.pi, 0.01)
# y = [sin(i) for i in x]
# plt.figure()
# # plot为画线,title为增加标题
# plt.title("stright line")
# plt.plot([0, 1], [0, 1], color='r', linestyle='-')      # plot a line from (0, 0) to (1, 1)
# # plt.plot(x, y, color='r', linestyle='-')
# plt.show()


# 6、扇形图:plt.pie(x)
# x = [2.3, 3.4, 1.2, 6.6, 7.0]
# plt.figure()
# plt.pie(x)
# plt.title('PIE')
# plt.show()

# 7、柱状图bar
# plt.figure()
# plt.bar(x, y)
# plt.title("bar")
# plt.show()

# 2D data
import numpy as np
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z = Y**2 + X**2

plt.subplot(235)
plt.contour(X,Y,Z)
plt.colorbar()
plt.title("contour")
plt.show()

matplotlib是基于Python语言的开源项目,旨在为Python提供一个数据绘图包。我将在这篇文章中介绍matplotlib API的核心对象,并介绍如何使用这些对象来实现绘图。实际上,matplotlib的对象体系严谨而有趣,为使用者提供了巨大的发挥空间。用户在熟悉了核心对象之后,可以轻易的定制图像。matplotlib的对象体系也是计算机图形学的一个优秀范例。即使你不是Python程序员,你也可以从文中了解一些通用的图形绘制原则。

matplotlib使用numpy进行数组运算,并调用一系列其他的Python库来实现硬件交互。matplotlib的核心是一套由对象构成的绘图API。

https://img4.mukewang.com/5ae535a60001ad7104800088.jpg

 

matplotlib项目是由John D. Hunter发起的。John D. Hunter由于癌症于去年过世,但他发为社区作出的无比贡献将永远留存。

https://img2.mukewang.com/5ae535ae0001f45201600153.jpg

John D. Hunter

函数式绘图

matplotlib是受MATLAB的启发构建的。MATLAB是数据绘图领域广泛使用的语言和工具。MATLAB语言是面向过程的。利用函数的调用,MATLAB中可以轻松的利用一行命令来绘制直线,然后再用一系列的函数调整结果。matplotlib有一套完全仿照MATLAB的函数形式的绘图接口,matplotlib.pyplot模块中。这套函数接口方便MATLAB用户过度到matplotlib包。在Python特殊方法与多范式中,我们已经谈到,Python中的函数式编程是通过封装对象实现的。matplotlib中的函数式调用其实也是如此。matplotlib本质上还是构建对象来构建图像。函数式编程将构建对象的过程封装在函数中,从而让我们觉得很方便。

函数式编程创造了一个仿真MATLAB的工作环境,并有许多成形的绘图函数。如果只是作为Matplotlib的一般用户(非开发者),pyplot可以满足大部分的需求。


面向对象编程

尽管函数式绘图很便利,但利用函数式编程会有以下缺点:

1) 增加了一层“函数”调用,降低了效率。

2) 隶属关系被函数掩盖。整个matplotlib包是由一系列有组织有隶属关系的对象构成的。函数掩盖了原有的隶属关系,将事情变得复杂。

3) 细节被函数掩盖。pyplot并不能完全复制对象体系的所有功能,图像的许多细节调中最终还要回到对象。

4) 每件事情都可以有至少两种方式完成,用户很容易混淆。

而对于开发者来说,了解对象是参与到Matplotlib项目的第一步。

 

我们将上面的直线绘图更改为面向对象式(OO, object-oriented)的,为此,我们引入两个类: Figure和FigureCanvas。(函数式编程也调用了这些类,只是调用的过程被函数调用所遮掩。)

# object-oriented plot

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')

理解对象

上面的例子中,我们至少构建了四个对象: fig, canvas, ax, line。它们分别属于Figure类,FigureCanvas类,Axes类和Line2D类。(使用obj.__class__.__name__来查询对象所属的类)

在深入各个对象之前,我们先来做一个比喻。看下面一个图片:

https://img4.mukewang.com/5ae535b6000171c404930496.jpg

这个图片是用KTurtle绘制。参看把你的孩子打造成为码农

可以看到,图中有一个房子,房子上有窗户和门,窗户上有条纹,门上有把手,此外图像外还有一只小乌龟。我们所提到的房子,窗户,门,条纹,把手,都可以称其为对象。不同的对象之间有依附关系,比如窗户和门属于房子,而把手属于门。乌龟和房子则是并行的两个对象。此外,整个图像外有一个方框,用来表明可绘图的范围,所有上面提到的元素都依附于该方框。

这就是用面向对象的方式来理解一个图像。事实上,对象是描述图像的最自然的方式,面向对象编程最成功的领域就是在计算机图形方面。

 

我们先来看什么是Figure和Axes对象。在matplotlib中,整个图像为一个Figure对象。在Figure对象中可以包含一个,或者多个Axes对象。每个Axes对象都是一个拥有自己坐标系统的绘图区域。其逻辑关系如下:

https://img3.mukewang.com/5ae535bd000161bf03550240.jpg

转过头来看直线图。整个图像是fig对象。我们的绘图中只有一个坐标系区域,也就是ax。此外还有以下对象。(括号中表示对象的基本类型)

https://img.mukewang.com/5ae535c500016feb04900506.jpg

Title为标题。Axis为坐标轴,Label为坐标轴标注。Tick为刻度线,Tick Label为刻度注释。各个对象之间有下面的对象隶属关系:

https://img3.mukewang.com/5ae535cd0001bcc504760343.jpg

(yaxis同样有tick, label和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)。我们可以通过绘出的坐标轴读出数据坐标的位置。

 

https://img3.mukewang.com/5ae5361d0001eec608000600.jpg

如果绘制的是具体数据,那么数据坐标符合我们的需求。如果绘制的是标题这样的附加信息,那么Axes坐标符合符合我们的需求。如果是整个图像的注解,那么Figure坐标更符合需求。每一个Artist对象都有一个transform属性,用于查询和改变所使用的坐标系统。如果为显示坐标,transform属性为None。

 深入基础

在上面的例子中,无论是使用plot绘制线,还是scatter绘制散点,它们依然是比较成熟的函数。matplotlib实际上提供了更大的自由度,允许用户以更基础的方式来绘制图形,比如下面,我们绘制一个五边形。

# object-oriented plot

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])

from matplotlib.path import Path
import matplotlib.patches as patches

verts = [
    (0., 0.), 
    (0., 1.),
    (0.5, 1.5),
    (1., 1.),
    (1., 0.),
    (0., 0.),
    ]

codes = [Path.MOVETO,
         Path.LINETO,
         Path.LINETO,
         Path.LINETO,
         Path.LINETO,
         Path.CLOSEPOLY,
         ]

path = Path(verts, codes)

patch = patches.PathPatch(path, facecolor='coral')
ax.add_patch(patch)
ax.set_xlim(-0.5,2)
ax.set_ylim(-0.5,2)

canvas.print_figure('demo.jpg')

在上面的程序中。我们首先确定顶点,然后构建了一个path对象,这个对象实际上就是5个顶点的连线。在codes中,我们先使用MOVETO将画笔移动到起点,然后依次用直线连接(LINETO)(我们也可以用曲线来连线,比如CURVE4,但这里没有用到)。 在path建立了封闭的5边形后,我们在path的基础上构建了patch对象,是一个图形块。patch的背景颜色选为coral。最后,我们将这个patch对象添加到预先准备好的ax上,就完成了整个绘图。

https://img4.mukewang.com/5ae5360c0001477008000600.jpg

上面的过程中,我们就好像拿着一个画笔的小孩,一步步画出心目中的图画。这就是深入理解matplotlib的魅力所在——创造你自己的数据绘图函数!

(将上面的程序封装到函数中,保留顶点以及其它参数接口,就构成了一个五边形绘图函数。O(∩_∩)O~ 我们也创造了新的“一键绘图”)

 

可以相像,一个plot函数如何用path对象实现。

 

总结

我们已经了解了matplotlib的最重要的方面,它们是:

1) pyplot函数绘图借口

2) 对象如何组合成为图像

3) 坐标系统

希望我的讲解没有消耗完你对matplotlib的兴趣。事实上,matplotlib是发展相当迅猛的绘图包,而它的开放性也让它成为了解计算机图形学的一个好接口。利用开放的核心对象,你可以随心的定制自己的数据绘图,而不用受制于高层的调用函数。谢谢John D. Hunter。

原文出处

打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP