0 系列简介
这个系列,小白从起点出发开始整理matplotlib库的基本使用及相关技巧。小白的主要思路是沿着官网的tutorials先学习matplotlib的基础功能,然后针对官网总结的Cheat Sheets及其中的知识点查漏补缺。作为一个工具库,小白认为只要能够使用Cheat Sheets绘制出自己需要的图形,应该就足够平时学习、工作使用了。
1 简单认识matplotlib
matplotlib是python的一个经典库,主要是用来做可视化。
目前已经更新到3.7.2。
小白很喜欢matplotlib的Cheat Sheets,它很贴心地把重要的方法总结到同一页纸上,如果熟练地掌握了其使用的基础知识,在需要的时候,只要查阅这薄薄几页的Cheat Sheets,就可以快速实现各种不同的图形。
甚至官方还根据内容的难易度,将Cheat Sheets分为了初级、中级和高级。
来一起瞄一下初级的Cheat Sheets:
内容非常丰富!而且我们能看到,散点图、柱状图、像素图、轮廓图、饼图、误差图、蜡烛图、曲线图的一些基本用法都在其中。当然这里面缺少了很多细节,所以我们还是要深入学习之后才能使用Cheat Sheets进行快速使用。
如果你有使用过matlab的经历,那么你会发现matplotlib的设计非常类似,甚至有很多概念和函数与matlab几乎相同。
2 从tutorials开始_Quick start guide
配置matplotlib是一件简单的事,这里小白不做特别介绍。因为不管是使用pycharm配置环境,还是用pip 进行python依赖库配置,是python的基本技能。
tutorials的首个课程名称就叫Quick start guide。
这一部分包含了一些基本的使用模式,帮助我们开始使用Matplotlib。
首先学习一下如何导入包:
python
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
官方的教程其实就是最权威的缩写方式,约定俗成(当然你也可以很个性,但是个人觉得按约定俗成的方式会使你的代码具有很好的可读性)。
matplotlib将你的数据画在Figure
上(通常程序会打开一个小窗口来绘制,或者在jupyter notebook中打开一个窗口来绘制)。每个Figure
通常又包括一个或多个Axes
。所谓Axes
,指的是建立一个x-y坐标系,可以在其中绘制数据。当然也不一定都是x-y这样的二维坐标系,也可能是极坐标的theta-r坐标,或者是一个x-y-z的三维坐标系。
创建一个带有Axes
的Figure
,最简单的办法是使用pyplot.subplots
,接着我们可以使用Axes.plot
在Axes上绘制一些数据:
python
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
plt.show()
注意,在Axes中绘制Figure后,需要使用plt.show()
来显示最终的结果。
3 Figure
的组成部分
以下就是一个Matplotlib Figure的组成部分,小白对官网网上的示例图做了一点翻译:
Figure
Figure指的是图形的整体。Figure中包含了所有的子坐标系Axes,一组特别设定的风格Artists(包括标题、图例、颜色映射表等等),甚至还有子图subfigures。
创建一个新的Figure,最简单的方法就是通过pyplot:
python
fig = plt.figure() # 空figure
fig, ax = plt.subplots() # 带一个坐标系的figure
fig, axs = plt.subplots(2, 2) # 带有2*2共4个子坐标系的figure
# 左边一个坐标系,右边上下分布两个坐标系的figure:
fig, axs = plt.subplot_mosaic([['left', 'right_top'],
['left', 'right_bottom']])
看下效果:
通常在创建Figure时直接创建Axes是很方便的,但若是先创建Figure再创建Axes也是可以的。请注意,许多matplotlib后端支持(backends,关于这个概念可能还需要再探索一下,此处暂且按下不 表)在图形窗口上进行缩放和平移。
Axes
Axes 是附加到Figure的"艺术家"(Artist)对象,通常包括两个(或在3D情况下三个)Axis对象(注意区分Axes和Axis两个概念)。这些对象提供刻度和刻度标签,为Axes中的数据提供刻度。每个Axes还有一个标题(通过set_title()
设置),一个x轴标签(通过set_xlabel()
设置), 和一个y轴标签(通过set_ylabel()
设置)。
Axes类和它的成员函数是使用OOP接口的主要入口点,并且在其上定义了大多数绘图方法(例如ax.plot()
)。
Axis
这些对象设置刻度和限制,并生成刻度(Axis上的标记)和刻度标签(标记刻度的字符串)。刻度的位置由定位器对象确定,刻度标签字符串由格式化程序实现格式化。正确的定位和格式化程序的组合可以非常精细地控制刻度位置和标签。
Artist
这是matplotlib的重要概念。基本上,所有在Figure中可见的组件都是一个Artist(甚至Figure本身,Axes和Axis对象)。它包括Text对象,Line2D对象,collections对象,Patch对象等等。渲染图形时,所有Artist都会被绘制到画布上。大多数Artist都绑定在Axes上,这样的Artist不能由多个Axes共享,也不能从一个Axes移动到另一个Axes。
4 绘制函数时的输入类型
绘制函数时,需要numpy.array
或numpy.ma.masked_array
作为输入,或者可以被转换为numpy.asarray
的对象。和arrays类似的类(似array),诸如pandas
数据对象和numpy.matrix
不能正常工作。通常的约定是在绘制之前将它们转换为numpy.array
对象。比如,转换一个numpy.matrix
:
python
b = np.matrix([[1, 2], [3, 4]])
b_asarray = np.asarray(b)
大多数方法还将解析可寻址的对象,如字典,numpy.recarray
,或者pandas.DataFrame
。matplotlib允许你提供数据关键字参数并生成传递对应于x和y变量的字符串的绘图。
python
np.random.seed(19680801) # 为随机数设置种子
data = {'a': np.arange(50),
'c': np.random.randint(0, 50, 50),
'd': np.random.randn(50)}
data['b'] = data['a'] + 10 * np.random.randn(50)
data['d'] = np.abs(data['d']) * 100
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.scatter('a', 'b', c='c', s='d', data=data)
ax.set_xlabel('entry a')
ax.set_ylabel('entry b')
5 代码风格
显式接口和隐式接口
如上所述,基本上有两种方法可以使用matplotlib:
- 显式地创建Figures和Axes,然后以它们为主体调用方法(即面向对象的风格);
- 依靠pyplot来隐式地创建和管理Figures和Axes,然后使用pyplot函数来绘制。
所以,你可以使用面向对象风格:
python
x = np.linspace(0, 2, 100) # 数据.
# 注意,即使是在面向对象风格中,我们仍然使用`.pyplot.figure`创建figure.
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.plot(x, x, label='linear') # 在axes绘制一些数据.
ax.plot(x, x**2, label='quadratic') #在axes中绘制更多数据...
ax.plot(x, x**3, label='cubic') # ... 更多数据.
ax.set_xlabel('x label') # 在axes中设置x标签.
ax.set_ylabel('y label') # 在axes中设置y标签.
ax.set_title("Simple Plot") # 在axes中设置标题.
ax.legend() # 添加图例.
或者是pyplot风格:
python
x = np.linspace(0, 2, 100) # 数据.
plt.figure(figsize=(5, 2.7), layout='constrained')
plt.plot(x, x, label='linear') # 在axes中绘制一些数据.
plt.plot(x, x**2, label='quadratic') # 其他.
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()
这里绘制出来的效果和上面是一致的。
此外,还有第三种方法,用于在GUI应用程序中嵌入matplotlib的情况,即使对于图形创建,也可以完全删除pyplot。
matplotlib的文档和示例同时使用面向对象和pyplot风格。通常,我们建议使用面向对象风格,特别是对于复杂的绘图,以及旨在作为较大项目的一部分重用的函数和脚本。但是,pyplot风格对于快速交互式工作非常方便。
你可能会找到使用pylab接口的旧示例,通过
from pylab import *
,此方法已经被强烈弃用。
创建一个有帮助文档的函数
如果你需要针对不同的数据反复调用某些绘图函数,或者想封装一个带有matplotlib方法的包,可以使用以下推荐的方法:
python
def my_plotter(ax, data1, data2, param_dict):
"""
A helper function to make a graph.
一个帮助文档的段落.
"""
out = ax.plot(data1, data2, **param_dict)
return out
下面调用两次上面这个函数:
python
data1, data2, data3, data4 = np.random.randn(4, 100) # 生成4组随机数据
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(5, 2.7))
my_plotter(ax1, data1, data2, {'marker': 'x'})
my_plotter(ax2, data3, data4, {'marker': 'o'})
绘制出的效果如下:
6 风格化艺术家
大多数绘图方法都有Artist的样式选项,可以在调用绘图方法时访问,也可以从Artist的资源库访问。在下面的图中,我们手动设置了由绘图创建的艺术家的颜色、线宽和线型,并在事后用set_linestyle设置了第二条线的线型。
python
fig, ax = plt.subplots(figsize=(5, 2.7))
x = np.arange(len(data1))
ax.plot(x, np.cumsum(data1), color='blue', linewidth=3, linestyle='--')
l, = ax.plot(x, np.cumsum(data2), color='orange', linewidth=2)
l.set_linestyle(':')
颜色
matplotlib对于多数Artists都可以使用比较灵活的数组,参见一系列颜色教程。有些Artists可以使用复合颜色。例如散点图,标记点的边缘可以与内部颜色不相同:
python
fig, ax = plt.subplots(figsize=(5, 2.7))
ax.scatter(data1, data2, s=50, facecolor='C0', edgecolor='k')
线宽、线型和标记点大小
线宽通常以排版点(1 pt = 1/72 英寸)为单位,可用于具有描边线条的Artists。同样,描边线可以具有线型。请参阅线型教程。
标记点的大小取决于所使用的方法。plot以磅为单位指定标记大小,通常百标记的"直径"或宽度。散点指定标记点大小与标记的可视区域大致成比例。有一组标记样式可用作字符串代码,或者用户可以定义自己的标记样式。
python
fig, ax = plt.subplots(figsize=(5, 2.7))
ax.plot(data1, 'o', label='data1')
ax.plot(data2, 'd', label='data2')
ax.plot(data3, 'v', label='data3')
ax.plot(data4, 's', label='data4')
ax.legend()
7 标记绘图
Axes标记和文本
set_xlabel
、set_ylabel
和set_title
用于在指示的位置添加文本(有关更多讨论,请参阅matplotlib图中的文本)。也可以使用文本将文本直接添加到绘图中:
python
mu, sigma = 115, 15
x = mu + sigma * np.random.randn(10000)
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
# the histogram of the data
n, bins, patches = ax.hist(x, 50, density=True, facecolor='C0', alpha=0.75)
ax.set_xlabel('Length [cm]')
ax.set_ylabel('Probability')
ax.set_title('Aardvark lengths\n (not really)')
ax.text(75, .025, r'$\mu=115,\ \sigma=15$')
ax.axis([55, 175, 0, 0.03])
ax.grid(True)
所有文本函数都返回一个matplotlib.text.Text实例。与上面的行一样,你可以通过将关键字参数传递到文本函数中来自定义属性:
python
t = ax.set_xlabel('my data', fontsize=14, color='red')
在文本中使用数学表达式
matplotlib接受任何文本表达式中的 LaTeX \LaTeX LATEX 方程表达式,例如,想要在标题里输入 σ i = 15 \sigma_i=15 σi=15,只需要和通常一样用美元符号包围它:
python
ax.set_title(r'$\sigma_i=15$')
其中标题字符串前面的r表示字符串是原始字符串,而不是将反斜杠视为python转义。matplotlib具有内置的 TeX \TeX TEX表达式解析器和布局引擎,并附带自己的数学字体------有关详细信息,请参阅编写数学表达式。你还可以直接使用 LaTeX \LaTeX LATEX格式化文本,并将输出直接合并到显示图形或保存的后记中------请参阅使用 LaTeX \LaTeX LATEX进行文本渲染。
附注
我们还可以在图上注释点,通常是通过将指向xy的箭头连接到xytext处的一段文本:
python
fig, ax = plt.subplots(figsize=(5, 2.7))
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2 * np.pi * t)
line, = ax.plot(t, s, lw=2)
ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='green', shrink=0.15))
ax.set_ylim(-2, 2)
在此基本示例中,xy和xytext都在数据坐标中。还可以选择其他多种坐标系。
图例
通常我们用Axes.legend
来标记线型和标记点:
python
fig, ax = plt.subplots(figsize=(5, 2.7))
ax.plot(np.arange(len(data1)), data1, label='data1')
ax.plot(np.arange(len(data2)), data2, label='data2')
ax.plot(np.arange(len(data3)), data3, 'd', label='data3')
ax.legend()
matplotlib中的图例在布局、位置以及它们可以代表的Artist方面非常灵活。它们在图例指南中有详细讨论。
刻度放缩
除了线性刻度,matplotlib还提供非线性刻度,例如对数刻度。由于对数刻度的使用如此之多,因此也有直接的方法,如对数、半对数x和半对数y。在这里我们手动设置放缩。
python
fig, axs = plt.subplots(1, 2, figsize=(5, 2.7), layout='constrained')
xdata = np.arange(len(data1)) # make an ordinal for this
data = 10**data1
axs[0].plot(xdata, data)
axs[1].set_yscale('log')
axs[1].plot(xdata, data)
刻度设置从数据值到沿轴间距的映射。这在两个方向上发生,并组合成一个转换,这是matplotlib从数据坐标映射到轴、图形或屏幕坐标的方式。
刻度位置和格式
每个轴都有一个刻度定位器和格式化函数,用于选择沿Axis对象放置刻度线的位置。一个简单的界面是set_xticks
:
python
fig, axs = plt.subplots(2, 1, layout='constrained')
axs[0].plot(xdata, data1)
axs[0].set_title('Automatic ticks')
axs[1].plot(xdata, data1)
axs[1].set_xticks(np.arange(0, 100, 30), ['zero', '30', 'sixty', '90'])
axs[1].set_yticks([-1.5, 0, 1.5]) # note that we don't need to specify labels
axs[1].set_title('Manual ticks')
不同的比例可以有不同的定位器和格式化程序;例如,上面的对数刻度使用LogLocator和LogFormatter。有关其他格式化程序和定位器的信息,请参阅刻度定位器和刻度格式化程序,以及编写自己的定位器的程序。
绘制日期和字符串
matplotlib可以处理绘制日期数组和字符串数组,以及浮点数。它们会根据需要获得特殊的定位器和格式化程序。对于日期:
python
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
dates = np.arange(np.datetime64('2021-11-15'), np.datetime64('2021-12-25'),
np.timedelta64(1, 'h'))
data = np.cumsum(np.random.randn(len(dates)))
ax.plot(dates, data)
cdf = mpl.dates.ConciseDateFormatter(ax.xaxis.get_major_locator())
ax.xaxis.set_major_formatter(cdf)
python
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
categories = ['turnips', 'rutabaga', 'cucumber', 'pumpkins']
ax.bar(categories, np.random.rand(len(categories)))
关于分类绘图的一个警告是,某些分析文本文件的方法返回字符串列表,即使字符串都表示数字或日期。如果你传递 1000 个字符串,Matplotlib 会认为你的意思是 1000 个类别,并将在你的绘图中添加 1000 个刻度!
添加Axis对象
在同一个图表中绘制不同量级的数据,可能需要额外的y轴。可以通过使用twinx添加一个新Axis来创建。该轴具有不可见的x轴和位于右侧的y轴(类似于twiny)。
同样,你可以添加与主轴比例不同的secondary_xaxis或secondary_yaxis,以表示不同比例或单位的数据。
python
fig, (ax1, ax3) = plt.subplots(1, 2, figsize=(7, 2.7), layout='constrained')
l1, = ax1.plot(t, s)
ax2 = ax1.twinx()
l2, = ax2.plot(t, range(len(t)), 'C1')
ax2.legend([l1, l2], ['Sine (left)', 'Straight (right)'])
ax3.plot(t, s)
ax3.set_xlabel('Angle [rad]')
ax4 = ax3.secondary_xaxis('top', functions=(np.rad2deg, np.deg2rad))
ax4.set_xlabel('Angle [°]')
8 热力图
通常,我们希望在由颜色图中的颜色表示的图中具有第三个维度,matplotlib中有许多绘图类型可以做到这一点:
python
X, Y = np.meshgrid(np.linspace(-3, 3, 128), np.linspace(-3, 3, 128))
Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2)
fig, axs = plt.subplots(2, 2, layout='constrained')
pc = axs[0, 0].pcolormesh(X, Y, Z, vmin=-1, vmax=1, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[0, 0])
axs[0, 0].set_title('pcolormesh()')
co = axs[0, 1].contourf(X, Y, Z, levels=np.linspace(-1.25, 1.25, 11))
fig.colorbar(co, ax=axs[0, 1])
axs[0, 1].set_title('contourf()')
pc = axs[1, 0].imshow(Z**2 * 100, cmap='plasma',
norm=mpl.colors.LogNorm(vmin=0.01, vmax=100))
fig.colorbar(pc, ax=axs[1, 0], extend='both')
axs[1, 0].set_title('imshow() with LogNorm()')
pc = axs[1, 1].scatter(data1, data2, c=data3, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[1, 1], extend='both')
axs[1, 1].set_title('scatter()')
颜色图
这些都是派生自ScalarMappable对象的Artists示例。它们都可以将vmin和vmax之间的线性映射设置为由cmap指定的颜色图。
matplotlib有许多颜色图可供选择(在matplotlib中选择颜色图),你可以自己制作(在matplotlib中创建颜色图)或作为第三方软件包下载。
归一化
有时我们希望将数据非线性映射到颜色图,如上面的LogNorm示例所示。我们通过为ScalarMappable提供norm参数而不是vmin和vmax来做到这一点。
颜色条
添加颜色条提供了将颜色关联回基础数据的皱起这。颜色条是Figure级别的Artist对象,并附加到ScalarMappable,并且通常从父轴窃取空间。颜色条的放置可能很复杂。你还可以使用extend关键字更改颜色条的外观,以在末端添加箭头,并缩小和纵横以控制大小。最后,颜色条将具有适合规范的默认定位器和格式化程序。可以像更改其他Axis对象一样更改这些对象。
9 多个Figure和Axes同时绘制
你可以通过多次调用fig = plt.figure()或fig2, ax = plt.subplots()打开多个图形。通过保留对象引用,你可以将Artist添加到任一图形中。
可以通过多种方式添加多个Axes,但最基本的是上面使用的plt.subplots(),可以使用subplot_mosaic实现更复杂的布局。如下图这样,一边对象跨越多行或多列:
python
fig, axd = plt.subplot_mosaic([['upleft', 'right'],
['lowleft', 'right']], layout='constrained')
axd['upleft'].set_title('upleft')
axd['lowleft'].set_title('lowleft')
axd['right'].set_title('right')