8000 Updates from review · matplotlib/matplotlib@37d1197 · GitHub
[go: up one dir, main page]

Skip to content

Commit 37d1197

Browse files
committed
Updates from review
1 parent d61831c commit 37d1197

File tree

1 file changed

+73
-100
lines changed

1 file changed

+73
-100
lines changed

doc/users/explain/api_interfaces.rst

Lines changed: 73 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@
44
Matplotlib Application Interfaces (APIs)
55
========================================
66

7-
Python is a flexible language allowing different design choices when
8-
creating an application programming interface (API). Matplotlib
9-
has seen two of these over the years (or more, depending on how you
10-
count). In addition, external libraries have their own interfaces to
11-
Matplotlib. This means that code snippets across the internet are written
12-
using different interfaces, often leading to confusion. The three major
13-
interfaces are:
14-
15-
- an explicit interface that uses methods on a Figure or Axes object to
16-
create other Artists, and build a visualization step by step in an
17-
`imperative <https://en.wikipedia.org/wiki/Imperative_programming>`_
18-
manner.
19-
- an implicit interface that keeps track of the last Figure and Axes
20-
created, and adds Artists to the object it thinks the user wants. This
21-
interface is also imperative.
22-
- a number of downstream libraries offer a
23-
`declarative <https://en.wikipedia.org/wiki/Declarative_programming>`_
24-
interface, usually where a data object has a ``plot`` method implemented
25-
that will plot the data.
7+
Matplotlib has two major application interfaces, or styles of using the library:
8+
9+
- An explicit "Axes" interface that uses methods on a Figure or Axes object to
10+
create other Artists, and build a visualization step by step. This has also
11+
been called an "object-oriented" interface.
12+
- An implicit "pyplot" interface that keeps track of the last Figure and Axes
13+
created, and adds Artists to the object it thinks the user wants.
14+
15+
In addition, a number of downstream libraries (like `pandas` and `xarray`)
16+
offer a ``plot`` method implemented directly on their data classes so that
17+
users can call ``data.plot()``.
18+
19+
The difference between these interfaces can be a bit confusing, particularly
20+
given snippets on the web that use one or the other, or sometimes multiple
21+
interfaces in the same example. Here we attempt to point out how the "pyplot"
22+
and downstream interfaces relate to the explicit "Axes" interface to help users
23+
better navigate the library.
24+
25+
26+
Native Matplotlib interfaces
27+
----------------------------
2628

2729
The explicit "Axes" interface
28-
-----------------------------
30+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2931

3032
The "Axes" interface is how Matplotlib is implemented, and many customizations
31-
and fine-tuning end up needing to be done at this level. So even if you
32-
prefer the higher-level interfaces, it is very useful to understand that this
33-
interface exists, and how to access it.
33+
and fine-tuning end up being done at this level.
3434

35-
In general, using it is as easy as instantiating an instance of a
36-
`~.matplotlib.figure.Figure` class (``fig`` below), using an
37-
`~.Figure.add_axes` method (or similar) on that object to create an
38-
`~.matplotlib.axes.Axes` object (``ax`` below), and then calling drawing methods
39-
on the Axes (``plot`` in this example):
35+
This interface works by instantiating an instance of a
36+
`~.matplotlib.figure.Figure` class (``fig`` below), using a method
37+
`~.Figure.subplots` method (or similar) on that object to create one or more
38+
`~.matplotlib.axes.Axes` objects (``ax`` below), and then calling drawing
39+
methods on the Axes (``plot`` in this example):
4040

4141
.. plot::
4242
:include-source:
@@ -45,35 +45,21 @@ on the Axes (``plot`` in this example):
4545
import matplotlib.pyplot as plt
4646

4747
fig = plt.figure()
48-
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
49-
l = ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])
50-
51-
Note that ``plot`` itself also returns an object that can be subsequently
52-
manipulated.
48+
ax = fig.subplots()
49+
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])
5350

5451
We call this an "explicit" interface because each object is explicitly
55-
referenced, and used to make the next object. Keeping handles to the
56-
objects is very flexible, and allows us to customize the objects after
57-
they are created, but before they are displayed.
58-
59-
This is also an example of an "imperative" interface because the user tells
60-
Matplotlib what to do each step of the way. Of course Matplotlib has defaults
61-
for many things (line widths, fonts, colors, etc.), but this interface tells
62-
Matplotlib how to compose the visualization. Of course this paradigm is not
63-
pure, in that ``fig.add_axes()`` encapsulates creating many of the objects on
64-
an Axes (spines, labels, ticks) and ``ax.plot()`` encapsulates creating a
65-
line plot. But it is much more "imperative" than something like
66-
``data.plot()`` as we will see below.
52+
referenced, and used to make the next object. Keeping references to the objects
53+
is very flexible, and allows us to customize the objects after they are created,
54+
but before they are displayed.
6755

6856

6957
The implicit "pyplot" interface
70-
-------------------------------
58+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7159

72-
The "pyplot" interface arose from Matlab, where one would just call
73-
``plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])`` and a figure with axes would be
74-
created for you. The `~.matplotlib.pyplot` module shadows most of the
60+
The `~.matplotlib.pyplot` module shadows most of the
7561
`~.matplotlib.axes.Axes` plotting methods to give the equivalent of
76-
the above:
62+
the above, where the creation of the Figure and Axes is done for the user:
7763

7864
.. plot::
7965
:include-source:
@@ -84,20 +70,10 @@ the above:
8470
plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])
8571

8672
This can be convenient, particularly when doing interactive work or simple
87-
scripts - it saves at least a line of typing, and saves the user from having
88-
to think about figure and axes creation. However, for more complicated scripts
89-
it can be at more of a disadvantage - imagine you want to loop through a number
90-
of variables and plot them on separate Axes, it is often more straight-forward
91-
to create the Axes beforehand. It can also become ambiguous when you have
92-
multiple Figures or Axes which one is being acted on. In general the
93-
Matplotlib project recommends using the explicit interface, at least for code
94-
you want to share or preserve.
95-
96-
Whichever interface you choose, it is helpful to decode what is happening
97-
in the pyplot interface. Matplotib retains a list of Figures, and each Figure
98-
retains a list of Axes on the figure. The most recent figure can be retrieved
99-
with `~.pyplot.gcf` and the most recent Axes with `~.pyplot.gca`. So for a more
100-
complicated example:
73+
scripts. A reference to the current Figure can be retrieved using
74+
`~.pyplot.gcf` and to the current Axes by `~.pyplot.gca`. The `~.pyplot` module
75+
retains a list of Figures, and each Figure retains a list of Axes on the figure
76+
for the user so that the following are all equivalent:
10177

10278
.. plot::
10379
:include-source:
@@ -127,7 +103,7 @@ is equivalent to
127103
ax = plt.gca()
128104
ax.plot([3, 2, 1], [0, 0.5, 0.2])
129105

130-
which in the explict interface would be:
106+
In the explicit interface, this would be:
131107

132108
.. plot::
133109
:include-source:
@@ -139,22 +115,16 @@ which in the explict interface would be:
139115
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
140116
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])
141117

142-
The "pyplot" interface creates and uses all the same objects, that the "Axes"
143-
interface does, but it implicitly keeps track of the current Figure
144-
(``plt.gcf()``) and Axes (``plt.gca()``) for the user, and uses those Figures
145-
and Axes for any "pyplot" plotting methods until another Axes or Figure is
146-
selected.
147-
148118
Why be explicit?
149-
~~~~~~~~~~~~~~~~
119+
^^^^^^^^^^^^^^^^
150120

151-
What happens if you have to backtrack, and operate on an old axes? One that
152-
is not referenced by ``plt.gca()``. Of course you can do this, the simplest
153-
way is to call ``subplot`` again with the same arguments. However, that quickly
154-
becomes inelegant. You can also peer into the Figure object and get its list
155-
of Axes, however, that can be misleading (colorbars are axes too!). The best
156-
solution is probably to save a handle to every axes you create, but if you
157-
do that, why not simply create the axes at the start?
121+
What happens if you have to backtrack, and operate on an old axes that is not
122+
referenced by ``plt.gca()``? One simple way is to call ``subplot`` again with
123+
the same arguments. However, that quickly becomes inelegant. You can also
124+
inspect the Figure object and get its list of Axes objects, however, that can be
125+
misleading (colorbars are Axes too!). The best solution is probably to save a
126+
handle to every Axes you create, but if you do that, why not simply create the
127+
all the Axes objects at the start?
158128

159129
The first approach is to call ``plt.subplot`` again:
160130

@@ -199,7 +169,7 @@ The second is to save a handle:
199169
plt.sca(axs[i])
200170
plt.xlabel('Boo')
201171

202-
But the recommended way would be to just be explicit from the outset:
172+
However, the recommended way would be to be explicit from the outset:
203173

204174
.. plot::
205175
:include-source:
@@ -215,18 +185,14 @@ But the recommended way would be to just be explicit from the outset:
215185
axs[i].set_xlabel('Boo')
216186

217187

218-
The declarative "Data" interface
219-
--------------------------------
220-
221-
Matplotlib does not implement this interface natively. However, it is seen
222-
in `pandas`, `xarray`, and
223-
other third-party libraries. These interfaces are "declarative" in that
224-
the data object is told to plot itself, and then it uses Matplotlib behind
225-
the scenes to create the visualization.
188+
Third-party library "Data-object" interfaces
189+
--------------------------------------------
226190

227-
For illustrative purposes, a downstream library may implement a simple
228-
data container that has ``x`` and ``y`` data stored together, and then
229-
impliments a ``plot`` method as so:
191+
Some third party libraries have chosen to implement plotting for their data
192+
objects, e.g. `data.plot()`, is seen in `pandas`, `xarray`, and other
193+
third-party libraries. For illustrative purposes, a downstream library may
194+
implement a simple data container that has ``x`` and ``y`` data stored together,
195+
and then implements a ``plot`` method:
230196

231197
.. plot::
232198
:include-source:
@@ -258,16 +224,16 @@ impliments a ``plot`` method as so:
258224

259225
So the library can hide all the nitty-gritty from the user, and can make a
260226
visualization appropriate to the data type, often with good labels, choices of
261-
colormaps, and other nice features.
227+
colormaps, and other convenient features.
262228

263229
In the above, however, we may not have liked the title the library provided.
264-
Thankfully, they pass us back the axes object from the ``plot()`` method, and
265-
understanding the explicit Axes interface, we could manually manipulate it
266-
``ax.set_title('My preferred title')``.
230+
Thankfully, they pass us back the Axes from the ``plot()`` method, and
231+
understanding the explicit Axes interface, we could call:
232+
``ax.set_title('My preferred title')`` to customize the title.
267233

268-
Many libraries also allow ``plot`` to take an optional *ax* argument.
269-
This allows us to place the visualization in an Axes of our explicit
270-
choice.
234+
Many libraries also allow their ``plot`` methods to accept an optional *ax*
235+
argument. This allows us to place the visualization in an Axes that we have
236+
placed and perhaps customized.
271237

272238
Summary
273239
-------
@@ -289,9 +255,10 @@ the implicit "pyplot" interface.
289255
interface chosen.
290256

291257
Similarly, the declarative interfaces provided by partner libraries use the
292-
objects accessible by the Axes interface, and often accept these as arguments
258+
objects accessible by the "Axes" interface, and often accept these as arguments
293259
or pass them back from methods. It is usually essential to use the explicit
294-
"Axes" interface to perform any customization of the initial visualization.
260+
"Axes" interface to perform any customization of the default visualization, or
261+
to unpack the data into numpy arrays and pass directly to Matplotlib.
295262

296263
Appendix: "Axes" interface with data structures
297264
-----------------------------------------------
@@ -309,4 +276,10 @@ Most `~.axes.Axes` methods allow yet another API addressing by passing a
309276
fig, ax = plt.subplots()
310277
ax.plot(x='x1', y='y1', data=data)
311278

279+
Appendix: "pylab" interface
280+
---------------------------
312281

282+
There is one further interface that is highly discouraged, and that is to
283+
basically do `from matplotlib.pyplot import *`. This allows users to simply
284+
call `plot(x, y)`. While convenient, this can lead to obvious problems if the
285+
user unwittingly names a variable the same name as a pyplot method.

0 commit comments

Comments
 (0)
0