数据科学 IPython 笔记本 8.3 Matplotlib 可视化

2022-05-07 13:44:49 浏览数 (1)

8.3 Matplotlib 可视化

原文:Visualization with Matplotlib 译者:飞龙 协议:CC BY-NC-SA 4.0 本节是《Python 数据科学手册》(Python Data Science Handbook)的摘录。

我们现在将深入研究M atplotlib 包,以便在 Python 中进行可视化。Matplotlib 是一个基于 NumPy 数组的多平台数据可视化库,旨在兼容更广泛的 SciPy 技术栈。它由 John Hunter 在 2002 年构思,最初是作为 IPython 的补丁,用于通过来自 IPython 命令行的gnuplot实现 MATLAB 风格的交互式绘图。

IPython 的创始人 Fernando Perez 当时正努力完成他的博士学位,并且 John 知道他几个月都没时间审查补丁。John 把它看做一个使自己开始的动机,之后 Matplotlib 软件包诞生了,2003 年发布了 0.1 版本。当它被用作太空望远镜科学研究所(哈勃望远镜背后的人)选择的绘图包时,它得到了早期的赞助,该研究所在财务上支持了 Matplotlib 的开发,并极大地扩展了它的能力。

Matplotlib 最重要的功能之一是,它能够很好地兼容许多操作系统和图形后端。Matplotlib 支持许多后端和输出类型,这意味着无论你使用哪种操作系统或你希望使用哪种输出格式,你都可以依赖它。

这种跨平台的通用方法是 Matplotlib 的强大优势之一。它带来了庞大的用户群,这反过来又产生了活跃的开发人员的基础,和 Python 科学世界中的 Matplotlib 的威力和普遍性。

然而,近年来,Matplotlib 的界面和风格已经有些过时。 比起 R 语言中的ggplotggvis等新工具,以及基于 D3js 和 HTML5 画布的 Web 可视化工具包,Matplotlib 显得笨重和陈旧。尽管如此,我认为我们不能忽视 Matplotlib 作为经过良好测试的跨平台图形引擎的优势。

最近的 Matplotlib 版本,使得设置新的全局绘图样式变得相对容易(参见“自定义 Matplotlib:配置和样式表”),人们一直在开发新的包,它们基于 Matplotlib 的强大内核,通过更清洁,更现代的 API 驱动 Matplotlib - 例如,Seaborn(“可视化与 Seaborn”),[ggpy](http://yhat.github.io/ggpy /),HoloViews,Altair,甚至 Pandas 本身都可以用作 Matplotlib API 的包装。

即使使用这样的包装器,通常也可以深入研究 Matplotlib 的语法来调整最终的绘图输出。出于这个原因,我相信 Matplotlib 本身,仍将是数据可视化技术栈的重要组件,即使新工具意味着社区逐渐不再使用 Matplotlib API。

一般 Matplotlib 提示

在我们深入了解使用 Matplotlib 创建可视化的细节之前,你应该了解一些使用该软件包的有用信息。

导入 Matplotlib

正如我们对 NumPy 使用np简写,对 Pandas 使用pd简写一样,我们将对 Matplotlib 导入使用一些标准简写:

代码语言:javascript复制
import matplotlib as mpl
import matplotlib.pyplot as plt

我们将经常使用plt接口,我们将在本章中看到。

设置样式

我们将使用plt.style指令,为我们的图形选择合适的美学风格。在这里,我们将设置classic样式,确保我们创建的图使用经典的 Matplotlib 样式:

代码语言:javascript复制
plt.style.use('classic')

在本节中,我们将按需调整此样式。请注意,此处使用的样式表从 Matplotlib 1.5 版开始得到支持;如果你使用的是早期版本的 Matplotlib ,则只能使用默认样式。样式表的更多信息,请参阅“自定义 Matplotlib:配置和样式表”。

show()还是不show()?如何展示你的绘图

你看不到的可视化并没什么用,但是你查看 Matplotlib 绘图的方式取决于上下文。Matplotlib 的最佳用法取决于你的使用方式;粗略地说,三个适用的上下文是,在脚本,IPython 终端或 IPython 笔记本中使用 Matplotlib。

来自脚本的绘图

如果你在脚本中使用 Matplotlib,函数plt.show()就是你的伙伴。plt.show()启动一个事件循环,查找所有当前活动的图形对象,并打开一个或多个显示你的图形的交互式窗口。

因此,例如,你可能有一个名为myplot.py的文件,其中包含以下内容:

代码语言:javascript复制
# ------- 文件:myplot.py ------
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

plt.plot(x, np.sin(x))
plt.plot(x, np.cos(x))

plt.show()

然后,你可以从命令行运行此脚本,这将打开一个窗口并显示你的图形:

代码语言:javascript复制
$ python myplot.py

plt.show()命令做了很多工作,因为它必须与系统的交互式图形后端进行交互。此操作的细节可能因不同系统,甚至安装而异,但 matplotlib 会尽力对你隐藏所有这些细节。

有一点需要注意:plt.show()命令应该在每个 Python 会话中只使用一次,并且最常见于脚本的最后。多个show()命令可能产生不可预测的后端依赖的行为,并且多数情况下应该避免。

来自 IPython shell 的绘图

在 IPython shell 中以交互方式使用 Matplotlib 非常方便(参见“IPython:不只是普通的 Python”)。如果指定 Matplotlib 模式,IPython 可以很好地配合 Matplotlib。要启用此模式,可以在启动ipython后使用%matplotlib魔术命令:

代码语言:javascript复制
In [1]: %matplotlib
Using matplotlib backend: TkAgg

In [2]: import matplotlib.pyplot as plt

此时,任何plt绘图命令都会打开一个图形窗口,可以运行更多命令来更新绘图。一些更改(例如修改已绘制的直线的属性)将不会自动绘制:要强制更新,请使用plt.draw()。不需要在 Matplotlib 模式下使用plt.show()

来自 IPython 笔记本的绘图

IPython 笔记本是一个基于浏览器的,交互式数据分析工具,可以将叙述,代码,图形,HTML 元素等组合到一个可执行文档中(参见“IPython:不只是普通的 Python”)。

IPython 笔记本中的交互式绘图,可以使用%matplotlib命令完成,其工作方式与 IPython shell 类似。在 IPython 笔记本中,你还可以选择直接在笔记本中嵌入图形,有两种可能的选择:

  • %matplotlib notebook将产生嵌入在笔记本中的交互式绘图
  • %matplotlib inline将产生嵌入在笔记本中的绘图的静态图像

对于本书,我们通常会选择%matplotlib inline

代码语言:javascript复制
%matplotlib inline

运行此命令后(每个内核/会话只需执行一次),笔记本中创建绘图的任何单元格,都将嵌入所得图形的 PNG 图像:

代码语言:javascript复制
import numpy as np
x = np.linspace(0, 10, 100)

fig = plt.figure()
plt.plot(x, np.sin(x), '-')
plt.plot(x, np.cos(x), '--');

将图形保存到文件

Matplotlib 的一个很好的功能,是以各种格式保存图形的能力。保存图形可以使用savefig()命令完成。例如,要将上一个图形保存为 PNG 文件,可以运行以下命令:

代码语言:javascript复制
fig.savefig('my_figure.png')

在当前工作目录中,我们现在有一个名为my_figure.png的文件:

代码语言:javascript复制
!ls -lh my_figure.png

# -rw-r--r--  1 jakevdp  staff    16K Aug 11 10:59 my_figure.png

为了确认它包含我们认为它包含的内容,让我们使用 IPython Image对象来显示该文件的内容:

代码语言:javascript复制
from IPython.display import Image
Image('my_figure.png')

savefig()中,文件格式是从给定文件名的扩展名推断出来的。根据你安装的后端,可以使用许多不同的文件格式。通过使用图形画布对象的以下方法,可以找到系统支持的文件类型列表:

代码语言:javascript复制
fig.canvas.get_supported_filetypes()

'''
{'eps': 'Encapsulated Postscript',
 'jpeg': 'Joint Photographic Experts Group',
 'jpg': 'Joint Photographic Experts Group',
 'pdf': 'Portable Document Format',
 'pgf': 'PGF code for LaTeX',
 'png': 'Portable Network Graphics',
 'ps': 'Postscript',
 'raw': 'Raw RGBA bitmap',
 'rgba': 'Raw RGBA bitmap',
 'svg': 'Scalable Vector Graphics',
 'svgz': 'Scalable Vector Graphics',
 'tif': 'Tagged Image File Format',
 'tiff': 'Tagged Image File Format'}
'''

请注意,在保存图形时,不必使用前面讨论过的plt.show()或相关命令。

一个功能的两个接口

Matplotlib 的一个可能令人困惑的特性是它的两个接口:一个是方便的 MATLAB 风格的,基于状态的接口,以及一个更强大的面向对象的接口。我们将在这里快速强调两者之间的差异。

MATLAB 风格的接口

Matplotlib 最初编写为 MATLAB 用户的 Python 替代品,其大部分语法都反映了这一事实。MATLAB 风格的工具包含在pyplotplt)接口中。例如,以下代码可能对 MATLAB 用户来说非常熟悉:

代码语言:javascript复制
plt.figure()  # 创建绘图图形

# 创建两个面板的第一个并设置当前轴
plt.subplot(2, 1, 1) # 行,列,面板号
plt.plot(x, np.sin(x))

# 创建第二个面板并设置当前轴
plt.subplot(2, 1, 2)
plt.plot(x, np.cos(x));

重要的是要注意这个接口是有状态的:它跟踪“当前”图形和轴域,这是所有plt命令都适用的地方。你可以使用plt.gcf()(获取当前图形)和plt.gca()(获取当前轴域)例程来获取这些引用。

虽然这种状态接口对于简单的绘图来说既快速又方便,但很容易遇到问题。例如,一旦创建了第二个面板,我们如何返回并向第一个面板添加内容?这在 MATLAB 风格的接口中是可能的,但有点笨拙。幸运的是,有一种更好的方法。

面向对象的接口

面向对象的接口可用于这些更复杂的情况,以及需要对图形进行更多控制的时候。在面向对象的界面中,绘图函数并不依赖于“活动”图形或轴域的某些概念,而是显式“图形”和“轴域”对象的方法。要使用此风格的绘图重新创建上一个绘图,你可以执行以下操作:

代码语言:javascript复制
# 首先创建绘图网格
# ax will be an array of two Axes objects
fig, ax = plt.subplots(2)

# 在对应对象上调用 plot() 方法
ax[0].plot(x, np.sin(x))
ax[1].plot(x, np.cos(x));

对于更简单的图,选择使用哪种样式在很大程度上取决于偏好,但随着变得更复杂,面向对象的方法可能成为必要。在本章中,我们将根据最方便的方式,在 MATLAB风 格和面向对象的界面之间切换。在大多数情况下,差异就像切换plt.plot()ax.plot()一样小,但是在下面的章节中我们会强调一些问题。

0 人点赞