Matplotlib指南

本教程介绍了一些基本的使用模式和最佳实践,以帮助您开始使用Matplotlib。

常规概念

matplotlib拥有广泛的代码库,对许多新用户来说可能是令人生畏的。但是,大多数matplotlib都可以通过一个相当简单的概念框架和一些重要知识来理解。

绘图需要在一系列级别上进行操作,从最一般的(例如,“画侧像2-D阵列”)到最具体的(例如,“将屏幕像素颜色设置为红色”)。绘图包的目的是帮助您尽可能轻松地可视化数据,并提供所有必要的控制 - 即通过在大多数时间使用相对高级别的命令,并且仍然能够使用低需要时使用-level命令。

因此,matplotlib中的所有内容都按层次结构进行组织。在层次结构的顶部是matplotlib状态机环境,它由matplotlib.pyplot模块提供。在此级别,简单函数用于将绘图元素(线条,图像,文本等)添加到当前图形中的当前轴。

注意:Pyplot的状态机环境与MATLAB的行为类似,对于具有MATLAB经验的用户来说应该是很熟悉的。

层次结构中的下一级是面向对象的接口的第一级,其中pyplot仅用于少数函数,例如图形创建,并且用户显式创建并跟踪图形和轴对象。在此级别,用户使用pyplot来创建图形,并且通过这些图形,可以创建一个或多个轴对象。然后,这些轴对象用于大多数绘图操作。

对于更多的控制 - 对于在GUI应用程序中嵌入matplotlib图这一点至关重要 - 可以完全删除pyplot级别,留下面向对象的方法。

# sphinx_gallery_thumbnail_number = 3
import matplotlib.pyplot as plt
import numpy as np

图的一部分

Figure

整个Figure记录了所有子轴,一些“特殊”艺术家(标题,图形图例等)和画布。(不要过于担心画布,它是至关重要的,因为实际上绘制的对象可以获得plot,但是作为用户它或多或少是不可见的)。一个Figure可以有任意数量的Axes,但是有用的应该至少有一个。

创建新 figure 的最简单方法是使用pyplot

fig = plt.figure()  # an empty figure with no axes
fig.suptitle('No axes on this figure')  # Add a title so we know which it is

fig, ax_lst = plt.subplots(2, 2)  # a figure with a 2x2 grid of Axes


Axes

plot是具有数据空间的图像区域。给定的图形可以包含许多轴,但给定的Axes对象只能在一个图中。Axes包含两个(或3D的三个)Axis对象(注意AxesAxis的区别),它们负责数据限制(数据限制也可以通过Axes的set_xlim()set_ylim()方法来设置)。每个Axes都有一个标题(通过set_title()设置),一个x标签(通过set_xlabel()设置)和一个y标签(通过set_ylabel()设置)。

Axes类及其成员函数是使用OO接口的主入口点。

Axis

它类似数字行的对象。它们负责设置图形限制并生成刻度线(轴上的标记)和ticklabels(标记刻度线的字符串)。刻度的位置由Locator对象确定,ticklabel字符串由Formatter格式化。正确的定位器和格式化器的组合可以非常精确地控制刻度位置和标签。

Artist

基本上在图上看到的一切都是Artist(甚至是图,轴和轴对象)。这包括Text对象,Line2D对象,collection对象,Patch对象等等。渲染图形时,所有Artist都被绘制到画布上。大多数Artist都与轴有关; 这样的Artist不能被多个轴共享,也不能从一个轴移动到另一个轴。

绘制函数的输入类型

所有绘图函数都期望np.arraynp.ma.masked_array作为输入。像“数组一样”的类(如pandas数据对象和np.matrix)可能会或可能不会按预期工作。最好在绘图之前将它们转换为np.array对象。

例如,要转换pandas.DataFrame

a = pandas.DataFrame(np.random.rand(4,5), columns = list('abcde'))
a_asarray = a.values

并转换np.matrix,如下代码所示 -

b = np.matrix([[1,2],[3,4]])
b_asarray = np.asarray(b)

Matplotlib,pyplot和pylab之间的关系

Matplotlib是整个包,matplotlib.pyplot是Matplotlib中的一个模块。

对于pyplot模块中的功能,始终存在“当前”图形和轴(根据请求自动创建)。例如,在下面的示例中,第一次调用plt.plot会创建轴,然后对plt.plot的后续调用会在相同的轴上添加其他行,并且plt.xlabelplt.ylabelplt.titleplt.legend设置轴标签和标题并添加图例。

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【许虎虎】,商业转载请联系作者获得授权,非商业请保留原文链接:

x = np.linspace(0, 2, 100)
plt.plot(x, x, label='线性')
plt.plot(x, x**2, label='二次方')
plt.plot(x, x**3, label='立方')

plt.xlabel('X轴标签')
plt.ylabel('Y轴标签')

plt.title("简单绘制(图表)")

plt.legend()
plt.show()

执行上面示例代码,得到以下结果 -

pylab是一个便利模块,可以在单个命名空间中批量导入matplotlib.pyplot(用于绘图)和numpy(用于数学和使用数组)。但不推荐使用pylab,由于命名空间污染而强烈建议不要使用它,请改用pyplot

对于非交互式绘图,建议使用pyplot创建图形,然后使用OO界面进行绘图。

编码样式

查看此文档和示例时,您将看到有不同的编码样式和使用模式。这些风格完全有效,各有利弊。几乎所有示例都可以转换为另一种样式并获得相同的结果。唯一需要注意的是避免为自己的代码混合编码样式。

在不同的风格中,有两种是官方支持的。这些是使用matplotlib的首选方法。
对于pyplot样式,通常在脚本顶部的导入:

import matplotlib.pyplot as plt
import numpy as np

然后一个调用,例如,np.arangenp.zerosnp.piplt.figureplt.plotplt.show等。使用pyplot接口创建图形,然后使用对象方法,参考以下示例代码:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【许虎虎】,商业转载请联系作者获得授权,非商业请保留原文链接:

x = np.arange(0, 10, 0.2)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()

执行上面示例代码,得到以下结果 -

编码样式

那么,为什么所有额外的输入代替了MATLAB风格(依赖于全局状态和平面命名空间)? 对于像这个例子这样非常简单的事情,唯一的优势就是学术上的:更多的风格更明确,更明确地指出事物的来源和发生的事情。对于更复杂的应用程序,这种显式性和清晰度变得越来越有价值,更丰富和更完整的面向对象的接口可能使程序更容易编写和维护。

人们发现自己经常一遍又一遍地制作相同的图,但只是使用不同的数据集,这导致需要编写专门的函数来进行绘图。推荐的函数签名类似于:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【许虎虎】,商业转载请联系作者获得授权,非商业请保留原文链接:

def my_plotter(ax, data1, data2, param_dict):
    """
    A helper function to make a graph

    Parameters
    ----------
    ax : Axes
        The axes to draw to

    data1 : array
       The x data

    data2 : array
       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

# which you would then use as:

data1, data2, data3, data4 = np.random.randn(4, 100)
fig, ax = plt.subplots(1, 1)
my_plotter(ax, data1, data2, {'marker': 'x'})

plt.show()

执行上面示例代码,得到以下结果 -

或者如果想要2个子图,那么可以使用以下代码:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【许虎虎】,商业转载请联系作者获得授权,非商业请保留原文链接:

def my_plotter(ax, data1, data2, param_dict):
    """
    A helper function to make a graph

    Parameters
    ----------
    ax : Axes
        The axes to draw to

    data1 : array
       The x data

    data2 : array
       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

# which you would then use as:

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()

执行上面示例代码,得到以下结果 -

2个子图

同样,对于这些简单的例子,这种风格似乎有些过分,但是一旦图表变得稍微复杂一点,就会有好处。

后端

什么是后端?

网站和邮件列表中的大量文档都是指“后端”,许多新用户对此术语感到困惑。matplotlib针对许多不同的用例有不同的输出格式。有些人在python shell中交互使用matplotlib,并在键入命令时弹出绘图窗口。有些人运行Jupyter笔记本并绘制内联图以进行快速数据分析。其他人将matplotlib嵌入到图形用户界面(如wxpython或pygtk)中以构建丰富的应用程序。有些人在批处理脚本中使用matplotlib从数值模拟生成postscript图像,还有一些人运行Web应用程序服务器来动态提供图形。

为了支持所有这些用例,matplotlib可以针对不同的输出,并且这些功能中的每一个都称为后端; “前端”是面向用户的代码,即绘图代码,而“后端”完成幕后的所有艰苦工作以制作图形。有两种类型的后端:用户界面后端(用于pygtk,wxpython,tkinter,qt4或macosx;也称为“交互式后端”)和硬拷贝后端来制作图像文件(PNG,SVG,PDF,PS;也被称为“非交互式后端”)。

配置后端有四种方法。如果它们彼此冲突,将使用以下列表中最后提到的方法,例如,调用use()将覆盖matplotlibrc中的设置。

第1步 - matplotlibrc文件中的后端参数:

backend : WXAgg   # use wxpython with antigrain (agg) rendering

第2步 - 为当前shell或单个脚本设置MPLBACKEND环境变量。在Unix上:

> export MPLBACKEND=module://my_backend
> python simple_plot.py

> MPLBACKEND="module://my_backend" python simple_plot.py

在Windows上,格式为:

> set MPLBACKEND=module://my_backend
> python simple_plot.py

设置此环境变量将覆盖任何matplotlibrc中的后端参数,即使当前工作目录中存在matplotlibrc也是如此。因此,全局设置MPLBACKEND,例如 在.bashrc.profile中,但不鼓励这样操作,因为它可能导致反直觉的行为。

第3步 - 如果脚本依赖于特定的后端,则可以这样使用use()函数:

import matplotlib
matplotlib.use('PS')   # generate postscript output by default

如果要使用use()函数,则必须在导入matplotlib.pyplot之前完成此操作。导入pyplot后调用use()将不起作用。如果用户想要使用不同的后端,则使用use()将需要更改代码。因此,除非绝对必要,否则应避免显式调用use()函数。

注 - 后端名称规范不区分大小写; 例如,’GTK3Agg’和’gtk3agg’是相同的。

通过matplotlib的典型安装,例如二进制安装程序或Linux发行包,可以设置一个好的默认后端,允许交互式工作和从脚本绘图,输出到屏幕和/或文件,所以最初不需要使用上面的任何方法。

但是,如果想编写图形用户界面或Web应用程序服务器(Web应用程序服务器中的Matplotlib),或者需要更好地了解正在发生的事情,请继续阅读。为了使图形用户界面可以更加自定义,matplotlib将画布(绘图所在的位置)的渲染器(实际绘制的东西)的概念分开。用户界面的规范渲染器是Agg,它使用Anti-Grain Geometry C++库来制作图形的光栅(像素)图像。除macosx之外的所有用户界面都可以与agg渲染一起使用,例如:WXAgg,GTK3Agg,QT4Agg,QT5Agg,TkAgg。此外,一些用户界面支持其他渲染引擎。例如,使用GTK + 3,还可以选择开罗渲染(后端GTK3Cairo)。

对于渲染引擎,还可以区分矢量或光栅渲染器。矢量图形语言发出绘图命令,例如“从此点到此点绘制一条线”,因此无标度,并且栅格后端生成线的像素表示,其精度取决于DPI设置。

以下是matplotlib渲染器的摘要(每个渲染器都有一个同名的后端;这些是非交互式后端,能够写入文件):

渲染 文件类型 描述说明
AGG png 光栅图形 - 使用Anti-Grain Geometry引擎的高质量图像。
PS ps eps 矢量图形 - Postscript输出
PDF pdf 矢量图形 - 可移植文档格式
SVG svg 矢量图形 - 可缩放矢量图形
Cairo png ps pdf svg 光栅图形和矢量图形 - 使用Cairo图形库

以下是支持的用户界面和渲染器组合; 这些是交互式后端,能够显示到屏幕并使用上表中的适当渲染器写入文件:

后端 描述
Qt5Agg 在Qt5画布中进行Agg渲染(需要PyQt5)。可以使用%matplotlib qt5在IPython中激活此后端。
ipympl 嵌入在Jupyter小部件中的Agg渲染(需要ipympl)。可以在带有%matplotlib ipympl的Jupyter Notebook中启用此后端。
GTK3Agg Agg渲染到GTK 3.x画布(需要PyGObject,pycairo或cairocffi)。可以使用%matplotlib gtk3在IPython中激活此后端。
macosx 在OSX中将渲染转换为Cocoa画布。可以使用%matplotlib osx在IPython中激活此后端。
TkAgg Agg渲染到Tk画布(需要TkInter)。可以使用%matplotlib tk在IPython中激活此后端。
nbAgg 在Jupyter Notebook中嵌入一个互动图像。可以通过%matplotlib Notebook在Jupyter Notebook中启用此后端。
WebAgg show()上将启动一个带有交互式图形的 tornado 服务器。
GTK3Cairo Cairo渲染到GTK 3.x画布(需要PyGObject,pycairo或cairocffi)。
Qt4Agg Agg渲染到Qt4画布(需要PyQt4或pyside)。可以使用%matplotlib qt4在IPython中激活此后端。
WXAgg Agg渲染到wxWidgets画布(需要wxPython 4)。可以使用%matplotlib wx在IPython中激活此后端。

ipympl

Jupyter小部件生态系统的移动速度太快,无法直接在Matplotlib中支持。安装ipympl -

pip install ipympl
jupyter nbextension enable --py --sys-prefix ipympl

或者 -

conda install ipympl -c conda-forge

GTK和Cairo

GTK3后端(GTK3Agg和GTK3Cairo)依赖于开罗(pycairo> = 1.11.0或cairocffi)。

如何选择PyQt4或PySide?
QT_API环境变量可以设置为pyqt或pyside,分别使用PyQt4或PySide。

由于要使用的绑定的默认值是PyQt4,matplotlib首先尝试导入它,如果导入失败,它会尝试导入PySide。

什么是交互模式?

使用交互式后端允许 - 但本身不需要或确保绘制到屏幕上。是否以及何时绘制到屏幕,以及在屏幕上绘制绘图后是否继续脚本或shell会话取决于调用的函数和方法,以及确定matplotlib是否处于“交互模式”的状态变量”。默认的布尔值由matplotlibrc文件设置,并且可以像任何其他配置参数一样进行自定义(请参阅使用样式表和rcParams自定义Matplotlib)。它也可以通过matplotlib.interactive()设置,并且可以通过matplotlib.is_interactive()查询其值。在绘图命令流中间打开和关闭交互模式,无论是在脚本还是在shell中,很少需要并且可能令人困惑,因此在下文中我们将假设所有绘图都是以交互模式打开或关闭。

注 - 在交互到matplotlib版本1.0时,与交互性相关的主要更改,特别是show()的角色和行为,在1.0.1中修复了错误。这里描述主要交互式后端的版本1.0.1行为,部分例外是macosx。

交互模式也可以通过matplotlib.pyplot.ion()打开,并通过matplotlib.pyplot.ioff()关闭。

交互示例

从普通的python提示符,或者在没有选项的情况下调用ipython之后,试试以下:

import matplotlib.pyplot as plt
plt.ion()
plt.plot([1.6, 2.7])

假设您运行的是1.0.1或更高版本,并且默认情况下安装并选择了交互式后端,应该看到一个图,并且终端提示也应该是活动的; 可以键入其他命令,例如:

plt.title("交互测试")
plt.xlabel("index")

会看到每一行后都更新了绘图。从版本1.5开始,通过其他方式修改绘图也应该自动更新大多数后端的显示。获取对Axes实例的引用,并调用该实例的方法:

ax = plt.gca()
ax.plot([3.1, 2.2])

如果使用的是某些后端(如macosx)或旧版本的matplotlib,则可能无法立即将新行添加到绘图中。在这种情况下,需要显式调用draw()以更新绘图:

plt.draw()

非交互式示例

像上一个示例中一样,开始一个新会话,但现在关闭交互模式:

import matplotlib.pyplot as plt
plt.ioff()
plt.plot([1.6, 2.7])

执行上面代码什么都没发生 - 或者至少没有任何东西出现在屏幕上(除非使用macosx后端,那么会产生异常)。要显示绘图,需要执行以下操作:

plt.show()

现在应该看到了绘图,但终端命令行没有响应; show()命令会阻止其他命令的输入,直到手动终止绘图窗口。

这有什么好处? - 被迫使用阻止功能。假设您需要一个脚本,将文件内容绘制到屏幕上。想要查看该图,然后结束脚本。如果没有一些阻塞命令(如show()),脚本会闪现绘图,然后立即结束,屏幕上不显示任何内容。

此外,非交互模式会延迟所有绘图,直到调用show()为止; 每次脚本中的一行添加新功能时,这比重绘绘图更有效。

在版本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()

它将一次绘制三个图。第一个绘图结束后,第二个绘图将出现。

总结

在交互模式下,pyplot功能会自动绘制到屏幕上。交互式绘制时,如果除了pyplot函数之外还使用对象方法调用,则每当要刷新绘图时调用draw()

在要生成一个或多个图形的脚本中使用非交互模式,并在结束或生成一组新图形之前显示它们。在这种情况下,使用show()显示图形并阻止执行,直到手动销毁它们。

性能

无论是以交互模式探索数据还是以编程方式保存大量绘图,渲染性能都可能是您管道中的一个瓶颈。Matplotlib提供了几种方法来大大减少渲染时间,但代价是绘图外观略有变化(可设置的容差)。可用于缩短渲染时间的方法取决于正在创建的绘图类型。

线段简化

对于具有线段的图(例如,典型的线图,多边形的轮廓等),可以通过matplotlibrc文件中的path.simplifypath.simplify_threshold参数控制渲染性能。path.simplify参数是一个布尔值,表示线段是否完全简化。path.simplify_threshold参数控制简化的线段数量; 更高的阈值会导致更快的渲染。

以下脚本将首先显示数据而不进行任何简化,然后简化显示相同的数据。尝试与它们交互:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

# Setup, and create the data to plot
y = np.random.rand(100000)
y[50000:] *= 2
y[np.logspace(1, np.log10(50000), 400).astype(int)] = -1
mpl.rcParams['path.simplify'] = True

mpl.rcParams['path.simplify_threshold'] = 0.0
plt.plot(y)
plt.show()

mpl.rcParams['path.simplify_threshold'] = 1.0
plt.plot(y)
plt.show()

Matplotlib目前默认为1/9的保守简化阈值。如果要更改默认设置以使用其他值,可以更改matplotlibrc文件。或者可以为交互式绘图(具有最大简化)创建新样式,并为发布质量绘图创建另一种样式(最小化简化)并根据需要激活它们。有关如何执行这些操作的说明,请参阅使用样式表和rcParams自定义Matplotlib。

通过将线段迭代地合并成单个矢量直到下一个线段与矢量的垂直距离(在显示坐标空间中测量)大于path.simplify_threshold参数,简化工作。

注意 - 与版本细分如何简化相关的更改在版本2.1中进行。2.1之前的这些参数仍将提高渲染时间,但2.1版及更高版本的某些类型数据的渲染时间将大大改善。

标记简化

标记也可以简化,尽管不如线段强大。标记简化仅适用于Line2D对象(通过markevery属性)。无论在哪里传递Line2D构造参数,例如matplotlib.pyplot.plot()matplotlib.axes.Axes.plot(),都可以使用markevery参数:

plt.plot(x, y, markevery=10)

将线分成较小的块

如果您正在使用Agg后端,那么可以使用agg.path.chunksize rc参数。它指定块大小,并且具有大于该多个顶点的任何行将被分割成多行,每行不超过agg.path.chunksize许多顶点。(除非agg.path.chunksize为零,在这种情况下没有分块。)对于某些类型的数据,将线条分成合理的大小可以大大减少渲染时间。

以下脚本将首先显示没有任何块大小限制的数据,然后显示块大小为10,000的相同数据。当数字很大时,可以最好地看到差异,尝试最大化GUI然后与它们进行交互:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['path.simplify_threshold'] = 1.0

# Setup, and create the data to plot
y = np.random.rand(100000)
y[50000:] *= 2
y[np.logspace(1,np.log10(50000), 400).astype(int)] = -1
mpl.rcParams['path.simplify'] = True

mpl.rcParams['agg.path.chunksize'] = 0
plt.plot(y)
plt.show()

mpl.rcParams['agg.path.chunksize'] = 10000
plt.plot(y)
plt.show()

Legends

轴的默认图例行为尝试查找覆盖最少数据点的位置(loc ='best')。如果有大量数据点,这可能是非常昂贵的计算。在这种情况下,可能希望提供特定位置。

使用快速的风格

快速样式可用于自动将简化和分块参数设置为合理设置,以加快绘制大量数据的速度。它可以通过运行简单地使用:

import matplotlib.style as mplstyle
mplstyle.use('fast')

它重量很轻,所以它与其他风格很好地搭配,只需确保最后应用快速样式,以便其他样式不会覆盖设置:

mplstyle.use(['dark_background', 'ggplot', 'fast'])

上一篇: Matplotlib变换 下一篇: Matplotlib pyplot教程