绘图的生命周期#

本教程旨在展示使用 Matplotlib 进行单一可视化的全过程,从开始、中间到结束。我们将从一些原始数据开始,最终保存一张自定义的可视化图表。在此过程中,我们将尝试突出 Matplotlib 的一些酷炫功能和最佳实践。

备注

本教程基于 Chris Moffitt 的 这篇优秀博客文章 。它由 Chris Holdgraf 改编为此教程。

关于显式与隐式接口的说明#

Matplotlib 有两种接口。关于显式和隐式接口之间的权衡解释,请参见 Matplotlib 应用程序接口 (APIs)

在显式的面向对象(OO)接口中,我们直接使用 axes.Axes 的实例来构建 figure.Figure 实例中的可视化。在隐式接口中,受 MATLAB 启发并以其为模型,我们使用一个基于全局状态的接口,该接口封装在 pyplot 模块中,用于绘制到“当前 Axes”。有关 pyplot 接口的更深入了解,请参阅 pyplot 教程

大多数术语都很直接,但需要记住的主要事情是:

  • Figure 是最终的图像,可能包含一个或多个 Axes

  • Axes 表示一个单独的图(不要与

    Axis,指的是图表的 x、y 或 z 轴。

我们调用直接从 Axes 进行绘图的方法,这为我们提供了更大的灵活性和自定义绘图的能力。

备注

通常情况下,绘图时应使用显式接口,而不是隐式的 pyplot 接口。

我们的数据#

我们将使用本教程所基于的帖子中的数据。它包含了许多公司的销售信息。

import matplotlib.pyplot as plt
import numpy as np

data = {'Barton LLC': 109438.50,
        'Frami, Hills and Schmidt': 103569.59,
        'Fritsch, Russel and Anderson': 112214.71,
        'Jerde-Hilpert': 112591.43,
        'Keeling LLC': 100934.30,
        'Koepp Ltd': 103660.54,
        'Kulas Inc': 137351.96,
        'Trantow-Barrows': 123381.38,
        'White-Trantow': 135841.99,
        'Will LLC': 104437.60}
group_data = list(data.values())
group_names = list(data.keys())
group_mean = np.mean(group_data)

入门指南#

这些数据自然地可以被可视化为条形图,每个组对应一个条形。使用面向对象的方法来实现这一点,我们首先生成一个 figure.Figure 实例和一个 axes.Axes 实例。Figure 就像一个画布,而 Axes 是画布上的一部分,我们将在其上进行特定的可视化。

备注

图形可以有多个轴。有关如何执行此操作的信息,请参阅 紧密布局教程

fig, ax = plt.subplots()
lifecycle

现在我们有了一个 Axes 实例,我们可以在其上绘图。

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
lifecycle

控制样式#

Matplotlib 中有许多样式可供选择,以便您根据需要定制您的可视化效果。要查看样式列表,我们可以使用 style

print(plt.style.available)
['Solarize_Light2', '_classic_test_patch', '_mpl-gallery', '_mpl-gallery-nogrid', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'petroff10', 'seaborn-v0_8', 'seaborn-v0_8-bright', 'seaborn-v0_8-colorblind', 'seaborn-v0_8-dark', 'seaborn-v0_8-dark-palette', 'seaborn-v0_8-darkgrid', 'seaborn-v0_8-deep', 'seaborn-v0_8-muted', 'seaborn-v0_8-notebook', 'seaborn-v0_8-paper', 'seaborn-v0_8-pastel', 'seaborn-v0_8-poster', 'seaborn-v0_8-talk', 'seaborn-v0_8-ticks', 'seaborn-v0_8-white', 'seaborn-v0_8-whitegrid', 'tableau-colorblind10']

您可以使用以下方式激活样式:

plt.style.use('fivethirtyeight')

现在让我们重新制作上面的图表,看看它的样子:

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
lifecycle

样式控制许多方面,例如颜色、线宽、背景等。

自定义绘图#

现在我们已经得到了一个大致符合我们要求的图,所以让我们对其进行微调,使其准备好打印。首先让我们旋转x轴上的标签,以便它们更清晰地显示。我们可以使用 axes.Axes.get_xticklabels() 方法访问这些标签:

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
lifecycle

如果我们想一次性设置多个项目的属性,使用 pyplot.setp() 函数会很有用。这将接受一个(或多个)Matplotlib 对象的列表,并尝试设置每个对象的某些样式元素。

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')
lifecycle

看起来这截断了底部的一些标签。我们可以告诉 Matplotlib 自动为我们在创建的图形中的元素腾出空间。为此,我们设置 rcParams 的 autolayout 值。有关使用 rcParams 控制绘图的风格、布局和其他特性的更多信息,请参阅 自定义

plt.rcParams.update({'figure.autolayout': True})

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')
lifecycle

接下来,我们为图表添加标签。使用面向对象接口时,我们可以使用 Artist.set() 方法来设置此 Axes 对象的属性。

fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')
ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company',
       title='Company Revenue')
Company Revenue

我们也可以使用 pyplot.subplots() 函数调整这个图的大小。我们可以通过 figsize 关键字参数来实现这一点。

备注

在 NumPy 中,索引遵循 (行, 列) 的形式,而 figsize 关键字参数遵循 (宽度, 高度) 的形式。这遵循了可视化的惯例,不幸的是,这与线性代数的惯例不同。

fig, ax = plt.subplots(figsize=(8, 4))
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')
ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company',
       title='Company Revenue')
Company Revenue

对于标签,我们可以通过函数的形式指定自定义的格式化规则。下面我们定义了一个函数,该函数接受一个整数作为输入,并返回一个字符串作为输出。当与 Axis.set_major_formatterAxis.set_minor_formatter 一起使用时,它们将自动创建并使用 ticker.FuncFormatter 类。

对于此函数,x 参数是原始刻度标签,pos 是刻度位置。我们这里只使用 x,但两个参数都是必需的。

def currency(x, pos):
    """The two arguments are the value and tick position"""
    if x >= 1e6:
        s = f'${x*1e-6:1.1f}M'
    else:
        s = f'${x*1e-3:1.0f}K'
    return s

然后我们可以将这个函数应用到我们图表的标签上。为此,我们使用 Axes 的 xaxis 属性。这允许你对图表上的特定轴执行操作。

fig, ax = plt.subplots(figsize=(6, 8))
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')

ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company',
       title='Company Revenue')
ax.xaxis.set_major_formatter(currency)
Company Revenue

结合多种可视化#

可以在同一个 axes.Axes 实例上绘制多个绘图元素。为此,我们只需在该 Axes 对象上调用另一个绘图方法。

fig, ax = plt.subplots(figsize=(8, 8))
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')

# Add a vertical line, here we set the style in the function call
ax.axvline(group_mean, ls='--', color='r')

# Annotate new companies
for group in [3, 5, 8]:
    ax.text(145000, group, "New Company", fontsize=10,
            verticalalignment="center")

# Now we move our title up since it's getting a little cramped
ax.title.set(y=1.05)

ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company',
       title='Company Revenue')
ax.xaxis.set_major_formatter(currency)
ax.set_xticks([0, 25e3, 50e3, 75e3, 100e3, 125e3])
fig.subplots_adjust(right=.1)

plt.show()
Company Revenue

保存我们的图表#

既然我们对绘图的结果感到满意,我们希望将其保存到磁盘上。在 Matplotlib 中,我们可以保存为多种文件格式。要查看可用选项的列表,请使用:

print(fig.canvas.get_supported_filetypes())
{'eps': 'Encapsulated Postscript', 'jpg': 'Joint Photographic Experts Group', 'jpeg': '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', 'webp': 'WebP Image Format'}

然后我们可以使用 figure.Figure.savefig() 将图形保存到磁盘。请注意,下面显示了几个有用的标志:

  • transparent=True 如果格式支持,会使保存的图像背景透明。

  • dpi=80 控制输出分辨率(每平方英寸点数)。

  • bbox_inches="tight" 使图形的边界适应我们的绘图。

# Uncomment this line to save the figure.
# fig.savefig('sales.png', transparent=False, dpi=80, bbox_inches="tight")

脚本的总运行时间: (0 分钟 1.428 秒)

由 Sphinx-Gallery 生成的图库