003-Matplotlib绘图逻辑框架之基础架构

约定

我们导入matplotlib时,推荐使用:

import matplotlib as mpl

因此,简洁起见,在后面我们将经常使用 mpl 代表 matplotlib。

还将用:

  • figure ,表示matplotlib绘制的图形
  • Image,表示位图图像,或栅格图像,保存方式为点阵存储,也称为点阵图像;
  • Graphic,表示矢量图形,用数学方法描述的图形。

有一部分初学者,因为对位图、矢量图、分辨率、DPI、PPI、图像尺寸、坐标等概念的不清晰,也造成了学习、使用mpl的一些困扰。

将会专门以一篇文章,用草根的语言讨论一下这些基本概念,理清这些概念对理解matplotlib框架非常有好处。

Matplotlib基础架构

003-Matplotlib绘图逻辑框架之基础架构_第1张图片
Matplotlib是基于Python语言,旨在为Python提供一个数据绘图包的开源项目,它以Python的一个库包的形式分发、安装、调用。

mpl是Python的一个包,记住:Python包就是一个定义了__init__.py文件的文件夹而已,包中的各个模块都会保存在这个文件夹中。

matplotlib 实际上就是提供了 3 种API,用于不同的绘图场景。

  • The pyplot API
  • The object-oriented API
  • The pylab API (已弃用)

mpl需要其它包的支持,主要有:

  • numpy,用于提供绘图数据,你要用于绘图的数据都应转换化numpy 的 ndarray,才传递给mpl;。事实上,一幅图像也可以保存为一个ndarray(将专门介绍)。
  • backend,后端,简单地说就是将mpl代码转换为图形的后台算法,如mpl中最常用的FigureCanvasAgg,将专文讨论backend。

函数式绘图 pyplot API

matplotlib是受MATLAB的启发构建的,MATLAB语言是面向过程的,利用函数的调用,MATLAB中可以轻松的利用一行命令来绘制直线,然后再用一系列的函数调整结果。

matplotlib通过matplotlib.pyplot模块,完全仿照MATLAB的函数形式,提供了一套绘图接口。这套函数接口方便MATLAB用户过度到matplotlib包。

Python中的函数式编程是通过封装对象实现的。matplotlib中的函数式调用其实也是如此。matplotlib本质上还是构建对象来构建图像。只不过pyplot函数将构建对象的过程封装在函数中,从而让我们觉得很方便。

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

但利用函数式绘图会有以下缺点:

  1. 增加了一层“函数”调用,降低了效率。
  2. 隶属关系被函数掩盖。整个matplotlib包是由一系列有组织有隶属关系的对象构成的。函数掩盖了原有的隶属关系,将事情变得复杂、模糊。
  3. 细节被函数掩盖。pyplot并不能完全复制对象体系的所有功能,图像的许多细节调整最终还要回到对象。
  4. 每件事情都可以有至少两种方式完成,用户很容易混淆。
  5. 封装,掩盖了真相,蒙蔽了很多初学者的双眼,让他们迷失在mpl世界的边缘,始终不能达到自由操控mpl的境界。

第4、5组成了一个陷阱,它就是困住很多初学者的那个最大的坑。

再看下面的函数绘图的示例:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4])
plt.show()

003-Matplotlib绘图逻辑框架之基础架构_第2张图片
对于初学者来说,它很简单,也很容易模仿,但很多人不知道,函数封装的一些东西在后台做了些什么,因此长时间只能照猫画虎。

面向对象**(OO, object-oriented)**绘图

我们改用**面向对象(OO, object-oriented)**绘图方式绘制上面的直线图。

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([1, 2, 3, 4])
s, (width, height) = canvas.print_to_buffer() 

from PIL import Image 
im = Image.frombytes("RGBA", (width, height), s)

im.show() 

大家先复制这些代码,运行看看就可以了,后面会详细解释它们。

因为,面向对象绘图需要我们一步一步地创建需要的对象,包括基础的Figure,FigureCanvasAgg(画布),这需要引入两个类: Figure和mpl.backends.backend_agg.FigureCanvasAgg。所以代码看其来要长得多、复杂得多。

但这时,我们反而非常清楚我们创建的每个对象,对象之间的关系,以及我们正在做什么。

函数式编程也调用了这些类,只是调用的过程被函数调用所遮掩,所以初学者对函数创建的对象常是模糊的,似懂非懂的。函数式绘图提供了状态机接口,会自动对当前对象进行绘图操作,所以对象的调用思路也是不清晰的。

而面向对象绘图不同,每个对象都是你明确创建的,要对哪个对象进行操作也是显式调用的,所以你就知其然且知其所以然。

以我个人的经验,认为:从面向对象绘图开始才是学习matplotlib的正确姿势!

跟着我前进,等你学习一段时间后,你就会有打通matplotlib修炼任督二脉的感觉。

pylab,仿matlab模式

matplotlib.pylab是一个模块,它在单个名称空间中批量导入matplotlib.pyplot、numpy以及一些附加函数用于绘图。 最初的目的是通过将所有函数导入全局命名空间来模仿类似MATLAB的工作方式。由于大量导入全局命名空间可能会导致意外行为,现在这被认为是糟糕的风格。因此强烈建议不要使用pylab。 事实上在matplotlib的最新版中已弃用它。

因此,后面,我们将认为mpl只有pyplot函数式绘图和面向对象绘图两套API。

总结

  • mpl以numpy, backend为支撑,提供了函数式绘图、面向对象绘图两种绘图模式(API);
  • 面向对象绘图是mpl的核心、精髓,请从这里开始;
  • pyplot仅是对对象的封装而已,会自然习得。

关于mpl框架的更详细介绍请见后续更新。

(This end.)

你可能感兴趣的:(matplotlib)