A picture says a thousand words, and with Python’s matplotlib library, it fortunately takes far less than a thousand words of code to create a production-quality graphic.
一幅图片说出一千个单词,而使用Python的matplotlib库,幸运的是,它只需不到一千个单词的代码即可创建出高质量的图形。
However, matplotlib is also a massive library, and getting a plot to look “just right” is often practiced on a trial-and-error basis. Using one-liners to generate basic plots in matplotlib is fairly simple, but skillfully commanding the remaining 98% of the library can be daunting.
但是,matplotlib还是一个庞大的库,通常在反复试验的基础上练习使图看起来“恰到好处”。 在matplotlib中使用单线生成基本图非常简单,但是熟练地命令库的其余98%可能会令人望而生畏。
This article is a beginner-to-intermediate-level walkthrough on matplotlib that mixes theory with example. While learning by example can be tremendously insightful, it helps to have even a surface-level understanding of the library’s inner workings and layout as well.
本文是matplotlib的初学者到中级的演练,它将理论与示例相结合。 虽然通过示例学习可以非常有洞察力,但是即使对图书馆的内部运作和布局也有了表面的了解,也有帮助。
Here’s what we’ll cover:
这是我们要介绍的内容:
plt.subplots()
plt.subplots()
Free Bonus: Click here to download 5 Python + Matplotlib examples with full source code that you can use as a basis for making your own plots and graphics.
免费奖金: 单击此处下载5个Python + Matplotlib示例,其中包含完整的源代码 ,您可以将其用作制作自己的图和图形的基础。
This article assumes the user knows a tiny bit of NumPy; we’ll mainly use the numpy.random
module to generate “toy” data, drawing samples from different statistical distributions.
本文假定用户了解一点点NumPy。 我们将主要使用numpy.random
模块生成“玩具”数据,从不同的统计分布中抽取样本。
If you don’t already have matplotlib installed, see here for a walkthrough before proceeding.
如果尚未安装matplotlib,请在继续操作之前查看此处的演练。
Learning matplotlib can be a frustrating process at times. The problem is not that matplotlib’s documentation is lacking (it’s extensive, actually). But, what can be challenging is that:
有时学习matplotlib可能是一个令人沮丧的过程。 问题不在于缺少matplotlib的文档(实际上,它是广泛的)。 但是,具有挑战性的是:
And so, before we get to any glitzy examples, it’s useful to grasp the core concepts of matplotlib’s design.
因此,在介绍任何浮华的示例之前,掌握matplotlib设计的核心概念很有用。
A bit of history: John D. Hunter, a neurobiologist, began developing matplotlib around 2003, originally inspired to emulate commands from Mathworks’ MATLAB software. John passed away tragically young at age 44, in 2012, and matplotlib is now a full-fledged community effort, developed and maintained by a host of others. (John talked about the evolution of matplotlib at the 2012 SciPy conference, which is worth a watch.)
一段历史:神经生物学家约翰·亨特(John D. Hunter)于2003年左右开始开发matplotlib,最初的灵感是模仿MathWorks的MATLAB软件中的命令。 John于2012年不幸去世,享年44岁,而matplotlib现在是由许多其他人开发和维护的成熟的社区活动。 (John在2012年SciPy会议上谈到了matplotlib的演变,值得关注。)
One relevant feature of MATLAB is its global style. The Python concept of importing is not heavily used in MATLAB, and most of MATLAB’s functions are readily available to the user at the top level.
MATLAB的一个相关功能是其全局样式 。 Python的导入概念并未在MATLAB中大量使用,并且顶层的用户可以轻松使用大多数MATLAB 函数 。
Knowing that matplotlib has its roots in MATLAB helps to explain why pylab exists. pylab is a module within the matplotlib library that was built to mimic MATLAB’s global style. It exists only to bring a number of functions and classes from both NumPy and matplotlib into the namespace, making for an easy transition for former MATLAB users who were not used to needing import
statements. Ex-MATLAB converts (who are all fine people, I promise!) liked this functionality, because with from pylab import *
, they could simply call plot()
or array()
directly, as they would in MATLAB.
知道matplotlib起源于MATLAB有助于解释为什么pylab存在。 pylab是matplotlib库中的一个模块,旨在模拟MATLAB的全局样式。 它的存在只是将NumPy和matplotlib中的许多函数和类引入了命名空间 ,从而为不习惯使用import
语句的前MATLAB用户提供了轻松的过渡。 前MATLAB转换人员(我保证都是优秀的人!)喜欢此功能,因为使用from pylab import *
,它们可以直接调用plot()
或array()
,就像在MATLAB中一样。
The issue here may be apparent to some Python users: using from pylab import *
in a session or script is generally bad practice. Matplotlib now directly advises against this in its own tutorials:
对于某些Python用户而言,这里的问题可能很明显:在会话或脚本中使用from pylab import *
通常是不好的做法。 Matplotlib现在在自己的教程中直接建议不要这样做:
[pylab] still exists for historical reasons, but it is highly advised not to use. It pollutes namespaces with functions that will shadow Python built-ins and can lead to hard-to-track bugs. To get IPython integration without imports the use of the
%matplotlib
magic is preferred. [source][pylab]由于历史原因仍然存在,但强烈建议不要使用。 它使用将掩盖Python内置函数并可能导致难以跟踪的错误的函数污染名称空间。 要获得不导入的IPython集成,首选使用
%matplotlib
魔术。 [ 来源 ]
Internally, there are a ton of potentially conflicting imports being masked within the short pylab source. And in fact, using ipython --pylab
(from the terminal/command line) or %pylab
(from IPython/Jupyter tools) simply calls from pylab import *
under-the-hood.
在内部,简短的pylab 源码中掩盖了大量潜在冲突的进口。 实际上,使用ipython --pylab
(从终端/命令行)或%pylab
(从IPython / Jupyter工具)只需from pylab import *
调用。
The bottom line is that matplotlib has abandoned this convenience module and now explicitly recommends against using pylab, bringing things more in line with one of Python’s key notions–that explicit is better than implicit.
最重要的是, matplotlib放弃了此便利模块,现在明确建议不要使用pylab,使事情更符合Python的一个关键概念— 显式优于隐式 。
Without the need for pylab, we can usually get away with just one canonical import:
无需pylab,我们通常只需完成一次规范的导入即可:
>>> >>> import import matplotlib.pyplot matplotlib.pyplot as as plt
plt
Let’s also import NumPy while we’re at it, which we’ll use for generating data later on, and call np.random.seed()
to make examples with (pseudo)random data reproducible:
让我们在导入NumPy的同时导入它,稍后将使用它来生成数据,并调用np.random.seed()
来使(伪)随机数据的示例可重现:
One important big-picture matplotlib concepts is that of its object hierarchy.
关于matplotlib的一个重要概念是其对象层次结构 。
If you’ve worked through any introductory matplotlib tutorial, you’ve probably called something like plt.plot([1, 2, 3])
. This one-liner hides the fact that a plot is really a hierarchy of nested Python objects. A “hierarchy” here means that there is a tree-like structure of matplotlib objects underlying each plot.
如果您已经完成了matplotlib入门教程,则可能会调用诸如plt.plot([1, 2, 3])
。 这种单线隐藏了这样一个事实,即图实际上是嵌套的Python对象的层次结构。 这里的“层次结构”意味着每个图下面都有matplotlib对象的树状结构。
A Figure
object is the outermost container for a matplotlib graphic, which can contain multiple Axes
objects. One source of confusion is from this naming: an Axes
actually translates into what we think of as an individual plot or graph (rather than the plural of “axis”, as we might expect).
Figure
对象是matplotlib图形的最外面的容器,其中可以包含多个Axes
对象。 混淆的一个根源是这种命名方式: Axes
实际上转化为我们认为的单个图或图(而不是我们可能期望的“轴”的复数)。
You can think of the Figure
object as a box-like container holding one or more Axes
(actual plots). Below the Axes
in the hierarchy are smaller objects such as tick marks, individual lines, legends, and text boxes. Almost every “element” of a chart is its own manipulable Python object, all the way down to the ticks and labels:
您可以将Figure
对象视为一个盒Axes
器,其中包含一个或多个Axes
(实际图)。 层次结构中的“ Axes
下方是较小的对象,例如刻度线,单行,图例和文本框。 图表的几乎每个“元素”都是其自己的可操作的Python对象,一直到刻度和标签:
Here’s an illustration of this hierarchy in action. Don’t worry if you’re not completely familiar with this notation, which we’ll cover later on.
这是这种层次结构的示例。 如果您对这个符号不完全熟悉,请不要担心,我们将在稍后介绍。
>>> >>> figfig , , _ _ = = pltplt .. subplotssubplots ()
()
>>> >>> typetype (( figfig )
)
Above we created two variables with plt.subplots()
. The first is a top-level Figure
object; the second is a “throwaway” variable that we don’t need just yet, denoted with an underscore. Using attribute notation, it is easy to traverse down the figure hierarchy and see the first tick of the y axis of the first Axes object:
上面我们用plt.subplots()
创建了两个变量。 第一个是顶级Figure
对象; 第二个是我们现在不需要的“丢弃”变量,用下划线表示。 使用属性表示法,可以很容易地遍历图形层次结构并查看第一个Axes对象的y轴的第一个刻度 :
Above, fig
(a Figure
class instance) has multiple Axes
(a list, for which we take the first element). Each Axes
has a yaxis
and xaxis
, each of which have a collection of “major ticks,” and we grab the first one.
上面的fig
(一个Figure
类实例)有多个Axes
(一个列表,我们将其取为第一个元素)。 每个Axes
有一个yaxis
和xaxis
,每一个具有集“主刻度”,我们抢第一个。
Matplotlib presents this as a figure anatomy, rather than an explicit hierarchy:
Matplotlib将此表示为人物解剖,而不是明确的层次结构:
(In true matplotlib style, the figure above is created in the matplotlib docs here.)
(以真正的matplotlib样式,上图是在此处的matplotlib文档中创建的。)
Alright, we need one more chunk of theory before we can get around to the shiny visualizations–the difference between the stateful (a.k.a. state-based, state-machine) and stateless (a.k.a. object-oriented, OO) interfaces.
好了,我们需要进一步的理论知识,才能研究出闪亮的可视化效果– 有状态 (又名基于状态的状态机)与无状态 (又名面向对象OO)接口之间的区别。
Above, we used import matplotlib.pyplot as plt
to import the pyplot module from matplotlib and name it plt
.
上面,我们使用import matplotlib.pyplot as plt
从matplotlib导入pyplot模块并将其命名为plt
。
Almost all functions from pyplot, such as plt.plot()
, are implicitly either referring to an existing current Figure and current Axes, or creating them anew if none exist. Hidden in the matplotlib docs is this helpful snippet:
pyplot中的几乎所有函数(例如plt.plot()
都隐式地引用现有的当前Figure和当前Axes,或者如果不存在则重新创建它们。 matplotlib文档中隐藏了以下有用的代码段:
[With pyplot], simple functions are used to add plot elements (lines, images, text, etc.) to the current axes in the current figure. [emphasis added]
[使用pyplot],简单的功能用于将绘图元素(线,图像,文本等)添加到当前图形中的当前轴 。 [重点添加]
Hardcore ex-MATLAB users may choose to word this by saying something like, “plt.plot()
is a state-machine interface that implicitly tracks of the current figure!” In English, this means that:
铁杆前MATLAB用户可以选择这样说:“ plt.plot()
是一个状态机接口,可以隐式跟踪当前图形!” 用英语,这意味着:
plt.plot()
and other top-level pyplot functions. There is only ever one Figure or Axes that you’re manipulating at a given time, and you don’t need to explicitly refer to it.Axes
object, which is the object that represents a plot itself.plt.plot()
和其他顶级pyplot函数进行调用。 在给定的时间,您只需要操纵一个图形或轴,而无需显式引用它。 Axes
对象的方法来执行此操作,该对象是表示图本身的对象。 The flow of this process, at a high level, looks like this:
此过程的流程大致上如下所示:
Tying these together, most of the functions from pyplot also exist as methods of the matplotlib.axes.Axes
class.
将它们捆绑在一起,pyplot中的大多数功能也作为matplotlib.axes.Axes
类的方法存在。
This is easier to see by peaking under the hood; plt.plot()
can be boiled down to five or so lines of code:
通过在引擎盖下达到顶峰更容易看到它。 plt.plot()
可以简化为plt.plot()
五行代码:
# matplotlib/pyplot.py
# matplotlib/pyplot.py
>>> >>> def def plotplot (( ** argsargs , , **** kwargskwargs ):
):
... ... """An abridged version of plt.plot()."""
"""An abridged version of plt.plot()."""
... ... ax ax = = pltplt .. gcagca ()
()
... ... return return axax .. plotplot (( ** argsargs , , **** kwargskwargs )
)
>>> >>> def def gcagca (( **** kwargskwargs ):
):
... ... """Get the current Axes of the current Figure."""
"""Get the current Axes of the current Figure."""
... ... return return pltplt .. gcfgcf ()() .. gcagca (( **** kwargskwargs )
)
That is, calling plt.plot()
is just a convenient way to get the current Axes of the current Figure and then call its plot()
method. This is what is meant by the assertion that the stateful interface always “implicitly tracks” the plot that it wants to reference.
也就是说,调用plt.plot()
只是获取当前图形的当前轴,然后调用其plot()
方法的便捷方法。 这就是有状态接口始终“隐式跟踪”它要引用的绘图的主张。
pyplot is home to a batch of functions which are really just wrappers around matplotlib’s object-oriented interface. For example, with plt.title()
, there are corresponding setter and getter methods within the OO approach, ax.set_title()
and ax.get_title()
. (Use of getters and setters tends to be more popular in languages such as Java, but is a key feature of matplotlib’s OO approach.)
pyplot是一批函数的所在地, 这些函数实际上只是围绕matplotlib的面向对象接口的包装 。 例如,使用plt.title()
,OO方法中有相应的setter和getter方法ax.set_title()
和ax.get_title()
。 (在Java之类的语言中,使用getter和setter往往更为流行,但这是matplotlib的OO方法的主要功能。)
Calling plt.title()
gets translated into this one line: gca().set_title(s, *args, **kwargs)
. What is this doing?
调用plt.title()
会转换为这一行: gca().set_title(s, *args, **kwargs)
。 这是在做什么
gca()
grabs the current axis and returns it.set_title()
is a setter method, which sets the title for that Axes object. The “convenience” here is that we didn’t need to specify any Axes object explicitly with plt.title()
.gca()
获取当前轴并返回它。 set_title()
是一个setter方法,用于设置该Axes对象的标题。 这里的“便利”是我们不需要使用plt.title()
显式指定任何Axes对象。 Similarly, if you take a few moments to look at the source for top-level functions like plt.grid()
, plt.legend()
, and plt.ylabels()
, you’ll notice that all of them follow the same structure of delegating to the current Axes with gca()
, and then calling some method of the current Axes. (This is the underlying object-oriented approach!)
同样,如果花一些时间查看诸如plt.grid()
, plt.legend()
和plt.ylabels()
类的顶级函数的源代码,您会注意到它们全部遵循相同的结构使用gca()
委派给当前轴的方法,然后调用当前轴的某些方法。 (这是底层的面向对象方法!)
plt.subplots()
表示法 (Understanding plt.subplots()
Notation)Alright, enough theory. Now, we’re ready to tie everything together and do some plotting. From here on out, we’ll mostly rely on the stateless (object-oriented) approach, which is more customizable and comes in handy as graphs become more complex.
好了,足够的理论。 现在,我们准备将所有内容捆绑在一起并进行一些绘图。 从现在开始, 我们将主要依靠无状态(面向对象)方法 ,该方法更具可定制性,并且在图形变得更加复杂时会派上用场。
The prescribed way to create a Figure with a single Axes under the OO approach is (not too intuitively) with plt.subplots()
. (This is really the only time that the OO approach uses pyplot
, to create a Figure and Axes).
在OO方法下使用单个轴创建图形的规定方式是使用plt.subplots()
(不太直观plt.subplots()
。 (这确实是OO方法唯一一次使用pyplot
来创建图形和轴)。
Above, we took advantage of iterable unpacking to assign a separate variable to each of the two results of plt.subplots()
. Notice that we didn’t pass arguments to subplots()
here; the default call is subplots(nrows=1, ncols=1)
. Consequently, ax
is a single AxesSubplot
object:
上面,我们利用了可迭代的拆包功能,为plt.subplots()
的两个结果中的每一个分配了一个单独的变量。 注意,这里我们没有将参数传递给subplots()
。 默认调用是subplots(nrows=1, ncols=1)
。 因此, ax
是单个AxesSubplot
对象:
>>> >>> typetype (( axax )
)
and we can call its instance methods to manipulate the plot similarly to how we call pyplots functions. Let’s illustrate with a stacked area graph of three time series:
并且我们可以调用其实例方法来操作绘图,类似于调用pyplots函数的方式。 让我们用三个时间序列的堆积面积图进行说明:
What’s going on above?
上面是怎么回事?
fig
) containing one Axes (a plot, ax
).ax
directly to create a stacked area chart and to add a legend, title, and y-axis label. Under the object-oriented approach, it’s clear that all of these are attributes of ax
.tight_layout()
applies to the Figure object as a whole to clean up whitespace padding.ax
)的图( fig
)。 ax
方法来创建堆积的面积图,并添加图例,标题和y轴标签。 在面向对象的方法下,很明显所有这些都是ax
属性。 tight_layout()
整个应用于Figure对象,以清理空白填充。
Let’s look at an example with multiple subplots (Axes) within one Figure, plotting two correlated arrays that are drawn from the discrete uniform distribution:
让我们看一个在一个图中具有多个子图(Axes)的示例,绘制两个从离散均匀分布中绘制的相关数组:
>>> >>> x x = = npnp .. randomrandom .. randintrandint (( lowlow == 11 , , highhigh == 1111 , , sizesize == 5050 )
)
>>> >>> y y = = x x + + npnp .. randomrandom .. randintrandint (( 11 , , 55 , , sizesize == xx .. sizesize )
)
>>> >>> data data = = npnp .. column_stackcolumn_stack (((( xx , , yy ))
))
>>> >>> figfig , , (( ax1ax1 , , ax2ax2 ) ) = = pltplt .. subplotssubplots (( nrowsnrows == 11 , , ncolsncols == 22 ,
,
figsize=(8, 4))
figsize=(8, 4))
>>> >>> ax1ax1 .. scatterscatter (( xx == xx , , yy == yy , , markermarker == 'o''o' , , cc == 'r''r' , , edgecoloredgecolor == 'b''b' )
)
>>> >>> ax1ax1 .. set_titleset_title (( 'Scatter: $x$ versus $y$''Scatter: $x$ versus $y$' )
)
>>> >>> ax1ax1 .. set_xlabelset_xlabel (( '$x$''$x$' )
)
>>> >>> ax1ax1 .. set_ylabelset_ylabel (( '$y$''$y$' )
)
>>> >>> ax2ax2 .. histhist (( datadata , , binsbins == npnp .. arangearange (( datadata .. minmin (), (), datadata .. maxmax ()),
()),
label=('x', 'y'))
label=('x', 'y'))
>>> >>> ax2ax2 .. legendlegend (( locloc == (( 0.650.65 , , 0.80.8 ))
))
>>> >>> ax2ax2 .. set_titleset_title (( 'Frequencies of $x$ and $y$''Frequencies of $x$ and $y$' )
)
>>> >>> ax2ax2 .. yaxisyaxis .. tick_righttick_right ()
()
There’s a little bit more going on in this example:
在这个例子中还有更多的事情要做:
plt.subplots(1, 2)
is now a Figure object and a NumPy array of Axes objects. (You can inspect this with fig, axs = plt.subplots(1, 2)
, and taking a look at axs
.)ax1
and ax2
individually. (Something that’d be difficult to do with the stateful approach.) The final line is a good illustration of the object hierarchy, where we are modifying the yaxis
belonging to the second Axes, placing its ticks and ticklabels to the right.plt.subplots(1, 2)
的返回结果现在是Figure对象和Axes对象的NumPy数组。 (您可以使用fig, axs = plt.subplots(1, 2)
,并查看axs
。) ax1
和ax2
。 (东西会是很难做到与状态的方法。)最后一行是对象的层次结构,我们正在修改的一个很好的例证yaxis
属于第二轴,将其蜱ticklabels的权利。 Remember that multiple Axes can be enclosed in or “belong to” a given figure; in the case above, fig.axes
(lowercase, not uppercase Axes–there’s no denying the terminology is a bit confusing) gets us a list of all the Axes objects:
请记住,多个轴可以包含在给定的图形中或“属于”给定的图形; 在上面的例子中, fig.axes
(小写的Axes,而不是大写的Axes –不能否认术语有点混乱)为我们提供了所有Axes对象的列表:
Taking this one step further, we could alternatively create a figure that holds a 2×2 grid of Axes
objects:
更进一步,我们可以选择创建一个包含2×2 Axes
对象网格的图形:
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(7, 7))
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(7, 7))
Now, what is ax
? It’s no longer a single Axes
, but a two-dimensional NumPy array of them:
现在,什么是ax
? 它不再是单个Axes
,而是它们的二维NumPy数组:
This is reaffirmed by the docstring:
docstring重申了这一点:
ax
can be either a singlematplotlib.axes.Axes
object or an array ofAxes
objects if more than one subplot was created.如果创建了多个子图,则
ax
可以是单个matplotlib.axes.Axes
对象,也可以是Axes
对象数组。
We now need to call plotting methods on each of these Axes
(not the NumPy array, which is just a container in this case). A common way to address this is to use iterable unpacking after flattening the array to be one-dimensional:
我们现在需要调用这些标图方法Axes
(不是NumPy的阵列,这仅仅是在这种情况下,一个容器)。 解决此问题的常用方法是在将数组展平为一维后使用可迭代的拆包 :
>>> >>> figfig , , ax ax = = pltplt .. subplotssubplots (( nrowsnrows == 22 , , ncolsncols == 22 , , figsizefigsize == (( 77 , , 77 ))
))
>>> >>> ax1ax1 , , ax2ax2 , , ax3ax3 , , ax4 ax4 = = axax .. flattenflatten () () # flatten a 2d NumPy array to 1d
# flatten a 2d NumPy array to 1d
We could’ve also done this with ((ax1, ax2), (ax3, ax4)) = ax
, but the first approach tends to be more flexible.
我们也可以使用((ax1, ax2), (ax3, ax4)) = ax
,但是第一种方法往往更灵活。
To illustrate some more advanced subplot features, let’s pull some macroeconomic California housing data extracted from a compressed tar archive, using io
, tarfile
, and urllib
from Python’s Standard Library.
为了说明一些更高级的子图功能,让我们使用Python标准库中的io
, tarfile
和urllib
提取从压缩的tar存档中提取的一些加利福尼亚宏观经济数据。
The “response” variable y
below, to use the statistical term, is an area’s average home value. pop
and age
are the area’s population and average house age, respectively.
使用统计术语,下面的“响应”变量y
是区域的平均房屋价值。 pop
和age
分别是该地区的人口和平均房屋年龄。
>>> >>> y y = = housinghousing [:, [:, -- 11 ]
]
>>> >>> poppop , , age age = = housinghousing [:, [:, [[ 44 , , 77 ]]]] .. T
T
Next let’s define a “helper function” that places a text box inside of a plot and acts as an “in-plot title”:
接下来,让我们定义一个“帮助函数”,该函数将文本框放置在绘图中并充当“绘图中标题”:
We’re ready to do some plotting. Matplotlib’s gridspec
module allows for more subplot customization. pyplot’s subplot2grid()
interacts with this module nicely. Say we want to create a layout like this:
我们准备进行一些绘图。 Matplotlib的gridspec
模块允许更多子图定制。 pyplot的subplot2grid()
与此模块很好地交互。 假设我们要创建这样的布局:
Above, what we actually have is a 3×2 grid. ax1
is twice the height and width of ax2
/ax3
, meaning that it takes up two columns and two rows.
上面,我们实际拥有的是3×2网格。 ax1
是ax2
/ ax3
的高度和宽度的两倍,这意味着它占用了两列和两行。
The second argument to subplot2grid()
is the (row, column) location of the Axes within the grid:
subplot2grid()
的第二个参数是网格内轴的(行,列)位置:
>>> >>> gridsize gridsize = = (( 33 , , 22 )
)
>>> >>> fig fig = = pltplt .. figurefigure (( figsizefigsize == (( 1212 , , 88 ))
))
>>> >>> ax1 ax1 = = pltplt .. subplot2gridsubplot2grid (( gridsizegridsize , , (( 00 , , 00 ), ), colspancolspan == 22 , , rowspanrowspan == 22 )
)
>>> >>> ax2 ax2 = = pltplt .. subplot2gridsubplot2grid (( gridsizegridsize , , (( 22 , , 00 ))
))
>>> >>> ax3 ax3 = = pltplt .. subplot2gridsubplot2grid (( gridsizegridsize , , (( 22 , , 11 ))
))
Now, we can proceed as normal, modifying each Axes individually:
现在,我们可以正常进行,分别修改每个轴:
Above, colorbar()
(different from ColorMap earlier) gets called on the Figure directly, rather than the Axes. Its first argument is the result of ax1.scatter()
, which functions as a mapping of y-values to a ColorMap.
在上面,直接在图形上调用colorbar()
(与以前的ColorMap不同),而不是在轴上调用。 它的第一个参数是ax1.scatter()
的结果,该函数用作y值到ColorMap的映射。
Visually, there isn’t much differentiation in color (the y-variable) as we move up-and-down the y-axis, indicating that home age seems to be a stonger determinant of house value.
在视觉上,当我们在y轴上上下移动时,颜色(y变量)没有太大区别,这表明居家年龄似乎是房屋价值的重要决定因素。
Each time you call plt.subplots()
or the lesser-used plt.figure()
(which creates a Figure, with no Axes), you are creating a new Figure object that matplotlib sneakily keeps around in memory. Earlier, we alluded to the concept of a current Figure and current Axes. By default, these are the most-recently-created Figure and Axes, which we can show with the builtin function id()
to display the address of the object in memory:
每次调用plt.subplots()
或使用较少的plt.figure()
(创建一个没有轴的Figure)时,您都在创建一个新的Figure对象,matplotlib潜行地将其保留在内存中。 之前,我们提到了当前图形和当前轴的概念。 默认情况下,这些是最近创建的Figure和Axes,我们可以使用内置函数id()
来显示它们,以显示对象在内存中的地址:
>>> >>> fig1fig1 , , ax1 ax1 = = pltplt .. subplotssubplots ()
()
>>> >>> idid (( fig1fig1 )
)
4525567840
4525567840
>>> >>> idid (( pltplt .. gcfgcf ()) ()) # `fig1` is the current figure
# `fig1` is the current figure
4525567840
4525567840
>>> >>> fig2fig2 , , ax2 ax2 = = pltplt .. subplotssubplots ()
()
>>> >>> idid (( fig2fig2 ) ) == == idid (( pltplt .. gcfgcf ()) ()) # current figure has changed to `fig2`
# current figure has changed to `fig2`
True
True
(We could also use the builtin is
operator here.)
(我们也可以在此处使用内建的is
运算符。)
After the above routine, the current figure is fig2
, the most recently created figure. However, both figures are still hanging around in memory, each with a corresponding ID number (1-indexed, in MATLAB style):
执行上述例程后,当前图形为fig2
,即最近创建的图形。 但是,两个图形仍在内存中徘徊,每个图形都有一个对应的ID号(以MATLAB样式为1索引):
A useful way to get all of the Figures themselves is with a mapping of plt.figure()
to each of these integers:
获取所有plt.figure()
本身的一种有用方法是将plt.figure()
映射到以下每个整数:
>>> >>> def def get_all_figuresget_all_figures ():
():
... ... return return [[ pltplt .. figurefigure (( ii ) ) for for i i in in pltplt .. get_fignumsget_fignums ()]
()]
>>> >>> get_all_figuresget_all_figures ()
()
[,
[,
]
]
A takeaway: be cognizant of this if running a script where you’re creating a group of figures. You’ll want to explicitly close each of them after use to avoid a MemoryError
. plt.close()
by itself closes the current figure; plt.close(num)
closes the figure number num
, and plt.close('all')
closes all the figure windows.
要点:如果在要创建一组图形的地方运行脚本,请意识到这一点。 您需要在使用后显式关闭它们,以避免MemoryError
。 plt.close()
本身会关闭当前图形; plt.close(num)
关闭图形编号num
,而plt.close('all')
关闭所有图形窗口。
imshow()
和matshow()
(A Burst Of Color: imshow()
And matshow()
)While ax.plot()
is one of the most common plotting methods on an Axes, there are a whole host of others, as well. (We used ax.stackplot()
above; you can find the complete list here.)
虽然ax.plot()
是Axes上最常见的绘图方法之一,但也有很多其他方法。 (我们在上面使用了ax.stackplot()
;您可以在此处找到完整的列表。)
One group of methods that get heavy use are imshow()
and matshow()
, with the latter being a wrapper around the former. These are useful anytime that a raw numerical array an be visualized as a colored grid.
一组大量使用的方法是imshow()
和matshow()
,后者是前者的包装。 每当原始数值数组可视化为彩色网格时,这些命令就很有用。
First, let’s create two distinct grids with some fancy NumPy indexing:
首先,让我们用花哨的NumPy索引创建两个不同的网格:
>>> >>> x x = = npnp .. diagdiag (( npnp .. arangearange (( 22 , , 1212 ))[::))[:: -- 11 ]
]
>>> >>> xx [[ npnp .. diag_indices_fromdiag_indices_from (( xx [::[:: -- 11 ])] ])] = = npnp .. arangearange (( 22 , , 1212 )
)
>>> >>> x2 x2 = = npnp .. arangearange (( xx .. sizesize )) .. reshapereshape (( xx .. shapeshape )
)
Next, we can map these to their image representations. In this specific case, we toggle “off” all axis labels and ticks by using a dictionary comprehension and passing the result to ax.tick_params()
:
接下来,我们可以将它们映射到它们的图像表示。 在这种特定情况下,我们通过使用字典理解并将结果传递给ax.tick_params()
来“关闭”所有轴标签和刻度线:
Then, we can use a context manager to disable the grid, and call matshow()
on each Axes. Lastly, we need to put the colorbar in what is technically a new Axes within fig
. For this we can use a bit of an esoteric function from deep within matplotlib:
然后,我们可以使用上下文管理器禁用网格,并在每个matshow()
调用matshow()
。 最后,我们需要将色标放入fig
技术上称为新轴的位置 。 为此,我们可以在matplotlib中深入使用一些深奥的功能:
>>> >>> from from mpl_toolkits.axes_grid1.axes_divider mpl_toolkits.axes_grid1.axes_divider import import make_axes_locatable
make_axes_locatable
>>> >>> with with pltplt .. rc_contextrc_context (( rcrc == {{ 'axes.grid''axes.grid' : : FalseFalse }):
}):
... ... figfig , , (( ax1ax1 , , ax2ax2 ) ) = = pltplt .. subplotssubplots (( 11 , , 22 , , figsizefigsize == (( 88 , , 44 ))
))
... ... ax1ax1 .. matshowmatshow (( xx )
)
... ... img2 img2 = = ax2ax2 .. matshowmatshow (( x2x2 , , cmapcmap == 'RdYlGn_r''RdYlGn_r' )
)
... ... for for ax ax in in (( ax1ax1 , , ax2ax2 ):
):
... ... axax .. tick_paramstick_params (( axisaxis == 'both''both' , , whichwhich == 'both''both' , , **** nolabelsnolabels )
)
... ... for for ii , , j j in in zipzip (( ** xx .. nonzerononzero ()):
()):
... ... ax1ax1 .. texttext (( jj , , ii , , xx [[ ii , , jj ], ], colorcolor == 'white''white' , , haha == 'center''center' , , vava == 'center''center' )
)
...
...
... ... divider divider = = make_axes_locatablemake_axes_locatable (( ax2ax2 )
)
... ... cax cax = = dividerdivider .. append_axesappend_axes (( "right""right" , , sizesize == '5%''5%' , , padpad == 00 )
)
... ... pltplt .. colorbarcolorbar (( img2img2 , , caxcax == caxcax , , axax == [[ ax1ax1 , , ax2ax2 ])
])
... ... figfig .. suptitlesuptitle (( 'Heatmaps with `Axes.matshow`''Heatmaps with `Axes.matshow`' , , fontsizefontsize == 1616 )
)
The pandas library has become popular for not just for enabling powerful data analysis, but also for its handy pre-canned plotting methods. Interestingly though, pandas plotting methods are really just convenient wrappers around existing matplotlib calls.
熊猫库之所以流行,不仅是因为它能够进行强大的数据分析 ,而且还因为其方便的预罐装绘图方法。 但是有趣的是,熊猫绘图方法实际上只是现有matplotlib调用周围的便捷包装 。
That is, the plot()
method on pandas’ Series and DataFrame is a wrapper around plt.plot()
. One convenience provided, for example, is that if the DataFrame’s Index consists of dates, gcf().autofmt_xdate()
is called internally by pandas to get the current Figure and nicely auto-format the x-axis.
也就是说,熊猫的Series和DataFrame上的plot()
方法是对plt.plot()
的包装。 例如,提供的一种便利是,如果DataFrame的索引由日期组成,则pandas在内部调用gcf().autofmt_xdate()
来获取当前的Figure并很好地自动设置x轴的格式。
In turn, remember that plt.plot()
(the state-based approach) is implicitly aware of the current Figure and current Axes, so pandas is following the state-based approach by extension.
反过来,请记住, plt.plot()
(基于状态的方法)隐含地了解当前的Figure和当前的轴,因此pandas通过扩展遵循了基于状态的方法。
We can prove this “chain” of function calls with a bit of introspection. First, let’s construct a plain-vanilla pandas Series, assuming we’re starting out in a fresh interpreter session:
我们可以通过一些内省来证明函数调用的“链”。 首先,假设我们从一个新的口译会议开始,让我们构建一个普通香草大熊猫系列:
This internal architecture is helpful to know when you are mixing pandas plotting methods with traditional matplotlib calls, which is done below in plotting the moving average of a widely-watched financial time series. ma
is a pandas Series for which we can call ma.plot()
(the pandas method), and then customize by retrieving the Axes that is created by this call (plt.gca()
), for matplotlib to reference.
当您将熊猫的绘图方法与传统的matplotlib调用混合使用时,这种内部体系结构将有助于您了解,以下是在绘制广受关注的金融时间序列的移动平均值时进行的操作。 ma
是一个熊猫系列,可以调用ma.plot()
(pandas方法),然后通过检索此调用创建的轴( plt.gca()
)进行自定义,以供matplotlib引用。
>>> >>> import import pandas pandas as as pd
pd
>>> >>> import import matplotlib.transforms matplotlib.transforms as as mtransforms
mtransforms
>>> >>> url url = = 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=VIXCLS'
'https://fred.stlouisfed.org/graph/fredgraph.csv?id=VIXCLS'
>>> >>> vix vix = = pdpd .. read_csvread_csv (( urlurl , , index_colindex_col == 00 , , parse_datesparse_dates == TrueTrue , , na_valuesna_values == '.''.' ,
,
... ... infer_datetime_formatinfer_datetime_format == TrueTrue ,
,
... ... squeezesqueeze == TrueTrue )) .. dropnadropna ()
()
>>> >>> ma ma = = vixvix .. rollingrolling (( '90d''90d' )) .. meanmean ()
()
>>> >>> state state = = pdpd .. cutcut (( mama , , binsbins == [[ -- npnp .. infinf , , 1414 , , 1818 , , 2424 , , npnp .. infinf ],
],
... ... labelslabels == rangerange (( 44 ))
))
>>> >>> cmap cmap = = pltplt .. get_cmapget_cmap (( 'RdYlGn_r''RdYlGn_r' )
)
>>> >>> mama .. plotplot (( colorcolor == 'black''black' , , linewidthlinewidth == 1.51.5 , , markermarker == '''' , , figsizefigsize == (( 88 , , 44 ),
),
... ... labellabel == 'VIX 90d MA''VIX 90d MA' )
)
>>> >>> ax ax = = pltplt .. gcagca () () # get the current Axes that ma.plot() references
# get the current Axes that ma.plot() references
>>> >>> axax .. set_xlabelset_xlabel (( '''' )
)
>>> >>> axax .. set_ylabelset_ylabel (( '90d moving average: CBOE VIX''90d moving average: CBOE VIX' )
)
>>> >>> axax .. set_titleset_title (( 'Volatility Regime State''Volatility Regime State' )
)
>>> >>> axax .. gridgrid (( FalseFalse )
)
>>> >>> axax .. legendlegend (( locloc == 'upper center''upper center' )
)
>>> >>> axax .. set_xlimset_xlim (( xminxmin == mama .. indexindex [[ 00 ], ], xmaxxmax == mama .. indexindex [[ -- 11 ])
])
>>> >>> trans trans = = mtransformsmtransforms .. blended_transform_factoryblended_transform_factory (( axax .. transDatatransData , , axax .. transAxestransAxes )
)
>>> >>> for for ii , , color color in in enumerateenumerate (( cmapcmap ([([ 0.20.2 , , 0.40.4 , , 0.60.6 , , 0.80.8 ])):
])):
... ... axax .. fill_betweenfill_between (( mama .. indexindex , , 00 , , 11 , , wherewhere == statestate ==== ii ,
,
... ... facecolorfacecolor == colorcolor , , transformtransform == transtrans )
)
>>> >>> axax .. axhlineaxhline (( vixvix .. meanmean (), (), linestylelinestyle == 'dashed''dashed' , , colorcolor == 'xkcd:dark grey''xkcd:dark grey' ,
,
... ... alphaalpha == 0.60.6 , , labellabel == 'Full-period mean''Full-period mean' , , markermarker == '''' )
)
There’s a lot happening above:
上面发生了很多事情:
ma
is a 90-day moving average of the VIX Index, a measure of market expectations of near-term stock volatility. state
is a binning of the moving average into different regime states; a high VIX is seen as signaling a heightened level of fear in the marketplace.cmap
is a ColorMap–a matplotlib object that is essentially a mapping of floats to RGBA colors. Any colormap can be reversed by appending '_r'
, so 'RdYlGn_r'
is the reversed Red-Yellow-Green colormap. Matplotlib maintains a handy visual reference guide to colormaps in its docs.ma.plot()
. This calls plt.plot()
internally, so to integrate the object-oriented approach, we need to get an explicit reference to the current Axes with ax = plt.gca()
.state
. cmap([0.2, 0.4, 0.6, 0.8])
says, “get us an RGBA sequence for the colors at the 20th, 40th, 60th, and 80th ‘percentile’ along the colormaps’s spectrum.” enumerate()
is used because we want to map each RGBA color back to a state.ma
是VIX指数的90天移动平均线,该指数衡量市场对短期股票波动的预期。 state
是将移动平均值划分为不同的制度状态 ; 较高的VIX被认为预示着市场上恐惧的加剧。 cmap
是ColorMap –一个matplotlib对象,它本质上是浮点数到RGBA颜色的映射。 可以通过附加'_r'
来反转任何颜色图,因此'RdYlGn_r'
是反转的红-黄-绿颜色图。 Matplotlib在其文档中维护了一个方便的视觉参考指南,用于颜色图。 ma.plot()
。 这将在内部调用plt.plot()
,因此要集成面向对象的方法,我们需要使用ax = plt.gca()
来获得对当前轴的显式引用。 state
bin相对应的颜色填充的块。 cmap([0.2, 0.4, 0.6, 0.8])
说,“为我们提供了沿颜色图光谱在第20、40、60和80个百分位的颜色的RGBA序列。” enumerate()
是因为我们要将每种RGBA颜色映射回一个状态。 Pandas also comes built-out with a smattering of more advanced plots (which could take up an entire tutorial all on their own). However, all of these, like their simpler counterparts, rely on matplotlib machinery internally.
Pandas还内置了一些更高级的情节 (这些情节可能全部占用了整个教程)。 但是,所有这些方法,如其较简单的方法一样,在内部都依赖于matplotlib机械。
As shown by some of the examples above, there’s no getting around that matplotlib can be a technical, syntax-heavy library. Creating a production-ready chart sometimes requires a half hour of Googling and combining a hodgepodge of lines in order to fine-tune a plot.
如上面的一些示例所示,matplotlib可以成为一个技术性强,语法繁重的库,这无可避免。 创建生产就绪的图表有时需要半小时的谷歌搜索,然后将大杂烩组合在一起以微调图。
However, understanding how matplotlib’s interfaces interact is an investment that an pay off down the road. As Real Python’s own Dan Bader has advised, taking the time to dissect code rather than resorting to the Stack Overflow “copy pasta” solution tends to be a smarter long-term solution. Sticking to the object-oriented approach can save hours of frustration when you want to take a plot from plain to a work of art.
但是,了解matplotlib的接口如何交互是一项不费吹灰之力的投资。 正如Real Python自己的Dan Bader所建议的那样,花时间剖析代码而不是诉诸于Stack Overflow的“复制面食”解决方案往往是一个更明智的长期解决方案。 当您想将平面图从艺术品变成艺术品时,坚持使用面向对象的方法可以节省数小时的挫败感。
From the matplotlib documentation:
从matplotlib文档中:
Free Bonus: Click here to download 5 Python + Matplotlib examples with full source code that you can use as a basis for making your own plots and graphics.
免费奖金: 单击此处下载5个Python + Matplotlib示例,其中包含完整的源代码 ,您可以将其用作制作自己的图和图形的基础。
Third-party resources:
第三方资源:
Other plotting libraries:
其他绘图库:
If you’ve been following along with this tutorial, it’s likely that the plots popping up on your screen look different stylistically than the ones given here.
如果您一直按照本教程进行操作,则从屏幕上弹出的图形在样式上可能看起来与此处给出的图形不同。
Matplotlib offers two ways to configure style in a uniform way across different plots:
Matplotlib提供了两种在不同地块上以统一方式配置样式的方法:
A matplotlibrc file (Option #1 above) is basically a text file specifying user-customized settings that are remembered between Python sessions. On MacOSX, this normally resides at ~/.matplotlib/matplotlibrc.
matplotlibrc文件(上面的选项#1 )基本上是一个文本文件,指定在Python会话之间记住的用户自定义设置。 在MacOSX上,它通常位于〜/ .matplotlib / matplotlibrc 。
Quick Tip: GitHub is a great place to keep configuration files–I keep mine here. Just make sure that they don’t contain personally identifiable or private information, such as passwords or SSH private keys!)
快速提示: GitHub是保存配置文件的好地方-我在这里 。 只要确保它们不包含个人身份信息或私密信息,例如密码或SSH私钥!)
Alternatively, you can change your configuration parameters interactively (Option #2 above). When you import matplotlib.pyplot as plt
, you get access to an rcParams
object that resembles a Python dictionary of settings. All of the module objects starting with “rc” are means to interact with your plot styles and settings:
或者,您可以交互地更改配置参数(上述选项2 )。 import matplotlib.pyplot as plt
,可以访问类似于Python设置字典的rcParams
对象。 所有以“ rc”开头的模块对象都是与绘图样式和设置进行交互的手段:
Of these,
这些,
plt.rcdefaults()
restores the rc parameters from Matplotlib’s internal defaults, which are listed at plt.rcParamsDefault
. This will revert back (overwrite) whatever you’ve already customized in a matplotlibrc file.plt.rc()
is used for setting parameters interactively.plt.rcParams
is a (mutable) dictionary-like object that lets you manipulate settings directly. If you have customized settings in a matplotlibrc file, these will be reflected in this dictionary.plt.rcdefaults()
从Matplotlib的内部默认值中恢复rc参数,这些默认值列在plt.rcParamsDefault
。 这将还原(覆盖)您在matplotlibrc文件中已自定义的内容。 plt.rc()
用于交互设置参数。 plt.rcParams
是一个(可变的)类似于字典的对象,使您可以直接操作设置。 如果您在matplotlibrc文件中具有自定义设置,则这些设置将反映在此字典中。 With plt.rc()
and plt.rcParams
, these two syntaxes are equivalent for adjusting settings:
使用plt.rc()
和plt.rcParams
,这两种语法等效于调整设置:
>>> >>> pltplt .. rcrc (( 'lines''lines' , , linewidthlinewidth == 22 , , colorcolor == 'r''r' ) ) # Syntax 1
# Syntax 1
>>> >>> pltplt .. rcParamsrcParams [[ 'lines.linewidth''lines.linewidth' ] ] = = 2 2 # Syntax 2
# Syntax 2
>>> >>> pltplt .. rcParamsrcParams [[ 'lines.color''lines.color' ] ] = = 'r'
'r'
Notably, the Figure class then uses some of these these as its default arguments.
值得注意的是,Figure类随后将其中一些用作其默认参数。
Relatedly, a style is just a predefined cluster of custom settings. To view available styles, use:
相关地, 样式只是自定义设置的预定义群集。 要查看可用样式,请使用:
And to set a style, call:
并设置样式,请调用:
>>> >>> pltplt .. stylestyle .. useuse (( 'fivethirtyeight''fivethirtyeight' )
)
Your plots will now take on a new look (this full example is available here):
现在,您的绘图将焕然一新(可以在此处找到完整的示例):
For inspiration, Matplotlib keeps some style sheet displays for reference as well.
为了获得启发,Matplotlib还保留了一些样式表显示以供参考。
Behind the scenes, matplotlib also interacts with different backends. A backend is the workhorse between actually rendering a chart. (On the popular Anaconda distribution, for instance, the default backend is Qt5Agg.) Some backends are interactive, meaning they are dynamically updated and “pop up” to the user when changed.
在后台 ,matplotlib也与不同的后端进行交互。 后端是实际渲染图表之间的主力军。 (例如,在流行的Anaconda发行版中,默认后端是Qt5Agg。)某些后端是交互式的,这意味着它们是动态更新的,并在更改后“弹出”给用户。
While interactive mode is off by default, you can check its status with plt.rcParams['interactive']
or plt.isinteractive()
, and toggle it on and off with plt.ion()
and plt.ioff()
, respectively:
当交互模式默认情况下处于关闭状态时,您可以使用plt.rcParams['interactive']
或plt.isinteractive()
来检查其状态,并分别使用plt.ion()
和plt.ioff()
其打开和关闭:
>>> >>> pltplt .. ioffioff ()
()
>>> >>> pltplt .. rcParamsrcParams [[ 'interactive''interactive' ]
]
False
False
In some code examples, you may notice the presence of plt.show()
at the end of a chunk of code. The main purpose of plt.show()
, as the name implies, is to actually “show” (open) the figure when you’re running with interactive mode turned off. In other words:
在某些代码示例中,您可能会注意到在一段代码的末尾有plt.show()
的存在。 顾名思义, plt.show()
的主要目的是在交互式模式关闭的情况下实际“显示”(打开)该图。 换一种说法:
plt.show()
, and images will automatically pop-up and be updated as you reference them.plt.show()
to display a figure and plt.draw()
to update a plot.plt.show()
,并且图像将自动弹出并在您引用它们时进行更新。 plt.show()
显示图形,并需要plt.draw()
更新绘图。 Below, we make that interactive mode is off, which requires that we call plt.show()
after building the plot itself.
在下面,我们使交互模式plt.show()
关闭状态,这要求在构建图本身之后调用plt.show()
。
Notably, interactive mode has nothing to do with what IDE you’re using, or whether you’ve enable inline plotting with something like jupyter notebook --matplotlib inline
or %matplotlib
.
值得注意的是,交互模式与您使用的IDE无关,或者是否已使用jupyter notebook --matplotlib inline
或%matplotlib
类的jupyter notebook --matplotlib inline
启用了内联绘图。
[ Improve Your Python With Python Tricks – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
[通过Python技巧Improve改进Python –每两天将简短而可爱的Python技巧传递到您的收件箱。 >>单击此处了解更多信息,并查看示例 ]
翻译自: https://www.pybloggers.com/2018/02/python-plotting-with-matplotlib-guide/