本教程涵盖了一些基本的使用模式和最佳实践,以帮助您开始使用Matplotlib。
import matplotlib.pyplot as plt
import numpy as np
一个简单的例子
Matplotlib把你的数据画在画布上。创建带有坐标轴图形的最简单方法是使用pyplot.subplots。
fig,ax=plt.subplots() # 创建一个仅包含一个坐标系的画布
ax.plot([1,2,3,4],[1,4,2,3]) # 在坐标轴上画出数据点
图形的各部分解析:
现在,让我们更深入地了解Matplotlib图形的组件。
- Figure
一个Figure可以包含任意数量的Axes,但通常至少有一个。
创建新Figure最简单的方法是使用pyplot:
fig = plt.figure() # 没有Axes的空figure
fig,ax=plt.subplots() # 只有一个Axes的figure
fig,axs=plt.subplots(2,2) # 拥有2x2个Axes的figure
与Figure一起创建Axes很方便,但是您也可以稍后添加Axes,以实现更复杂的Axes布局。
-
Axes
这就是你所认为的“一个图”,它是图像的数据空间区域。一个给定的Figure可以包含多个Axes,但是一个给定的Axes对象只能在一个Figure中。Axes包含两个(在3D中是三个)Axis对象(注意Axes和Axis之间的区别),它们负责数据限制(数据限制也可以通过axes. Axes.set_xlim()和axes. Axes.set_ylim()方法进行控制)。每个Axes都有一个title(通过set_title()设置)、一个x-label(通过set_xlabel()设置)和一个y-label(通过set_ylabel()设置)。
Axes类及其成员函数是使用OO接口的主要入口点。 -
Axis
这些是像数轴一样的对象。它们负责设置图形范围并生成刻度(轴上的标记)和刻度标签(标记刻度的字符串)。刻度的位置由 Locator 对象确定,刻度标签字符串由 Formatter 格式化。正确的Locator和Formatter的组合可以很好地控制标记的位置和标签。 -
Artist
基本上,您在图上看到的所有东西都是Artist(甚至包括Figure、Axes和Axis对象)。这包括Text对象、Line2D对象、collections对象、Patch对象……(你懂的)。当图像被渲染后,所有的Artists都被画到画布上。大多数Artist都与 Axes 相关联;这样的Artist不能被多个 Axes 共享,也不能从一个移到另一个。
绘图函数的输入类型
所有的绘图函数的输入类型是numpy.array或numpy.ma.masked_array。“类似数组”的类,比如pandas数据对象和numpy.matrix可能会或不会像预期的那样工作。最好在绘制他们之前将它们转换为numpy.array对象。
例如,转换pandas.DataFrame对象
import pandas
import numpy as np
a = pandas.DataFrame(np.random.rand(4,5),columns=list('abcde'))
a_asarray = a.values
print(a_asarray)
# array([[0.12854025, 0.45893918, 0.3407472 , 0.34229837, 0.09487173],
# [0.052882 , 0.99353112, 0.61412885, 0.82294782, 0.72473975],
# [0.72731438, 0.96392168, 0.10910014, 0.78100158, 0.67933034],
# [0.35774273, 0.08887762, 0.10107095, 0.06548651, 0.45251323]])
print(type(a_asarray))
# numpy.ndarray
转换numpy.matrix对象
import numpy as np
b = np.matrix([[1,2],[3,4]])
b_asarray = np.asarray(b)
print(type(b))
print(type(b_asarray))
# <class 'numpy.matrix'>
# <class 'numpy.ndarray'>
面向对象接口和pyplot接口
如上所述,基本上有两种使用 Matplotlib 的方法:
- 显式创建图形和轴,并在其上调用方法(“面向对象(OO)风格”)。
- 依靠pyplot自动创建和管理图形和轴,并使用pyplot函数进行绘图。
(1) oo风格
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,2,100)
# Note that even in the OO-style, we use .pyplot.figure to create the figure
fig,ax = plt.subplots() # 创建画布和坐标系
ax.plot(x, x, label="linear") # 在坐标系中画一些数据
ax.plot(x, x**2, label="quadratic") # 在坐标系中画一些数据
ax.plot(x, x**3, label="cubic") # 在坐标系中画一些数据
ax.set_xlabel('x label') # x轴标签
ax.set_ylabel('y label') # y轴标签
ax.set_title("Simple Plot") # 坐标系主题
ax.legend() # 添加legend
plt.show()
(2) pyplot风格
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,2,100)
plt.plot(x, x, label="linear") # 在坐标系中画一些数据
plt.plot(x, x**2, label="quadratic") # 在坐标系中画一些数据
plt.plot(x, x**3, label="cubic") # 在坐标系中画一些数据
plt.xlabel('x label') # x轴标签
plt.ylabel('y label') # y轴标签
plt.title("Simple Plot") # 坐标系主题
ax.legend() # 添加legend
plt.show()
此外,还有第三种方法,用于在GUI应用程序中嵌入Matplotlib时,它完全删除了pyplot,甚至用于图形创建。我们不在这里讨论;更多信息请参见图库中相应的部分(在图形用户界面中嵌入Matplotlib)。
Matplotlib的文档和示例同时使用了OO和pyplot方法(它们同样强大),您可以任意使用其中一种方法(不过,最好选择一种并坚持使用,而不是混合使用它们)。一般来说,我们建议交互式绘图(例如,在Jupyter笔记本中)使用pyplot,对于非交互式绘图(在打算作为更大项目的一部分重用的函数和脚本中),我们更喜欢oo风格。
通常情况下,人们会发现自己反复绘制相同的图,但使用不同的数据集,这导致需要编写专门的函数来绘制图。推荐的函数模板如下:
def my_plotter(ax, data1, data2, param_dict):
"""
A helper function to make a graph
Parameters:
---------
ax:Axes the axes to draw to
data1:data the X data
data2:data the Y data
param_dict:dict Dictionary of kwargs to pass to ax.plot
Returns:
----------
out:list list of artists added
"""
out = ax.plot(data1, data2, **param_dict)
return out
# 举例
import numpy as np
import matplotlib.pyplot as plt
data1,data2,data3,data4=np.random.randn(4,100)
fig, ax=plt.subplots(1,1)
my_plotter(ax, data1, data2, {'marker':'x'})
plt.show()
如果你想要两个子图:
import numpy as np
import matplotlib.pyplot as plt
data1,data2,data3,data4=np.random.randn(4,100)
fig, (ax1, ax2)=plt.subplots(1,2)
my_plotter(ax1, data1, data2, {'marker':'x'})
my_plotter(ax2, data3, data4, {'marker':'o'})
plt.show()
对于这些简单的例子来说,这种风格似乎有点过火,但是一旦图表变得稍微复杂一点,它就会得到回报。
后端
什么是后端?
有些人在python shell中交互式地使用Matplotlib,并在键入命令时弹出绘图窗口。有些人运行Jupyter notebook,绘制图以快速分析数据。另一些则将Matplotlib嵌入图形用户界面(如PyQt或PyGObject),以构建丰富的应用程序。有一些人运行web应用服务器动态地支持图形。
为了支持所有这些用例,Matplotlib可以针对不同的输出,每个功能都被称为后端;“前端”是面向用户的代码,即绘图代码,而“后端”则完成所有幕后的艰苦工作,以制作图形。有两种类型的后端:用户界面后端(用于PyQt/PySide, PyGObject, Tkinter, wxPython,或macOS/Cocoa);也被称为“交互式后端”和硬拷贝后端来制作图像文件(PNG, SVG, PDF, PS;也被称为“非交互式后端”)。
选择一个后端
有三种方式来配置你的后端:
- matplotlibrc文件中的rcParams[“backend”]参数
- MPLBACKEND环境变量
- 函数matplotlib.use ()
下面给出了更详细的描述。
如果存在多个这样的配置,则列表中的最后一个优先;例如,调用matplotlib.use()将重写matplotlib中的设置。
如果没有显式设置后端,Matplotlib会根据系统上可用的内容和GUI事件循环是否已经在运行,自动检测可用后端。在Linux上,如果没有设置环境变量DISPLAY,则“事件循环”被标识为“headless”,这将导致回退到非交互后端(agg);在所有其他情况下,首选交互式后端(通常,至少tkagg是可用的)。
下面是配置方法的详细说明:
-
在matplotlib/matplotlibrc文件中设置rcParams[“backend”]:
backend:Agg
-
设置MPLBACKEND环境变量:
您可以为当前shell或单个脚本设置环境变量。
(1)Unix:# 方法一 export MPLBACKEND=qt5agg python simple_plot.py # 方法二 MPLBACKEND=qt5agg python simple_plot.py
(2) Windows中只有前者是可能的:
set MPLBACKEND=qt5agg python simple_plot.py
设置此环境变量将覆盖任何matplotlibrc文件中的后端参数,即使当前工作目录中存在matplotlibrc。因此,全局设置MPLBACKEND,例如在.bashrc或.profile中,是不鼓励的,因为它可能会导致反直觉的行为。
-
如果你的脚本依赖于一个特定的后端,你可以使用函数matplotlib.use():
import matplotlib
matplotlib.use('qt5agg')
这应该在创建任何图形之前完成,否则Matplotlib可能无法切换后端并引发ImportError。
如果用户想要使用不同的后端,使用use将需要更改代码。因此,除非绝对必要,否则应该避免显式调用use。
- 内嵌后端
默认情况下,Matplotlib应该自动选择一个默认后端,它允许交互工作和从脚本绘图,并输出到屏幕和/或文件,所以至少在最初,您不需要担心后端。最常见的异常是,如果您的Python发行版没有tkinter,并且您没有安装其他GUI工具包。这种情况发生在某些Linux发行版上,您需要安装一个名为python-tk(或类似的)的Linux包。
但是,如果您想要编写图形用户界面,或者web应用服务器(嵌入web应用服务器(Flask)),或者需要更好地理解正在发生的事情,请继续阅读。为了让图形用户界面更易于定制,Matplotlib将渲染器(实际进行绘图的东西)的概念与画布(绘图的位置)分离。用户界面的典型渲染器是Agg,它使用 Anti-Grain Geometry C++库来制作图形的光栅(像素)图像;它被Qt5Agg, Qt4Agg, GTK3Agg, wxAgg, TkAgg和macosx后端使用。另一种渲染器基于Cairo库,由Qt5Cairo、Qt4Cairo等使用。
对于渲染引擎,还可以分为矢量渲染器和栅格渲染器。下面是Matplotlib渲染器的概要(每个渲染器都有一个同名的后端;这些是非交互式后端,能够写入文件):
要使用非交互式后端保存图形,请使用matplotlib.pyplot.savefig(‘filename’) 方法。
这里是支持的用户界面和渲染器组合;这些是交互式后端,能够显示到屏幕上,并使用来自上表的适当的渲染器写入文件:
内建后端名称不区分大小写;例如,“Qt5Agg”和“Qt5Agg”是等价的。
什么是交互模式?
使用交互式后端允许–但其本身并不要求或确保–将图形绘制到屏幕上。是否发生以及何时发生向屏幕绘图,以及在屏幕上绘制绘图后脚本或shell会话是否继续,都取决于所调用的函数和方法,以及确定Matplotlib是否处于“交互模式”的状态变量。默认布尔值由matplotlibrc文件设置,可以像任何其他配置参数一样自定义。它也可以通过matplotlib.interactive()设置,它的值可以通过matplotlib.is_interactive()查询。在绘图命令流中打开或关闭交互模式(无论是在脚本中还是在shell中)是很少需要的,而且可能会让人感到困惑。在下文中,我们将假设所有绘图都是在开启或关闭交互模式的情况下完成的。
交互模式也可以通过matplotlib.pyplot.ion()打开,通过matplotlib.pyplot.ioff()关闭。
交互的例子
# 在普通的python提示符中,或者在没有选项的调用ipython之后,尝试如下
import matplotlib.pyplot as plt
plt.ion()
plt.plot([1.6, 2.7])
这将弹出一个绘图窗口。你的终端提示符将保持活动状态,因此你可以输入额外的命令,如:
plt.title("interactive test")
plt.xlabel("index")
在大多数交互式后端中,如果您通过面向对象接口更改图形窗口,它也将被更新。例如,获取一个Axes实例的引用,并调用该实例的一个方法:
ax = plt.gca()
ax.plot([3.1, 2.2])
如果您正在使用某些后端(如macosx)或较老版本的Matplotlib,您可能不会立即看到添加到plot中的新线条。在这种情况下,你需要显式地调用draw()来更新plot:
plt.draw()
非交互式的例子
像前面的例子一样开始一个新的会话,但是现在关闭交互模式:
import matplotlib.pyplot as plt
plt.ioff()
plt.plot([1.6, 2.7])
什么都没有发生——或者至少什么都没有显示在屏幕上(除非您正在使用macosx后端,这是不正常的)。为了让plot出现,你需要这样做:
plt.show()
现在您看到了这个图,但是您的终端命令行没有响应;pyplot.show()命令会阻止其他命令的输入,直到您手动杀死绘图窗口。
这有什么好处——被迫使用阻塞函数?假设您需要一个脚本来将文件的内容绘制到屏幕上。你想看一下plot,然后结束脚本。如果没有show()这样的阻塞命令,脚本就会弹出plot,然后立即结束,在屏幕上什么也不留下。
此外,非交互模式会延迟所有绘制,直到调用show();这比每次脚本中添加新功能时重新绘制plot更有效。
在1.0版本之前,show()通常不能在单个脚本中调用不止一次(尽管有时可以使用它);对于版本1.0.1及更高版本,这个限制被取消了,所以可以这样写一个脚本:
import numpy as np
import matplotlib.pyplot as plt
plt.ioff()
for i in range(3):
plt.plot(np.random.rand(10))
plt.show()
这就形成了三个plot,一次一个。也就是说,一旦第一个plot结束,第二个plot就会出现。
总结
在交互模式下,pyplot函数会自动绘制到屏幕上。
在交互式绘图时,如果使用pyplot函数之外的对象方法调用,则在需要刷新绘图时调用draw()。
如果希望生成一个或多个图形并在结束或生成一组新的图形之前显示它们,请在脚本中使用非交互模式。在这种情况下,使用show()显示图形并阻止执行,直到手动销毁它们。