一图胜千言,利用Python中的Matplotlib库可以在短短几行代码中创建一幅高质量的图。不管是用在论文还是汇报ppt中,这些图都将让人眼前一亮,迅速了解问题的核心。

      然而,Matplotlib库相当庞大,要创建一个看起来恰到好处的图通常需要反复试验。虽然代码一行流能够很简单的生成一些基础图,但是如何巧妙的使用库中剩下的98%功能是一件不简单的事。

      本文将介绍Matplotlib库的使用,水平从入门到中阶,其中理论知识和实验穿插在一起,使读者更容易理解Matplotlib库的原理与运作方式。

本文假设读者们已经了解一点numpy的知识,使用numpy.random主要是为了生产示例数据,便于从不同的统计分布中抽取样本。

1、令人头疼的Matplotlib库

      有时学习Matplotlib库是一个令人头疼的过程,问题不在于资料太少,事实上与之相关的资料多如牛毛。以下几个原因造成了人们学习的困境:

      1)库本身很大,包括了超过70000行代码;

      2)Matplotlib 拥有多种不同的界面(构建图形的方式),并且能够与少数不同的后端进行交互。 (后端处理图表实际呈现的过程,而不仅仅是内部结构化)

      3)虽然它很全面,但 Matplotlib 自己的一些公共文档已经严重过时了。 该库仍在不断发展,许多在网上流传的旧示例在其现代版本中可能会减少 70% 的代码行。

      因此,在进入任何华丽的示例之前,掌握 Matplotlib 设计的核心概念很有用。

2、Pylab:它是什么,我应该使用它吗?

      让我们从一段历史开始:神经生物学家 John D. Hunter 于 2003 年左右开始开发 Matplotlib,最初的灵感是模拟 Mathworks 的 MATLAB 软件中的命令。John 于 2012 年不幸去世,享年 44 岁,Matplotlib 现在是一个成熟的社区项目,由许多其他人开发和维护。

MATLAB 的一项相关特性是其全局样式。 导入的 Python 概念在 MATLAB 中并没有大量使用,并且 MATLAB 的大部分功能在顶级用户都可以轻松使用。

      知道 Matplotlib 起源于 MATLAB 有助于解释为什么 pylab 存在。 pylab 是 Matplotlib 库中的一个模块,旨在模仿 MATLAB 的全局风格。它的存在只是为了将 NumPy 和 matplotlib 中的许多函数和类带入命名空间,使不习惯需要导入语句的前 MATLAB 用户轻松过渡。

习惯于使用MATLAB的用户会喜欢这个功能,因为使用 from pylab import *,他们可以像在 MATLAB 中一样直接调用 plot() 或 array() 。

这里的问题对于一些 Python 用户来说可能很明显:在会话或脚本中使用 from pylab import * 通常是不好的做法。 Matplotlib 现在在其自己的教程中直接建议不要这样做:

“[pylab] still exists for historical reasons, but it is highly advised not to use. It pollutes namespaces with functions that will shadow Python built-ins and can lead to hard-to-track bugs. To get IPython integration without imports the use of the %matplotlib magic is preferred.” 

      在内部,在简短的 pylab 源中隐藏了大量潜在的冲突导入。 事实上,使用 ipython --pylab(来自终端/命令行)或 %pylab(来自 IPython/Jupyter 工具)只需在后台调用 from pylab import * 即可。

底线是 matplotlib 已经放弃了这个方便的模块,现在明确建议不要使用 pylab,使事情更符合 Python 的一个关键概念:显式优于隐式。

不需要 pylab,我们通常可以只使用一个规范导入:

import matplotlib.pyplot as plt

      在此过程中,我们还导入 NumPy,稍后我们将使用它来生成数据,并调用 np.random.seed() 使示例具有(伪)随机数据可重现:

import numpy as np

np.random.seed(444)

3、Matplotlib 对象层次结构

      matplotlib 的一个重要概念是它的对象层次结构。如果你学习过任何介绍性的 matplotlib 教程,你可能已经调用过类似 plt.plot([1, 2, 3]) 的东西。 这个单行隐藏了一个事实,即绘图实际上是嵌套 Python 对象的层次结构。 这里的“层次结构”意味着每个图下面都有一个 matplotlib 对象的树状结构。

      Figure 对象是 matplotlib 图形的最外层容器,它可以包含多个 Axes 对象。 混淆的一个来源是名称:Axes 实际上转化为我们认为的单个绘图或图形(而不是我们所期望的“轴”的复数形式)。

      您可以将 Figure 对象视为一个盒状容器,其中包含一个或多个 Axes(实际绘图)。层次结构中轴下方是较小的对象,例如刻度线、单行、图例和文本框。图表的几乎每个“元素”都是它自己的可操作 Python 对象,一直到刻度和标签: 

#yyds干货盘点#利用Matplotlib库画图(一)_显式

      这是此层次结构的示例图。 如果您不完全熟悉这种表示法,请不要担心,我们将在稍后介绍:

fig, _ = plt.subplots()

type(fig)

<class ‘matplotlib.figure.Figure'>

      上面,我们用 plt.subplots() 创建了两个变量。 第一个是顶级 Figure 对象。 第二个是我们还不需要的“一次性”变量,用下划线表示。 使用属性表示法,很容易向下遍历图形层次结构并查看第一个 Axes 对象的 y 轴的第一个刻度:

one_tick = fig.axes[0].yaxis.get_major_ticks()[0]

type(one_tick)

<class ‘matplotlib.axis.YTick'>

      上面, fig (一个 Figure 类实例)有多个 Axes (一个列表,我们取第一个元素)。 每个 Axes 都有一个 yaxis 和 xaxis,每个都有一个“主要刻度”的集合,我们抓住第一个。

      Matplotlib 将其呈现为人体解剖结构,而不是明确的层次结构:

 #yyds干货盘点#利用Matplotlib库画图(一)_显式_02