1 一幅图像的生命周期
本教程旨在揭示使用matplotlib绘制的一幅图像的生命周期,包括它的开始、中间和结束。我们将从一些原始数据开始,最后保存自定义可视化的图形。在此过程中,我们尝试使用matplotlib突出一些简洁的功能和最佳实践。
2 关于显示接口和隐式接口的说明
matplotlib有两个接口。有关显示接口和隐式接口之间权衡的说明,请参阅matplotlib应用程序接口(API)。
在显式面向对象接口中,我们直接使用axes的实例。用于在图形实例中构建可视化的figure.figure。在受MATLAB启发并建模的隐式接口中,我们使用封装在pyplot模块中的基于全局状态的接口来绘制到当前轴。请参阅pyplot教程,更深入地了解pyplot接口。
大多数术语都很简单,但要记住的主要事情有:
Figure
是最终的图像,可能包括一个或者多个axes
;axes
代表一个独立的绘图(不要和axis
混淆,后者是指图中的x-、y-或z-轴)。
我们调用直接从axes进行绘图的方法,这为我们在自定义绘图时提供了更大的灵活性和功能。
注意
通常,使用显式接口而不是隐式pyplot接口进行绘图。
3 数据
我们将使用派生本教程的帖子中的数据。它包含一些公司的销售信息。
import numpy as np
import matplotlib.pyplot as plt
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)
4 启程
此数据自然可视化为柱形图,每组一条柱形。为了使用面向对象的方法做到这一点,我们首先生成一个图形的figure.Figure实例。图形就像一个画布,axes是该画布的一部分,我们将在其上进行特定的可视化。
我们这就产生了一个axes实例,我们能够在它的顶层绘图。
fig, ax = plt.subplots()
ax.barh(group_names, group_data)
5 控制风格
Matplotlib中有许多可用的样式,以便你根据需要定制可视化。要查看样式列表,我们可以使用style。
print(plt.style.available)
可以得到
['Solarize_Light2', '_classic_test_patch', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark', 'seaborn-dark-palette', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'tableau-colorblind10']
你可以使用下列的语句激活一种样式:
plt.style.use('fivethirtyeight')
现在让我们重新绘制一下之前的图形,再看一下结果:
fig, ax = plt.subplots()
ax.barh(group_names, group_data)
可以看出这个图没有边框,标签字体也比较大。
这里的样式控制了很多方面,例如颜色、线宽、背景等等。
6 自定义绘图
现在我们得到了一个具有我们想要的一般外观的情节,所以让我们对其进行微调,以便它准备好打印。首先,让我们旋转x轴上的标签,以便它们更清晰地显示。我们可以使用axes.Axes.xticklabels()
来访问这些标签。
fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
如果我们想一次设置多个项目的属性,使用pyplot.setp()
函数很有用。这将获取 matplotlib对象中的一个(或多个列表),并尝试为每个对象设置一些样式元素。
fig, ax = plt.subplots()
ax.barh(group_names, group_data)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, horizontalalignment='right')
看起来这样切断了底部的一些标签。我们可以告诉matplotlib自动为我们创建的图形中的元素腾出空间。为此,我们设置了rcParams的自动布局值。有关使用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')
接下来,我们向图添加标签。要使用面向对象接口执行此操作,我们可以使用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')
我们还可以使用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')
对于标签,我们可以以函数的形式指定自定义格式指南。下面我们定义一个函数,该函数将整数作为输入,并返回一个字符串作为输出。当与Axis.set_major_formatter
或Axis.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 = '${:1.1f}M'.format(x*1e-6)
else:
s = '${:1.0f}K'.format(x*1e-3)
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)
7 组合多个可视化效果
可以在同一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')
# 添加一条垂直线,这里我们在函数调用中设置样式
ax.axvline(group_mean, ls='--', color='r')
# 标记新的公司
for group in [3, 5, 8]:
ax.text(145000, group, "New Company", fontsize=10,
verticalalignment="center")
# 标题有点低,我们现在把它提高一些
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()
8 保存图像
现在我们对绘图的结果感到满意,我们想将其保存到磁盘。我们可以在matploblib中保存许多文件格式。要查看可用选项的列表,请使用:
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'}
然后我们可以使用figure.Figure.savefig()
来将图像保存到磁盘。请注意,我们在下面显示了几个有用的标识位:
transparent = True
使保存的图形的背景透明(如果格式支持);dpi = 80
控制输出的分辨率(每平方英寸点数);bbox_inches = "tight"
自动适应图像的边界。
# 解引用这一行即可保存图像,不透明,dpi为80,边距设置为紧.
# fig.savefig('sales.png', transparent=False, dpi=80, bbox_inches="tight")