mirror of
https://github.com/Estom/notes.git
synced 2026-02-03 18:44:19 +08:00
matplotlib & pandas
This commit is contained in:
830
Python/matplotlab/intermediate/artists.md
Normal file
830
Python/matplotlab/intermediate/artists.md
Normal file
@@ -0,0 +1,830 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Artist tutorial
|
||||
|
||||
Using Artist objects to render on the canvas.
|
||||
|
||||
There are three layers to the matplotlib API.
|
||||
|
||||
- the ``matplotlib.backend_bases.FigureCanvas`` is the area onto which
|
||||
the figure is drawn
|
||||
- the ``matplotlib.backend_bases.Renderer`` is
|
||||
the object which knows how to draw on the
|
||||
``FigureCanvas``
|
||||
- and the [``matplotlib.artist.Artist``](https://matplotlib.orgapi/artist_api.html#matplotlib.artist.Artist) is the object that knows how to use
|
||||
a renderer to paint onto the canvas.
|
||||
|
||||
The ``FigureCanvas`` and
|
||||
``Renderer`` handle all the details of
|
||||
talking to user interface toolkits like [wxPython](https://www.wxpython.org) or drawing languages like PostScript®, and
|
||||
the ``Artist`` handles all the high level constructs like representing
|
||||
and laying out the figure, text, and lines. The typical user will
|
||||
spend 95% of their time working with the ``Artists``.
|
||||
|
||||
There are two types of ``Artists``: primitives and containers. The primitives
|
||||
represent the standard graphical objects we want to paint onto our canvas:
|
||||
[``Line2D``](https://matplotlib.orgapi/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D), [``Rectangle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle),
|
||||
[``Text``](https://matplotlib.orgapi/text_api.html#matplotlib.text.Text), [``AxesImage``](https://matplotlib.orgapi/image_api.html#matplotlib.image.AxesImage), etc., and
|
||||
the containers are places to put them ([``Axis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Axis),
|
||||
[``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) and [``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure)). The
|
||||
standard use is to create a [``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure) instance, use
|
||||
the ``Figure`` to create one or more [``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) or
|
||||
``Subplot`` instances, and use the ``Axes`` instance
|
||||
helper methods to create the primitives. In the example below, we create a
|
||||
``Figure`` instance using [``matplotlib.pyplot.figure()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.figure.html#matplotlib.pyplot.figure), which is a
|
||||
convenience method for instantiating ``Figure`` instances and connecting them
|
||||
with your user interface or drawing toolkit ``FigureCanvas``. As we will
|
||||
discuss below, this is not necessary -- you can work directly with PostScript,
|
||||
PDF Gtk+, or wxPython ``FigureCanvas`` instances, instantiate your ``Figures``
|
||||
directly and connect them yourselves -- but since we are focusing here on the
|
||||
``Artist`` API we'll let [``pyplot``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.html#module-matplotlib.pyplot) handle some of those details
|
||||
for us:
|
||||
|
||||
``` python
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(2, 1, 1) # two rows, one column, first plot
|
||||
```
|
||||
|
||||
The [``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) is probably the most important
|
||||
class in the matplotlib API, and the one you will be working with most
|
||||
of the time. This is because the ``Axes`` is the plotting area into
|
||||
which most of the objects go, and the ``Axes`` has many special helper
|
||||
methods ([``plot()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.plot.html#matplotlib.axes.Axes.plot),
|
||||
[``text()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.text.html#matplotlib.axes.Axes.text),
|
||||
[``hist()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.hist.html#matplotlib.axes.Axes.hist),
|
||||
[``imshow()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.imshow.html#matplotlib.axes.Axes.imshow)) to create the most common
|
||||
graphics primitives ([``Line2D``](https://matplotlib.orgapi/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D),
|
||||
[``Text``](https://matplotlib.orgapi/text_api.html#matplotlib.text.Text),
|
||||
[``Rectangle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle),
|
||||
``Image``, respectively). These helper methods
|
||||
will take your data (e.g., ``numpy`` arrays and strings) and create
|
||||
primitive ``Artist`` instances as needed (e.g., ``Line2D``), add them to
|
||||
the relevant containers, and draw them when requested. Most of you
|
||||
are probably familiar with the ``Subplot``,
|
||||
which is just a special case of an ``Axes`` that lives on a regular
|
||||
rows by columns grid of ``Subplot`` instances. If you want to create
|
||||
an ``Axes`` at an arbitrary location, simply use the
|
||||
[``add_axes()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_axes) method which takes a list
|
||||
of ``[left, bottom, width, height]`` values in 0-1 relative figure
|
||||
coordinates:
|
||||
|
||||
``` python
|
||||
fig2 = plt.figure()
|
||||
ax2 = fig2.add_axes([0.15, 0.1, 0.7, 0.3])
|
||||
```
|
||||
|
||||
Continuing with our example:
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
t = np.arange(0.0, 1.0, 0.01)
|
||||
s = np.sin(2*np.pi*t)
|
||||
line, = ax.plot(t, s, color='blue', lw=2)
|
||||
```
|
||||
|
||||
In this example, ``ax`` is the ``Axes`` instance created by the
|
||||
``fig.add_subplot`` call above (remember ``Subplot`` is just a
|
||||
subclass of ``Axes``) and when you call ``ax.plot``, it creates a
|
||||
``Line2D`` instance and adds it to the ``Axes.lines`` list. In the interactive [ipython](http://ipython.org/) session below, you can see that the
|
||||
``Axes.lines`` list is length one and contains the same line that was
|
||||
returned by the ``line, = ax.plot...`` call:
|
||||
|
||||
``` python
|
||||
In [101]: ax.lines[0]
|
||||
Out[101]: <matplotlib.lines.Line2D instance at 0x19a95710>
|
||||
|
||||
In [102]: line
|
||||
Out[102]: <matplotlib.lines.Line2D instance at 0x19a95710>
|
||||
```
|
||||
|
||||
If you make subsequent calls to ``ax.plot`` (and the hold state is "on"
|
||||
which is the default) then additional lines will be added to the list.
|
||||
You can remove lines later simply by calling the list methods; either
|
||||
of these will work:
|
||||
|
||||
``` python
|
||||
del ax.lines[0]
|
||||
ax.lines.remove(line) # one or the other, not both!
|
||||
```
|
||||
|
||||
The Axes also has helper methods to configure and decorate the x-axis
|
||||
and y-axis tick, tick labels and axis labels:
|
||||
|
||||
``` python
|
||||
xtext = ax.set_xlabel('my xdata') # returns a Text instance
|
||||
ytext = ax.set_ylabel('my ydata')
|
||||
```
|
||||
|
||||
When you call [``ax.set_xlabel``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.set_xlabel.html#matplotlib.axes.Axes.set_xlabel),
|
||||
it passes the information on the [``Text``](https://matplotlib.orgapi/text_api.html#matplotlib.text.Text)
|
||||
instance of the [``XAxis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.XAxis). Each ``Axes``
|
||||
instance contains an [``XAxis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.XAxis) and a
|
||||
[``YAxis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.YAxis) instance, which handle the layout and
|
||||
drawing of the ticks, tick labels and axis labels.
|
||||
|
||||
Try creating the figure below.
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig = plt.figure()
|
||||
fig.subplots_adjust(top=0.8)
|
||||
ax1 = fig.add_subplot(211)
|
||||
ax1.set_ylabel('volts')
|
||||
ax1.set_title('a sine wave')
|
||||
|
||||
t = np.arange(0.0, 1.0, 0.01)
|
||||
s = np.sin(2*np.pi*t)
|
||||
line, = ax1.plot(t, s, color='blue', lw=2)
|
||||
|
||||
# Fixing random state for reproducibility
|
||||
np.random.seed(19680801)
|
||||
|
||||
ax2 = fig.add_axes([0.15, 0.1, 0.7, 0.3])
|
||||
n, bins, patches = ax2.hist(np.random.randn(1000), 50,
|
||||
facecolor='yellow', edgecolor='yellow')
|
||||
ax2.set_xlabel('time (s)')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Customizing your objects
|
||||
|
||||
Every element in the figure is represented by a matplotlib
|
||||
[``Artist``](https://matplotlib.orgapi/artist_api.html#matplotlib.artist.Artist), and each has an extensive list of
|
||||
properties to configure its appearance. The figure itself contains a
|
||||
[``Rectangle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle) exactly the size of the figure,
|
||||
which you can use to set the background color and transparency of the
|
||||
figures. Likewise, each [``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) bounding box
|
||||
(the standard white box with black edges in the typical matplotlib
|
||||
plot, has a ``Rectangle`` instance that determines the color,
|
||||
transparency, and other properties of the Axes. These instances are
|
||||
stored as member variables ``Figure.patch`` and ``Axes.patch`` ("Patch" is a name inherited from
|
||||
MATLAB, and is a 2D "patch" of color on the figure, e.g., rectangles,
|
||||
circles and polygons). Every matplotlib ``Artist`` has the following
|
||||
properties
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Property
|
||||
Description
|
||||
|
||||
|
||||
|
||||
alpha
|
||||
The transparency - a scalar from 0-1
|
||||
|
||||
animated
|
||||
A boolean that is used to facilitate animated drawing
|
||||
|
||||
axes
|
||||
The axes that the Artist lives in, possibly None
|
||||
|
||||
clip_box
|
||||
The bounding box that clips the Artist
|
||||
|
||||
clip_on
|
||||
Whether clipping is enabled
|
||||
|
||||
clip_path
|
||||
The path the artist is clipped to
|
||||
|
||||
contains
|
||||
A picking function to test whether the artist contains the pick point
|
||||
|
||||
figure
|
||||
The figure instance the artist lives in, possibly None
|
||||
|
||||
label
|
||||
A text label (e.g., for auto-labeling)
|
||||
|
||||
picker
|
||||
A python object that controls object picking
|
||||
|
||||
transform
|
||||
The transformation
|
||||
|
||||
visible
|
||||
A boolean whether the artist should be drawn
|
||||
|
||||
zorder
|
||||
A number which determines the drawing order
|
||||
|
||||
rasterized
|
||||
Boolean; Turns vectors into raster graphics (for compression & eps transparency)
|
||||
|
||||
|
||||
|
||||
|
||||
Each of the properties is accessed with an old-fashioned setter or
|
||||
getter (yes we know this irritates Pythonistas and we plan to support
|
||||
direct access via properties or traits but it hasn't been done yet).
|
||||
For example, to multiply the current alpha by a half:
|
||||
|
||||
``` python
|
||||
a = o.get_alpha()
|
||||
o.set_alpha(0.5*a)
|
||||
```
|
||||
|
||||
If you want to set a number of properties at once, you can also use
|
||||
the ``set`` method with keyword arguments. For example:
|
||||
|
||||
``` python
|
||||
o.set(alpha=0.5, zorder=2)
|
||||
```
|
||||
|
||||
If you are working interactively at the python shell, a handy way to
|
||||
inspect the ``Artist`` properties is to use the
|
||||
[``matplotlib.artist.getp()``](https://matplotlib.orgapi/_as_gen/matplotlib.artist.getp.html#matplotlib.artist.getp) function (simply
|
||||
``getp()`` in pyplot), which lists the properties
|
||||
and their values. This works for classes derived from ``Artist`` as
|
||||
well, e.g., ``Figure`` and ``Rectangle``. Here are the ``Figure`` rectangle
|
||||
properties mentioned above:
|
||||
|
||||
``` python
|
||||
In [149]: matplotlib.artist.getp(fig.patch)
|
||||
alpha = 1.0
|
||||
animated = False
|
||||
antialiased or aa = True
|
||||
axes = None
|
||||
clip_box = None
|
||||
clip_on = False
|
||||
clip_path = None
|
||||
contains = None
|
||||
edgecolor or ec = w
|
||||
facecolor or fc = 0.75
|
||||
figure = Figure(8.125x6.125)
|
||||
fill = 1
|
||||
hatch = None
|
||||
height = 1
|
||||
label =
|
||||
linewidth or lw = 1.0
|
||||
picker = None
|
||||
transform = <Affine object at 0x134cca84>
|
||||
verts = ((0, 0), (0, 1), (1, 1), (1, 0))
|
||||
visible = True
|
||||
width = 1
|
||||
window_extent = <Bbox object at 0x134acbcc>
|
||||
x = 0
|
||||
y = 0
|
||||
zorder = 1
|
||||
```
|
||||
|
||||
The docstrings for all of the classes also contain the ``Artist``
|
||||
properties, so you can consult the interactive "help" or the
|
||||
[matplotlib.artist](https://matplotlib.orgapi/artist_api.html#artist-api) for a listing of properties for a given object.
|
||||
|
||||
## Object containers
|
||||
|
||||
Now that we know how to inspect and set the properties of a given
|
||||
object we want to configure, we need to know how to get at that object.
|
||||
As mentioned in the introduction, there are two kinds of objects:
|
||||
primitives and containers. The primitives are usually the things you
|
||||
want to configure (the font of a [``Text``](https://matplotlib.orgapi/text_api.html#matplotlib.text.Text)
|
||||
instance, the width of a [``Line2D``](https://matplotlib.orgapi/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D)) although
|
||||
the containers also have some properties as well -- for example the
|
||||
[``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) [``Artist``](https://matplotlib.orgapi/artist_api.html#matplotlib.artist.Artist) is a
|
||||
container that contains many of the primitives in your plot, but it
|
||||
also has properties like the ``xscale`` to control whether the xaxis
|
||||
is 'linear' or 'log'. In this section we'll review where the various
|
||||
container objects store the ``Artists`` that you want to get at.
|
||||
|
||||
### Figure container
|
||||
|
||||
The top level container ``Artist`` is the
|
||||
[``matplotlib.figure.Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure), and it contains everything in the
|
||||
figure. The background of the figure is a
|
||||
[``Rectangle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle) which is stored in
|
||||
``Figure.patch``. As
|
||||
you add subplots ([``add_subplot()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_subplot)) and
|
||||
axes ([``add_axes()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_axes)) to the figure
|
||||
these will be appended to the [``Figure.axes``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.axes). These are also returned by the
|
||||
methods that create them:
|
||||
|
||||
``` python
|
||||
In [156]: fig = plt.figure()
|
||||
|
||||
In [157]: ax1 = fig.add_subplot(211)
|
||||
|
||||
In [158]: ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3])
|
||||
|
||||
In [159]: ax1
|
||||
Out[159]: <matplotlib.axes.Subplot instance at 0xd54b26c>
|
||||
|
||||
In [160]: print(fig.axes)
|
||||
[<matplotlib.axes.Subplot instance at 0xd54b26c>, <matplotlib.axes.Axes instance at 0xd3f0b2c>]
|
||||
```
|
||||
|
||||
Because the figure maintains the concept of the "current axes" (see
|
||||
[``Figure.gca``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.gca) and
|
||||
[``Figure.sca``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.sca)) to support the
|
||||
pylab/pyplot state machine, you should not insert or remove axes
|
||||
directly from the axes list, but rather use the
|
||||
[``add_subplot()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_subplot) and
|
||||
[``add_axes()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_axes) methods to insert, and the
|
||||
[``delaxes()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.delaxes) method to delete. You are
|
||||
free however, to iterate over the list of axes or index into it to get
|
||||
access to ``Axes`` instances you want to customize. Here is an
|
||||
example which turns all the axes grids on:
|
||||
|
||||
``` python
|
||||
for ax in fig.axes:
|
||||
ax.grid(True)
|
||||
```
|
||||
|
||||
The figure also has its own text, lines, patches and images, which you
|
||||
can use to add primitives directly. The default coordinate system for
|
||||
the ``Figure`` will simply be in pixels (which is not usually what you
|
||||
want) but you can control this by setting the transform property of
|
||||
the ``Artist`` you are adding to the figure.
|
||||
|
||||
More useful is "figure coordinates" where (0, 0) is the bottom-left of
|
||||
the figure and (1, 1) is the top-right of the figure which you can
|
||||
obtain by setting the ``Artist`` transform to ``fig.transFigure``:
|
||||
|
||||
``` python
|
||||
import matplotlib.lines as lines
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
l1 = lines.Line2D([0, 1], [0, 1], transform=fig.transFigure, figure=fig)
|
||||
l2 = lines.Line2D([0, 1], [1, 0], transform=fig.transFigure, figure=fig)
|
||||
fig.lines.extend([l1, l2])
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
Here is a summary of the Artists the figure contains
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Figure attribute
|
||||
Description
|
||||
|
||||
|
||||
|
||||
axes
|
||||
A list of Axes instances (includes Subplot)
|
||||
|
||||
patch
|
||||
The Rectangle background
|
||||
|
||||
images
|
||||
A list of FigureImages patches - useful for raw pixel display
|
||||
|
||||
legends
|
||||
A list of Figure Legend instances (different from Axes.legends)
|
||||
|
||||
lines
|
||||
A list of Figure Line2D instances (rarely used, see Axes.lines)
|
||||
|
||||
patches
|
||||
A list of Figure patches (rarely used, see Axes.patches)
|
||||
|
||||
texts
|
||||
A list Figure Text instances
|
||||
|
||||
|
||||
|
||||
|
||||
### Axes container
|
||||
|
||||
The [``matplotlib.axes.Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) is the center of the matplotlib
|
||||
universe -- it contains the vast majority of all the ``Artists`` used
|
||||
in a figure with many helper methods to create and add these
|
||||
``Artists`` to itself, as well as helper methods to access and
|
||||
customize the ``Artists`` it contains. Like the
|
||||
[``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure), it contains a
|
||||
[``Patch``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Patch.html#matplotlib.patches.Patch)
|
||||
``patch`` which is a
|
||||
[``Rectangle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle) for Cartesian coordinates and a
|
||||
[``Circle``](https://matplotlib.orgapi/_as_gen/matplotlib.patches.Circle.html#matplotlib.patches.Circle) for polar coordinates; this patch
|
||||
determines the shape, background and border of the plotting region:
|
||||
|
||||
``` python
|
||||
ax = fig.add_subplot(111)
|
||||
rect = ax.patch # a Rectangle instance
|
||||
rect.set_facecolor('green')
|
||||
```
|
||||
|
||||
When you call a plotting method, e.g., the canonical
|
||||
[``plot()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.plot.html#matplotlib.axes.Axes.plot) and pass in arrays or lists of
|
||||
values, the method will create a [``matplotlib.lines.Line2D()``](https://matplotlib.orgapi/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D)
|
||||
instance, update the line with all the ``Line2D`` properties passed as
|
||||
keyword arguments, add the line to the ``Axes.lines`` container, and returns it to you:
|
||||
|
||||
``` python
|
||||
In [213]: x, y = np.random.rand(2, 100)
|
||||
|
||||
In [214]: line, = ax.plot(x, y, '-', color='blue', linewidth=2)
|
||||
```
|
||||
|
||||
``plot`` returns a list of lines because you can pass in multiple x, y
|
||||
pairs to plot, and we are unpacking the first element of the length
|
||||
one list into the line variable. The line has been added to the
|
||||
``Axes.lines`` list:
|
||||
|
||||
``` python
|
||||
In [229]: print(ax.lines)
|
||||
[<matplotlib.lines.Line2D instance at 0xd378b0c>]
|
||||
```
|
||||
|
||||
Similarly, methods that create patches, like
|
||||
[``bar()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.bar.html#matplotlib.axes.Axes.bar) creates a list of rectangles, will
|
||||
add the patches to the ``Axes.patches`` list:
|
||||
|
||||
``` python
|
||||
In [233]: n, bins, rectangles = ax.hist(np.random.randn(1000), 50, facecolor='yellow')
|
||||
|
||||
In [234]: rectangles
|
||||
Out[234]: <a list of 50 Patch objects>
|
||||
|
||||
In [235]: print(len(ax.patches))
|
||||
```
|
||||
|
||||
You should not add objects directly to the ``Axes.lines`` or
|
||||
``Axes.patches`` lists unless you know exactly what you are doing,
|
||||
because the ``Axes`` needs to do a few things when it creates and adds
|
||||
an object. It sets the figure and axes property of the ``Artist``, as
|
||||
well as the default ``Axes`` transformation (unless a transformation
|
||||
is set). It also inspects the data contained in the ``Artist`` to
|
||||
update the data structures controlling auto-scaling, so that the view
|
||||
limits can be adjusted to contain the plotted data. You can,
|
||||
nonetheless, create objects yourself and add them directly to the
|
||||
``Axes`` using helper methods like
|
||||
[``add_line()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.add_line.html#matplotlib.axes.Axes.add_line) and
|
||||
[``add_patch()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.add_patch.html#matplotlib.axes.Axes.add_patch). Here is an annotated
|
||||
interactive session illustrating what is going on:
|
||||
|
||||
``` python
|
||||
In [262]: fig, ax = plt.subplots()
|
||||
|
||||
# create a rectangle instance
|
||||
In [263]: rect = matplotlib.patches.Rectangle( (1,1), width=5, height=12)
|
||||
|
||||
# by default the axes instance is None
|
||||
In [264]: print(rect.get_axes())
|
||||
None
|
||||
|
||||
# and the transformation instance is set to the "identity transform"
|
||||
In [265]: print(rect.get_transform())
|
||||
<Affine object at 0x13695544>
|
||||
|
||||
# now we add the Rectangle to the Axes
|
||||
In [266]: ax.add_patch(rect)
|
||||
|
||||
# and notice that the ax.add_patch method has set the axes
|
||||
# instance
|
||||
In [267]: print(rect.get_axes())
|
||||
Axes(0.125,0.1;0.775x0.8)
|
||||
|
||||
# and the transformation has been set too
|
||||
In [268]: print(rect.get_transform())
|
||||
<Affine object at 0x15009ca4>
|
||||
|
||||
# the default axes transformation is ax.transData
|
||||
In [269]: print(ax.transData)
|
||||
<Affine object at 0x15009ca4>
|
||||
|
||||
# notice that the xlimits of the Axes have not been changed
|
||||
In [270]: print(ax.get_xlim())
|
||||
(0.0, 1.0)
|
||||
|
||||
# but the data limits have been updated to encompass the rectangle
|
||||
In [271]: print(ax.dataLim.bounds)
|
||||
(1.0, 1.0, 5.0, 12.0)
|
||||
|
||||
# we can manually invoke the auto-scaling machinery
|
||||
In [272]: ax.autoscale_view()
|
||||
|
||||
# and now the xlim are updated to encompass the rectangle
|
||||
In [273]: print(ax.get_xlim())
|
||||
(1.0, 6.0)
|
||||
|
||||
# we have to manually force a figure draw
|
||||
In [274]: ax.figure.canvas.draw()
|
||||
```
|
||||
|
||||
There are many, many ``Axes`` helper methods for creating primitive
|
||||
``Artists`` and adding them to their respective containers. The table
|
||||
below summarizes a small sampling of them, the kinds of ``Artist`` they
|
||||
create, and where they store them
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Helper method
|
||||
Artist
|
||||
Container
|
||||
|
||||
|
||||
|
||||
ax.annotate - text annotations
|
||||
Annotate
|
||||
ax.texts
|
||||
|
||||
ax.bar - bar charts
|
||||
Rectangle
|
||||
ax.patches
|
||||
|
||||
ax.errorbar - error bar plots
|
||||
Line2D and Rectangle
|
||||
ax.lines and ax.patches
|
||||
|
||||
ax.fill - shared area
|
||||
Polygon
|
||||
ax.patches
|
||||
|
||||
ax.hist - histograms
|
||||
Rectangle
|
||||
ax.patches
|
||||
|
||||
ax.imshow - image data
|
||||
AxesImage
|
||||
ax.images
|
||||
|
||||
ax.legend - axes legends
|
||||
Legend
|
||||
ax.legends
|
||||
|
||||
ax.plot - xy plots
|
||||
Line2D
|
||||
ax.lines
|
||||
|
||||
ax.scatter - scatter charts
|
||||
PolygonCollection
|
||||
ax.collections
|
||||
|
||||
ax.text - text
|
||||
Text
|
||||
ax.texts
|
||||
|
||||
|
||||
|
||||
|
||||
In addition to all of these ``Artists``, the ``Axes`` contains two
|
||||
important ``Artist`` containers: the [``XAxis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.XAxis)
|
||||
and [``YAxis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.YAxis), which handle the drawing of the
|
||||
ticks and labels. These are stored as instance variables
|
||||
``xaxis`` and
|
||||
``yaxis``. The ``XAxis`` and ``YAxis``
|
||||
containers will be detailed below, but note that the ``Axes`` contains
|
||||
many helper methods which forward calls on to the
|
||||
[``Axis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Axis) instances so you often do not need to
|
||||
work with them directly unless you want to. For example, you can set
|
||||
the font color of the ``XAxis`` ticklabels using the ``Axes`` helper
|
||||
method:
|
||||
|
||||
``` python
|
||||
for label in ax.get_xticklabels():
|
||||
label.set_color('orange')
|
||||
```
|
||||
|
||||
Below is a summary of the Artists that the Axes contains
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Axes attribute
|
||||
Description
|
||||
|
||||
|
||||
|
||||
artists
|
||||
A list of Artist instances
|
||||
|
||||
patch
|
||||
Rectangle instance for Axes background
|
||||
|
||||
collections
|
||||
A list of Collection instances
|
||||
|
||||
images
|
||||
A list of AxesImage
|
||||
|
||||
legends
|
||||
A list of Legend instances
|
||||
|
||||
lines
|
||||
A list of Line2D instances
|
||||
|
||||
patches
|
||||
A list of Patch instances
|
||||
|
||||
texts
|
||||
A list of Text instances
|
||||
|
||||
xaxis
|
||||
matplotlib.axis.XAxis instance
|
||||
|
||||
yaxis
|
||||
matplotlib.axis.YAxis instance
|
||||
|
||||
|
||||
|
||||
|
||||
### Axis containers
|
||||
|
||||
The [``matplotlib.axis.Axis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Axis) instances handle the drawing of the
|
||||
tick lines, the grid lines, the tick labels and the axis label. You
|
||||
can configure the left and right ticks separately for the y-axis, and
|
||||
the upper and lower ticks separately for the x-axis. The ``Axis``
|
||||
also stores the data and view intervals used in auto-scaling, panning
|
||||
and zooming, as well as the [``Locator``](https://matplotlib.orgapi/ticker_api.html#matplotlib.ticker.Locator) and
|
||||
[``Formatter``](https://matplotlib.orgapi/ticker_api.html#matplotlib.ticker.Formatter) instances which control where
|
||||
the ticks are placed and how they are represented as strings.
|
||||
|
||||
Each ``Axis`` object contains a ``label`` attribute
|
||||
(this is what [``pyplot``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.html#module-matplotlib.pyplot) modifies in calls to
|
||||
[``xlabel()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.xlabel.html#matplotlib.pyplot.xlabel) and [``ylabel()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.ylabel.html#matplotlib.pyplot.ylabel)) as
|
||||
well as a list of major and minor ticks. The ticks are
|
||||
[``XTick``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.XTick) and [``YTick``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.YTick) instances,
|
||||
which contain the actual line and text primitives that render the ticks and
|
||||
ticklabels. Because the ticks are dynamically created as needed (e.g., when
|
||||
panning and zooming), you should access the lists of major and minor ticks
|
||||
through their accessor methods [``get_major_ticks()``](https://matplotlib.orgapi/_as_gen/matplotlib.axis.Axis.get_major_ticks.html#matplotlib.axis.Axis.get_major_ticks)
|
||||
and [``get_minor_ticks()``](https://matplotlib.orgapi/_as_gen/matplotlib.axis.Axis.get_minor_ticks.html#matplotlib.axis.Axis.get_minor_ticks). Although the ticks contain
|
||||
all the primitives and will be covered below, ``Axis`` instances have accessor
|
||||
methods that return the tick lines, tick labels, tick locations etc.:
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots()
|
||||
axis = ax.xaxis
|
||||
axis.get_ticklocs()
|
||||
```
|
||||
|
||||

|
||||
|
||||
``` python
|
||||
axis.get_ticklabels()
|
||||
```
|
||||
|
||||
note there are twice as many ticklines as labels because by
|
||||
|
||||
``` python
|
||||
axis.get_ticklines()
|
||||
```
|
||||
|
||||
by default you get the major ticks back
|
||||
|
||||
``` python
|
||||
axis.get_ticklines()
|
||||
```
|
||||
|
||||
but you can also ask for the minor ticks
|
||||
|
||||
``` python
|
||||
axis.get_ticklines(minor=True)
|
||||
|
||||
# Here is a summary of some of the useful accessor methods of the ``Axis``
|
||||
# (these have corresponding setters where useful, such as
|
||||
# set_major_formatter)
|
||||
#
|
||||
# ====================== =========================================================
|
||||
# Accessor method Description
|
||||
# ====================== =========================================================
|
||||
# get_scale The scale of the axis, e.g., 'log' or 'linear'
|
||||
# get_view_interval The interval instance of the axis view limits
|
||||
# get_data_interval The interval instance of the axis data limits
|
||||
# get_gridlines A list of grid lines for the Axis
|
||||
# get_label The axis label - a Text instance
|
||||
# get_ticklabels A list of Text instances - keyword minor=True|False
|
||||
# get_ticklines A list of Line2D instances - keyword minor=True|False
|
||||
# get_ticklocs A list of Tick locations - keyword minor=True|False
|
||||
# get_major_locator The matplotlib.ticker.Locator instance for major ticks
|
||||
# get_major_formatter The matplotlib.ticker.Formatter instance for major ticks
|
||||
# get_minor_locator The matplotlib.ticker.Locator instance for minor ticks
|
||||
# get_minor_formatter The matplotlib.ticker.Formatter instance for minor ticks
|
||||
# get_major_ticks A list of Tick instances for major ticks
|
||||
# get_minor_ticks A list of Tick instances for minor ticks
|
||||
# grid Turn the grid on or off for the major or minor ticks
|
||||
# ====================== =========================================================
|
||||
#
|
||||
# Here is an example, not recommended for its beauty, which customizes
|
||||
# the axes and tick properties
|
||||
|
||||
# plt.figure creates a matplotlib.figure.Figure instance
|
||||
fig = plt.figure()
|
||||
rect = fig.patch # a rectangle instance
|
||||
rect.set_facecolor('lightgoldenrodyellow')
|
||||
|
||||
ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4])
|
||||
rect = ax1.patch
|
||||
rect.set_facecolor('lightslategray')
|
||||
|
||||
|
||||
for label in ax1.xaxis.get_ticklabels():
|
||||
# label is a Text instance
|
||||
label.set_color('red')
|
||||
label.set_rotation(45)
|
||||
label.set_fontsize(16)
|
||||
|
||||
for line in ax1.yaxis.get_ticklines():
|
||||
# line is a Line2D instance
|
||||
line.set_color('green')
|
||||
line.set_markersize(25)
|
||||
line.set_markeredgewidth(3)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Tick containers
|
||||
|
||||
The [``matplotlib.axis.Tick``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Tick) is the final container object in our
|
||||
descent from the [``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure) to the
|
||||
[``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) to the [``Axis``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Axis)
|
||||
to the [``Tick``](https://matplotlib.orgapi/axis_api.html#matplotlib.axis.Tick). The ``Tick`` contains the tick
|
||||
and grid line instances, as well as the label instances for the upper
|
||||
and lower ticks. Each of these is accessible directly as an attribute
|
||||
of the ``Tick``.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Tick attribute
|
||||
Description
|
||||
|
||||
|
||||
|
||||
tick1line
|
||||
Line2D instance
|
||||
|
||||
tick2line
|
||||
Line2D instance
|
||||
|
||||
gridline
|
||||
Line2D instance
|
||||
|
||||
label1
|
||||
Text instance
|
||||
|
||||
label2
|
||||
Text instance
|
||||
|
||||
|
||||
|
||||
|
||||
Here is an example which sets the formatter for the right side ticks with
|
||||
dollar signs and colors them green on the right side of the yaxis
|
||||
|
||||
``` python
|
||||
import matplotlib.ticker as ticker
|
||||
|
||||
# Fixing random state for reproducibility
|
||||
np.random.seed(19680801)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(100*np.random.rand(20))
|
||||
|
||||
formatter = ticker.FormatStrFormatter('$%1.2f')
|
||||
ax.yaxis.set_major_formatter(formatter)
|
||||
|
||||
for tick in ax.yaxis.get_major_ticks():
|
||||
tick.label1.set_visible(False)
|
||||
tick.label2.set_visible(True)
|
||||
tick.label2.set_color('green')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: artists.py](https://matplotlib.org/_downloads/a7b58a13e5ee2b59b31d49c2baa9f139/artists.py)
|
||||
- [Download Jupyter notebook: artists.ipynb](https://matplotlib.org/_downloads/c328d03dcd3b9dae9a8b3f008c82073b/artists.ipynb)
|
||||
|
||||
148
Python/matplotlab/intermediate/color_cycle.md
Normal file
148
Python/matplotlab/intermediate/color_cycle.md
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Styling with cycler
|
||||
|
||||
Demo of custom property-cycle settings to control colors and other style
|
||||
properties for multi-line plots.
|
||||
|
||||
::: tip Note
|
||||
|
||||
More complete documentation of the ``cycler`` API can be found
|
||||
[here](http://matplotlib.org/cycler/).
|
||||
|
||||
:::
|
||||
|
||||
This example demonstrates two different APIs:
|
||||
|
||||
1. Setting the default rc parameter specifying the property cycle.
|
||||
This affects all subsequent axes (but not axes already created).
|
||||
1. Setting the property cycle for a single pair of axes.
|
||||
|
||||
``` python
|
||||
from cycler import cycler
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
```
|
||||
|
||||
First we'll generate some sample data, in this case, four offset sine
|
||||
curves.
|
||||
|
||||
``` python
|
||||
x = np.linspace(0, 2 * np.pi, 50)
|
||||
offsets = np.linspace(0, 2 * np.pi, 4, endpoint=False)
|
||||
yy = np.transpose([np.sin(x + phi) for phi in offsets])
|
||||
```
|
||||
|
||||
Now ``yy`` has shape
|
||||
|
||||
``` python
|
||||
print(yy.shape)
|
||||
```
|
||||
|
||||
Out:
|
||||
|
||||
```
|
||||
(50, 4)
|
||||
```
|
||||
|
||||
So ``yy[:, i]`` will give you the ``i``-th offset sine curve. Let's set the
|
||||
default ``prop_cycle`` using [``matplotlib.pyplot.rc()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.rc.html#matplotlib.pyplot.rc). We'll combine a
|
||||
color cycler and a linestyle cycler by adding (``+``) two ``cycler``'s
|
||||
together. See the bottom of this tutorial for more information about
|
||||
combining different cyclers.
|
||||
|
||||
``` python
|
||||
default_cycler = (cycler(color=['r', 'g', 'b', 'y']) +
|
||||
cycler(linestyle=['-', '--', ':', '-.']))
|
||||
|
||||
plt.rc('lines', linewidth=4)
|
||||
plt.rc('axes', prop_cycle=default_cycler)
|
||||
```
|
||||
|
||||
Now we'll generate a figure with two axes, one on top of the other. On the
|
||||
first axis, we'll plot with the default cycler. On the second axis, we'll
|
||||
set the ``prop_cycle`` using [``matplotlib.axes.Axes.set_prop_cycle()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.set_prop_cycle.html#matplotlib.axes.Axes.set_prop_cycle),
|
||||
which will only set the ``prop_cycle`` for this [``matplotlib.axes.Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes)
|
||||
instance. We'll use a second ``cycler`` that combines a color cycler and a
|
||||
linewidth cycler.
|
||||
|
||||
``` python
|
||||
custom_cycler = (cycler(color=['c', 'm', 'y', 'k']) +
|
||||
cycler(lw=[1, 2, 3, 4]))
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(nrows=2)
|
||||
ax0.plot(yy)
|
||||
ax0.set_title('Set default color cycle to rgby')
|
||||
ax1.set_prop_cycle(custom_cycler)
|
||||
ax1.plot(yy)
|
||||
ax1.set_title('Set axes color cycle to cmyk')
|
||||
|
||||
# Add a bit more space between the two plots.
|
||||
fig.subplots_adjust(hspace=0.3)
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Setting ``prop_cycle`` in the ``matplotlibrc`` file or style files
|
||||
|
||||
Remember, if you want to set a custom cycler in your
|
||||
``.matplotlibrc`` file or a style file (``style.mplstyle``), you can set the
|
||||
``axes.prop_cycle`` property:
|
||||
|
||||
``` python
|
||||
axes.prop_cycle : cycler(color='bgrcmyk')
|
||||
```
|
||||
|
||||
## Cycling through multiple properties
|
||||
|
||||
You can add cyclers:
|
||||
|
||||
``` python
|
||||
from cycler import cycler
|
||||
cc = (cycler(color=list('rgb')) +
|
||||
cycler(linestyle=['-', '--', '-.']))
|
||||
for d in cc:
|
||||
print(d)
|
||||
```
|
||||
|
||||
Results in:
|
||||
|
||||
``` python
|
||||
{'color': 'r', 'linestyle': '-'}
|
||||
{'color': 'g', 'linestyle': '--'}
|
||||
{'color': 'b', 'linestyle': '-.'}
|
||||
```
|
||||
|
||||
You can multiply cyclers:
|
||||
|
||||
``` python
|
||||
from cycler import cycler
|
||||
cc = (cycler(color=list('rgb')) *
|
||||
cycler(linestyle=['-', '--', '-.']))
|
||||
for d in cc:
|
||||
print(d)
|
||||
```
|
||||
|
||||
Results in:
|
||||
|
||||
``` python
|
||||
{'color': 'r', 'linestyle': '-'}
|
||||
{'color': 'r', 'linestyle': '--'}
|
||||
{'color': 'r', 'linestyle': '-.'}
|
||||
{'color': 'g', 'linestyle': '-'}
|
||||
{'color': 'g', 'linestyle': '--'}
|
||||
{'color': 'g', 'linestyle': '-.'}
|
||||
{'color': 'b', 'linestyle': '-'}
|
||||
{'color': 'b', 'linestyle': '--'}
|
||||
{'color': 'b', 'linestyle': '-.'}
|
||||
```
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: color_cycle.py](https://matplotlib.org/_downloads/6d214f31d57999a93c8a6e18f0ce6aab/color_cycle.py)
|
||||
- [Download Jupyter notebook: color_cycle.ipynb](https://matplotlib.org/_downloads/e2174f7bdc06ad628a756f14967811ee/color_cycle.ipynb)
|
||||
|
||||
900
Python/matplotlab/intermediate/constrainedlayout_guide.md
Normal file
900
Python/matplotlab/intermediate/constrainedlayout_guide.md
Normal file
@@ -0,0 +1,900 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Constrained Layout Guide
|
||||
|
||||
How to use constrained-layout to fit plots within your figure cleanly.
|
||||
|
||||
*constrained_layout* automatically adjusts subplots and decorations like
|
||||
legends and colorbars so that they fit in the figure window while still
|
||||
preserving, as best they can, the logical layout requested by the user.
|
||||
|
||||
*constrained_layout* is similar to
|
||||
[tight_layout](tight_layout_guide.html),
|
||||
but uses a constraint solver to determine the size of axes that allows
|
||||
them to fit.
|
||||
|
||||
*constrained_layout* needs to be activated before any axes are added to
|
||||
a figure. Two ways of doing so are
|
||||
|
||||
- using the respective argument to [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) or
|
||||
[``figure()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.figure.html#matplotlib.pyplot.figure), e.g.:
|
||||
|
||||
``` python
|
||||
plt.subplots(constrained_layout=True)
|
||||
```
|
||||
- activate it via [rcParams](https://matplotlib.org/introductory/customizing.html#matplotlib-rcparams), like:
|
||||
|
||||
``` python
|
||||
plt.rcParams['figure.constrained_layout.use'] = True
|
||||
```
|
||||
|
||||
Those are described in detail throughout the following sections.
|
||||
|
||||
::: danger Warning
|
||||
|
||||
Currently Constrained Layout is **experimental**. The
|
||||
behaviour and API are subject to change, or the whole functionality
|
||||
may be removed without a deprecation period. If you *require* your
|
||||
plots to be absolutely reproducible, get the Axes positions after
|
||||
running Constrained Layout and use ``ax.set_position()`` in your code
|
||||
with ``constrained_layout=False``.
|
||||
|
||||
:::
|
||||
|
||||
## Simple Example
|
||||
|
||||
In Matplotlib, the location of axes (including subplots) are specified in
|
||||
normalized figure coordinates. It can happen that your axis labels or
|
||||
titles (or sometimes even ticklabels) go outside the figure area, and are thus
|
||||
clipped.
|
||||
|
||||
``` python
|
||||
# sphinx_gallery_thumbnail_number = 18
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.colors as mcolors
|
||||
import matplotlib.gridspec as gridspec
|
||||
import numpy as np
|
||||
|
||||
|
||||
plt.rcParams['savefig.facecolor'] = "0.8"
|
||||
plt.rcParams['figure.figsize'] = 4.5, 4.
|
||||
|
||||
|
||||
def example_plot(ax, fontsize=12, nodec=False):
|
||||
ax.plot([1, 2])
|
||||
|
||||
ax.locator_params(nbins=3)
|
||||
if not nodec:
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Title', fontsize=fontsize)
|
||||
else:
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
|
||||
|
||||
fig, ax = plt.subplots(constrained_layout=False)
|
||||
example_plot(ax, fontsize=24)
|
||||
```
|
||||
|
||||

|
||||
|
||||
To prevent this, the location of axes needs to be adjusted. For
|
||||
subplots, this can be done by adjusting the subplot params
|
||||
([Move the edge of an axes to make room for tick labels](https://matplotlib.orgfaq/howto_faq.html#howto-subplots-adjust)). However, specifying your figure with the
|
||||
``constrained_layout=True`` kwarg will do the adjusting automatically.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
example_plot(ax, fontsize=24)
|
||||
```
|
||||
|
||||

|
||||
|
||||
When you have multiple subplots, often you see labels of different
|
||||
axes overlapping each other.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=False)
|
||||
for ax in axs.flat:
|
||||
example_plot(ax)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Specifying ``constrained_layout=True`` in the call to ``plt.subplots``
|
||||
causes the layout to be properly constrained.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
example_plot(ax)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Colorbars
|
||||
|
||||
If you create a colorbar with the [``colorbar()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.colorbar.html#matplotlib.pyplot.colorbar)
|
||||
command you need to make room for it. ``constrained_layout`` does this
|
||||
automatically. Note that if you specify ``use_gridspec=True`` it will be
|
||||
ignored because this option is made for improving the layout via
|
||||
``tight_layout``.
|
||||
|
||||
::: tip Note
|
||||
|
||||
For the [``pcolormesh``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.pcolormesh.html#matplotlib.axes.Axes.pcolormesh) kwargs (``pc_kwargs``) we use a
|
||||
dictionary. Below we will assign one colorbar to a number of axes each
|
||||
containing a [``ScalarMappable``](https://matplotlib.orgapi/cm_api.html#matplotlib.cm.ScalarMappable); specifying the norm and colormap
|
||||
ensures the colorbar is accurate for all the axes.
|
||||
|
||||
:::
|
||||
|
||||
``` python
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
norm = mcolors.Normalize(vmin=0., vmax=100.)
|
||||
# see note above: this makes all pcolormesh calls consistent:
|
||||
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
|
||||
fig, ax = plt.subplots(figsize=(4, 4), constrained_layout=True)
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=ax, shrink=0.6)
|
||||
```
|
||||
|
||||

|
||||
|
||||
If you specify a list of axes (or other iterable container) to the
|
||||
``ax`` argument of ``colorbar``, constrained_layout will take space from
|
||||
the specified axes.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs, shrink=0.6)
|
||||
```
|
||||
|
||||

|
||||
|
||||
If you specify a list of axes from inside a grid of axes, the colorbar
|
||||
will steal space appropriately, and leave a gap, but all subplots will
|
||||
still be the same size.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(3, 3, figsize=(4, 4), constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs[1:, ][:, 1], shrink=0.8)
|
||||
fig.colorbar(im, ax=axs[:, -1], shrink=0.6)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Note that there is a bit of a subtlety when specifying a single axes
|
||||
as the parent. In the following, it might be desirable and expected
|
||||
for the colorbars to line up, but they don't because the colorbar paired
|
||||
with the bottom axes is tied to the subplotspec of the axes, and hence
|
||||
shrinks when the gridspec-level colorbar is added.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(3, 1, figsize=(4, 4), constrained_layout=True)
|
||||
for ax in axs[:2]:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs[:2], shrink=0.6)
|
||||
im = axs[2].pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs[2], shrink=0.6)
|
||||
```
|
||||
|
||||

|
||||
|
||||
The API to make a single-axes behave like a list of axes is to specify
|
||||
it as a list (or other iterable container), as below:
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(3, 1, figsize=(4, 4), constrained_layout=True)
|
||||
for ax in axs[:2]:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs[:2], shrink=0.6)
|
||||
im = axs[2].pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=[axs[2]], shrink=0.6)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Suptitle
|
||||
|
||||
``constrained_layout`` can also make room for [``suptitle``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.suptitle).
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs, shrink=0.6)
|
||||
fig.suptitle('Big Suptitle')
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Legends
|
||||
|
||||
Legends can be placed outside of their parent axis.
|
||||
Constrained-layout is designed to handle this for [``Axes.legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.legend.html#matplotlib.axes.Axes.legend).
|
||||
However, constrained-layout does *not* handle legends being created via
|
||||
[``Figure.legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.legend) (yet).
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
ax.plot(np.arange(10), label='This is a plot')
|
||||
ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
|
||||
```
|
||||
|
||||

|
||||
|
||||
However, this will steal space from a subplot layout:
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
|
||||
axs[0].plot(np.arange(10))
|
||||
axs[1].plot(np.arange(10), label='This is a plot')
|
||||
axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
|
||||
```
|
||||
|
||||

|
||||
|
||||
In order for a legend or other artist to *not* steal space
|
||||
from the subplot layout, we can ``leg.set_in_layout(False)``.
|
||||
Of course this can mean the legend ends up
|
||||
cropped, but can be useful if the plot is subsequently called
|
||||
with ``fig.savefig('outname.png', bbox_inches='tight')``. Note,
|
||||
however, that the legend's ``get_in_layout`` status will have to be
|
||||
toggled again to make the saved file work, and we must manually
|
||||
trigger a draw if we want constrained_layout to adjust the size
|
||||
of the axes before printing.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
|
||||
|
||||
axs[0].plot(np.arange(10))
|
||||
axs[1].plot(np.arange(10), label='This is a plot')
|
||||
leg = axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
|
||||
leg.set_in_layout(False)
|
||||
# trigger a draw so that constrained_layout is executed once
|
||||
# before we turn it off when printing....
|
||||
fig.canvas.draw()
|
||||
# we want the legend included in the bbox_inches='tight' calcs.
|
||||
leg.set_in_layout(True)
|
||||
# we don't want the layout to change at this point.
|
||||
fig.set_constrained_layout(False)
|
||||
fig.savefig('CL01.png', bbox_inches='tight', dpi=100)
|
||||
```
|
||||
|
||||

|
||||
|
||||
The saved file looks like:
|
||||
|
||||

|
||||
|
||||
A better way to get around this awkwardness is to simply
|
||||
use the legend method provided by [``Figure.legend``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.legend):
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
|
||||
axs[0].plot(np.arange(10))
|
||||
lines = axs[1].plot(np.arange(10), label='This is a plot')
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left',
|
||||
bbox_to_anchor=(0.8, 0.5), bbox_transform=axs[1].transAxes)
|
||||
fig.savefig('CL02.png', bbox_inches='tight', dpi=100)
|
||||
```
|
||||
|
||||

|
||||
|
||||
The saved file looks like:
|
||||
|
||||

|
||||
|
||||
## Padding and Spacing
|
||||
|
||||
For constrained_layout, we have implemented a padding around the edge of
|
||||
each axes. This padding sets the distance from the edge of the plot,
|
||||
and the minimum distance between adjacent plots. It is specified in
|
||||
inches by the keyword arguments ``w_pad`` and ``h_pad`` to the function
|
||||
[``set_constrained_layout_pads``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.set_constrained_layout_pads):
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
example_plot(ax, nodec=True)
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
fig.set_constrained_layout_pads(w_pad=4./72., h_pad=4./72.,
|
||||
hspace=0., wspace=0.)
|
||||
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
example_plot(ax, nodec=True)
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
|
||||
hspace=0., wspace=0.)
|
||||
```
|
||||
|
||||
- 
|
||||
- 
|
||||
|
||||
Spacing between subplots is set by ``wspace`` and ``hspace``. There are
|
||||
specified as a fraction of the size of the subplot group as a whole.
|
||||
If the size of the figure is changed, then these spaces change in
|
||||
proportion. Note in the blow how the space at the edges doesn't change from
|
||||
the above, but the space between subplots does.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
example_plot(ax, nodec=True)
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
|
||||
hspace=0.2, wspace=0.2)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Spacing with colorbars
|
||||
|
||||
Colorbars will be placed ``wspace`` and ``hsapce`` apart from other
|
||||
subplots. The padding between the colorbar and the axis it is
|
||||
attached to will never be less than ``w_pad`` (for a vertical colorbar)
|
||||
or ``h_pad`` (for a horizontal colorbar). Note the use of the ``pad`` kwarg
|
||||
here in the ``colorbar`` call. It defaults to 0.02 of the size
|
||||
of the axis it is attached to.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
pc = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(pc, ax=ax, shrink=0.6, pad=0)
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
|
||||
hspace=0.2, wspace=0.2)
|
||||
```
|
||||
|
||||

|
||||
|
||||
In the above example, the colorbar will not ever be closer than 2 pts to
|
||||
the plot, but if we want it a bit further away, we can specify its value
|
||||
for ``pad`` to be non-zero.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
pc = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=ax, shrink=0.6, pad=0.05)
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
|
||||
hspace=0.2, wspace=0.2)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## rcParams
|
||||
|
||||
There are five [rcParams](https://matplotlib.org/introductory/customizing.html#matplotlib-rcparams) that can be set,
|
||||
either in a script or in the ``matplotlibrc`` file.
|
||||
They all have the prefix ``figure.constrained_layout``:
|
||||
|
||||
- ``use``: Whether to use constrained_layout. Default is False
|
||||
- ``w_pad``, ``h_pad``: Padding around axes objects.
|
||||
Float representing inches. Default is 3./72. inches (3 pts)
|
||||
- ``wspace``, ``hspace``: Space between subplot groups.
|
||||
Float representing a fraction of the subplot widths being separated.
|
||||
Default is 0.02.
|
||||
|
||||
``` python
|
||||
plt.rcParams['figure.constrained_layout.use'] = True
|
||||
fig, axs = plt.subplots(2, 2, figsize=(3, 3))
|
||||
for ax in axs.flat:
|
||||
example_plot(ax)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Use with GridSpec
|
||||
|
||||
constrained_layout is meant to be used
|
||||
with [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.subplots) or
|
||||
[``GridSpec()``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec) and
|
||||
[``add_subplot()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_subplot).
|
||||
|
||||
Note that in what follows ``constrained_layout=True``
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1, figure=fig)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
```
|
||||
|
||||

|
||||
|
||||
More complicated gridspec layouts are possible. Note here we use the
|
||||
convenience functions ``add_gridspec`` and ``subgridspec``.
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
gs0 = fig.add_gridspec(1, 2)
|
||||
|
||||
gs1 = gs0[0].subgridspec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs2 = gs0[1].subgridspec(3, 1)
|
||||
|
||||
for ss in gs2:
|
||||
ax = fig.add_subplot(ss)
|
||||
example_plot(ax)
|
||||
ax.set_title("")
|
||||
ax.set_xlabel("")
|
||||
|
||||
ax.set_xlabel("x-label", fontsize=12)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Note that in the above the left and columns don't have the same vertical
|
||||
extent. If we want the top and bottom of the two grids to line up then
|
||||
they need to be in the same gridspec:
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
gs0 = fig.add_gridspec(6, 2)
|
||||
|
||||
ax1 = fig.add_subplot(gs0[:3, 0])
|
||||
ax2 = fig.add_subplot(gs0[3:, 0])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
ax = fig.add_subplot(gs0[0:2, 1])
|
||||
example_plot(ax)
|
||||
ax = fig.add_subplot(gs0[2:4, 1])
|
||||
example_plot(ax)
|
||||
ax = fig.add_subplot(gs0[4:, 1])
|
||||
example_plot(ax)
|
||||
```
|
||||
|
||||

|
||||
|
||||
This example uses two gridspecs to have the colorbar only pertain to
|
||||
one set of pcolors. Note how the left column is wider than the
|
||||
two right-hand columns because of this. Of course, if you wanted the
|
||||
subplots to be the same size you only needed one gridspec.
|
||||
|
||||
``` python
|
||||
def docomplicated(suptitle=None):
|
||||
fig = plt.figure()
|
||||
gs0 = fig.add_gridspec(1, 2, figure=fig, width_ratios=[1., 2.])
|
||||
gsl = gs0[0].subgridspec(2, 1)
|
||||
gsr = gs0[1].subgridspec(2, 2)
|
||||
|
||||
for gs in gsl:
|
||||
ax = fig.add_subplot(gs)
|
||||
example_plot(ax)
|
||||
axs = []
|
||||
for gs in gsr:
|
||||
ax = fig.add_subplot(gs)
|
||||
pcm = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax.set_xlabel('x-label')
|
||||
ax.set_ylabel('y-label')
|
||||
ax.set_title('title')
|
||||
|
||||
axs += [ax]
|
||||
fig.colorbar(pcm, ax=axs)
|
||||
if suptitle is not None:
|
||||
fig.suptitle(suptitle)
|
||||
|
||||
docomplicated()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Manually setting axes positions
|
||||
|
||||
There can be good reasons to manually set an axes position. A manual call
|
||||
to [``set_position``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.set_position.html#matplotlib.axes.Axes.set_position) will set the axes so constrained_layout has
|
||||
no effect on it anymore. (Note that constrained_layout still leaves the
|
||||
space for the axes that is moved).
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(1, 2)
|
||||
example_plot(axs[0], fontsize=12)
|
||||
axs[1].set_position([0.2, 0.2, 0.4, 0.4])
|
||||
```
|
||||
|
||||

|
||||
|
||||
If you want an inset axes in data-space, you need to manually execute the
|
||||
layout using ``fig.execute_constrained_layout()`` call. The inset figure
|
||||
will then be properly positioned. However, it will not be properly
|
||||
positioned if the size of the figure is subsequently changed. Similarly,
|
||||
if the figure is printed to another backend, there may be slight changes
|
||||
of location due to small differences in how the backends render fonts.
|
||||
|
||||
``` python
|
||||
from matplotlib.transforms import Bbox
|
||||
|
||||
fig, axs = plt.subplots(1, 2)
|
||||
example_plot(axs[0], fontsize=12)
|
||||
fig.execute_constrained_layout()
|
||||
# put into data-space:
|
||||
bb_data_ax2 = Bbox.from_bounds(0.5, 1., 0.2, 0.4)
|
||||
disp_coords = axs[0].transData.transform(bb_data_ax2)
|
||||
fig_coords_ax2 = fig.transFigure.inverted().transform(disp_coords)
|
||||
bb_ax2 = Bbox(fig_coords_ax2)
|
||||
ax2 = fig.add_axes(bb_ax2)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Manually turning off ``constrained_layout``
|
||||
|
||||
``constrained_layout`` usually adjusts the axes positions on each draw
|
||||
of the figure. If you want to get the spacing provided by
|
||||
``constrained_layout`` but not have it update, then do the initial
|
||||
draw and then call ``fig.set_constrained_layout(False)``.
|
||||
This is potentially useful for animations where the tick labels may
|
||||
change length.
|
||||
|
||||
Note that ``constrained_layout`` is turned off for ``ZOOM`` and ``PAN``
|
||||
GUI events for the backends that use the toolbar. This prevents the
|
||||
axes from changing position during zooming and panning.
|
||||
|
||||
## Limitations
|
||||
|
||||
### Incompatible functions
|
||||
|
||||
``constrained_layout`` will not work on subplots
|
||||
created via the ``subplot`` command. The reason is that each of these
|
||||
commands creates a separate ``GridSpec`` instance and ``constrained_layout``
|
||||
uses (nested) gridspecs to carry out the layout. So the following fails
|
||||
to yield a nice layout:
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot(221)
|
||||
ax2 = plt.subplot(223)
|
||||
ax3 = plt.subplot(122)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Of course that layout is possible using a gridspec:
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
gs = fig.add_gridspec(2, 2)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax2 = fig.add_subplot(gs[1, 0])
|
||||
ax3 = fig.add_subplot(gs[:, 1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Similarly,
|
||||
[``subplot2grid()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid) doesn't work for the same reason:
|
||||
each call creates a different parent gridspec.
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot2grid((3, 3), (0, 0))
|
||||
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
|
||||
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
|
||||
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
```
|
||||
|
||||

|
||||
|
||||
The way to make this plot compatible with ``constrained_layout`` is again
|
||||
to use ``gridspec`` directly
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
gs = fig.add_gridspec(3, 3)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax2 = fig.add_subplot(gs[0, 1:])
|
||||
ax3 = fig.add_subplot(gs[1:, 0:2])
|
||||
ax4 = fig.add_subplot(gs[1:, -1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Other Caveats
|
||||
|
||||
- ``constrained_layout`` only considers ticklabels, axis labels, titles, and
|
||||
legends. Thus, other artists may be clipped and also may overlap.
|
||||
- It assumes that the extra space needed for ticklabels, axis labels,
|
||||
and titles is independent of original location of axes. This is
|
||||
often true, but there are rare cases where it is not.
|
||||
- There are small differences in how the backends handle rendering fonts,
|
||||
so the results will not be pixel-identical.
|
||||
|
||||
## Debugging
|
||||
|
||||
Constrained-layout can fail in somewhat unexpected ways. Because it uses
|
||||
a constraint solver the solver can find solutions that are mathematically
|
||||
correct, but that aren't at all what the user wants. The usual failure
|
||||
mode is for all sizes to collapse to their smallest allowable value. If
|
||||
this happens, it is for one of two reasons:
|
||||
|
||||
1. There was not enough room for the elements you were requesting to draw.
|
||||
1. There is a bug - in which case open an issue at
|
||||
[https://github.com/matplotlib/matplotlib/issues](https://github.com/matplotlib/matplotlib/issues).
|
||||
|
||||
If there is a bug, please report with a self-contained example that does
|
||||
not require outside data or dependencies (other than numpy).
|
||||
|
||||
## Notes on the algorithm
|
||||
|
||||
The algorithm for the constraint is relatively straightforward, but
|
||||
has some complexity due to the complex ways we can layout a figure.
|
||||
|
||||
### Figure layout
|
||||
|
||||
Figures are laid out in a hierarchy:
|
||||
|
||||
1. Figure: ``fig = plt.figure()``
|
||||
|
||||
Each item has a layoutbox associated with it. The nesting of gridspecs
|
||||
created with [``GridSpecFromSubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpecFromSubplotSpec.html#matplotlib.gridspec.GridSpecFromSubplotSpec) can be arbitrarily deep.
|
||||
|
||||
Each [``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) has *two* layoutboxes. The first one,
|
||||
``ax._layoutbox`` represents the outside of the Axes and all its
|
||||
decorations (i.e. ticklabels,axis labels, etc.).
|
||||
The second layoutbox corresponds to the Axes' ``ax.position``, which sets
|
||||
where in the figure the spines are placed.
|
||||
|
||||
Why so many stacked containers? Ideally, all that would be needed are the
|
||||
Axes layout boxes. For the Gridspec case, a container is
|
||||
needed if the Gridspec is nested via [``GridSpecFromSubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpecFromSubplotSpec.html#matplotlib.gridspec.GridSpecFromSubplotSpec). At the
|
||||
top level, it is desirable for symmetry, but it also makes room for
|
||||
[``suptitle``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.suptitle).
|
||||
|
||||
For the Subplotspec/Axes case, Axes often have colorbars or other
|
||||
annotations that need to be packaged inside the Subplotspec, hence the
|
||||
need for the outer layer.
|
||||
|
||||
### Simple case: one Axes
|
||||
|
||||
For a single Axes the layout is straight forward. The Figure and
|
||||
outer Gridspec layoutboxes coincide. The Subplotspec and Axes
|
||||
boxes also coincide because the Axes has no colorbar. Note
|
||||
the difference between the red ``pos`` box and the green ``ax`` box
|
||||
is set by the size of the decorations around the Axes.
|
||||
|
||||
In the code, this is accomplished by the entries in
|
||||
``do_constrained_layout()`` like:
|
||||
|
||||
``` python
|
||||
ax._poslayoutbox.edit_left_margin_min(-bbox.x0 + pos.x0 + w_padt)
|
||||
```
|
||||
|
||||
``` python
|
||||
from matplotlib._layoutbox import plot_children
|
||||
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
example_plot(ax, fontsize=24)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Simple case: two Axes
|
||||
|
||||
For this case, the Axes layoutboxes and the Subplotspec boxes still
|
||||
co-incide. However, because the decorations in the right-hand plot are so
|
||||
much smaller than the left-hand, so the right-hand layoutboxes are smaller.
|
||||
|
||||
The Subplotspec boxes are laid out in the code in the subroutine
|
||||
``arange_subplotspecs()``, which simply checks the subplotspecs in the code
|
||||
against one another and stacks them appropriately.
|
||||
|
||||
The two ``pos`` axes are lined up. Because they have the same
|
||||
minimum row, they are lined up at the top. Because
|
||||
they have the same maximum row they are lined up at the bottom. In the
|
||||
code this is accomplished via the calls to ``layoutbox.align``. If
|
||||
there was more than one row, then the same horizontal alignment would
|
||||
occur between the rows.
|
||||
|
||||
The two ``pos`` axes are given the same width because the subplotspecs
|
||||
occupy the same number of columns. This is accomplished in the code where
|
||||
``dcols0`` is compared to ``dcolsC``. If they are equal, then their widths
|
||||
are constrained to be equal.
|
||||
|
||||
While it is a bit subtle in this case, note that the division between the
|
||||
Subplotspecs is *not* centered, but has been moved to the right to make
|
||||
space for the larger labels on the left-hand plot.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(1, 2, constrained_layout=True)
|
||||
example_plot(ax[0], fontsize=32)
|
||||
example_plot(ax[1], fontsize=8)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Two Axes and colorbar
|
||||
|
||||
Adding a colorbar makes it clear why the Subplotspec layoutboxes must
|
||||
be different from the axes layoutboxes. Here we see the left-hand
|
||||
subplotspec has more room to accommodate the [``colorbar``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.colorbar), and
|
||||
that there are two green ``ax`` boxes inside the ``ss`` box.
|
||||
|
||||
Note that the width of the ``pos`` boxes is still the same because of the
|
||||
constraint on their widths because their subplotspecs occupy the same
|
||||
number of columns (one in this example).
|
||||
|
||||
The colorbar layout logic is contained in [``make_axes``](https://matplotlib.orgapi/colorbar_api.html#matplotlib.colorbar.make_axes)
|
||||
which calls ``_constrained_layout.layoutcolorbarsingle()``
|
||||
for cbars attached to a single axes, and
|
||||
``_constrained_layout.layoutcolorbargridspec()`` if the colorbar is
|
||||
associated with a gridspec.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(1, 2, constrained_layout=True)
|
||||
im = ax[0].pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=ax[0], shrink=0.6)
|
||||
im = ax[1].pcolormesh(arr, **pc_kwargs)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Colorbar associated with a Gridspec
|
||||
|
||||
This example shows the Subplotspec layoutboxes being made smaller by
|
||||
a colorbar layoutbox. The size of the colorbar layoutbox is
|
||||
set to be ``shrink`` smaller than the vertical extent of the ``pos``
|
||||
layoutboxes in the gridspec, and it is made to be centered between
|
||||
those two points.
|
||||
|
||||
``` python
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flat:
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
fig.colorbar(im, ax=axs, shrink=0.6)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Uneven sized Axes
|
||||
|
||||
There are two ways to make axes have an uneven size in a
|
||||
Gridspec layout, either by specifying them to cross Gridspecs rows
|
||||
or columns, or by specifying width and height ratios.
|
||||
|
||||
The first method is used here. The constraint that makes the heights
|
||||
be correct is in the code where ``drowsC < drows0`` which in
|
||||
this case would be 1 is less than 2. So we constrain the
|
||||
height of the 1-row Axes to be less than half the height of the
|
||||
2-row Axes.
|
||||
|
||||
::: tip Note
|
||||
|
||||
This algorithm can be wrong if the decorations attached to the smaller
|
||||
axes are very large, so there is an unaccounted-for edge case.
|
||||
|
||||
:::
|
||||
|
||||
``` python
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = gridspec.GridSpec(2, 2, figure=fig)
|
||||
ax = fig.add_subplot(gs[:, 0])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[0, 1])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[1, 1])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Height and width ratios are accommodated with the same part of
|
||||
the code with the smaller axes always constrained to be less in size
|
||||
than the larger.
|
||||
|
||||
``` python
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = gridspec.GridSpec(3, 2, figure=fig,
|
||||
height_ratios=[1., 0.5, 1.5],
|
||||
width_ratios=[1.2, 0.8])
|
||||
ax = fig.add_subplot(gs[:2, 0])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[2, 0])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[0, 1])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[1:, 1])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Empty gridspec slots
|
||||
|
||||
The final piece of the code that has not been explained is what happens if
|
||||
there is an empty gridspec opening. In that case a fake invisible axes is
|
||||
added and we proceed as before. The empty gridspec has no decorations, but
|
||||
the axes position in made the same size as the occupied Axes positions.
|
||||
|
||||
This is done at the start of
|
||||
``_constrained_layout.do_constrained_layout()`` (``hassubplotspec``).
|
||||
|
||||
``` python
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = gridspec.GridSpec(1, 3, figure=fig)
|
||||
ax = fig.add_subplot(gs[0])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
ax = fig.add_subplot(gs[-1])
|
||||
im = ax.pcolormesh(arr, **pc_kwargs)
|
||||
plot_children(fig, fig._layoutbox, printit=False)
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Other notes
|
||||
|
||||
The layout is called only once. This is OK if the original layout was
|
||||
pretty close (which it should be in most cases). However, if the layout
|
||||
changes a lot from the default layout then the decorators can change size.
|
||||
In particular the x and ytick labels can change. If this happens, then
|
||||
we should probably call the whole routine twice.
|
||||
|
||||
**Total running time of the script:** ( 0 minutes 15.551 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: constrainedlayout_guide.py](https://matplotlib.org/_downloads/20cdf5d6a41b563e2ad7f13d2f8eb742/constrainedlayout_guide.py)
|
||||
- [Download Jupyter notebook: constrainedlayout_guide.ipynb](https://matplotlib.org/_downloads/22d57b5ff690950502e071d423750e4a/constrainedlayout_guide.ipynb)
|
||||
|
||||
305
Python/matplotlab/intermediate/gridspec.md
Normal file
305
Python/matplotlab/intermediate/gridspec.md
Normal file
@@ -0,0 +1,305 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Customizing Figure Layouts Using GridSpec and Other Functions
|
||||
|
||||
How to create grid-shaped combinations of axes.
|
||||
|
||||
[``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots)
|
||||
|
||||
[``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec)
|
||||
|
||||
[``SubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.SubplotSpec.html#matplotlib.gridspec.SubplotSpec)
|
||||
|
||||
[``subplot2grid()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid)
|
||||
|
||||
``` python
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
```
|
||||
|
||||
## Basic Quickstart Guide
|
||||
|
||||
These first two examples show how to create a basic 2-by-2 grid using
|
||||
both [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) and [``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec).
|
||||
|
||||
Using [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) is quite simple.
|
||||
It returns a [``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure) instance and an array of
|
||||
[``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) objects.
|
||||
|
||||
``` python
|
||||
fig1, f1_axes = plt.subplots(ncols=2, nrows=2, constrained_layout=True)
|
||||
```
|
||||
|
||||

|
||||
|
||||
For a simple use case such as this, [``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec) is
|
||||
perhaps overly verbose.
|
||||
You have to create the figure and [``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec)
|
||||
instance separately, then pass elements of gridspec instance to the
|
||||
[``add_subplot()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_subplot) method to create the axes
|
||||
objects.
|
||||
The elements of the gridspec are accessed in generally the same manner as
|
||||
numpy arrays.
|
||||
|
||||
``` python
|
||||
fig2 = plt.figure(constrained_layout=True)
|
||||
spec2 = gridspec.GridSpec(ncols=2, nrows=2, figure=fig2)
|
||||
f2_ax1 = fig2.add_subplot(spec2[0, 0])
|
||||
f2_ax2 = fig2.add_subplot(spec2[0, 1])
|
||||
f2_ax3 = fig2.add_subplot(spec2[1, 0])
|
||||
f2_ax4 = fig2.add_subplot(spec2[1, 1])
|
||||
```
|
||||
|
||||

|
||||
|
||||
The power of gridspec comes in being able to create subplots that span
|
||||
rows and columns. Note the
|
||||
[Numpy slice](https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html)
|
||||
syntax for selecting the part of the gridspec each subplot will occupy.
|
||||
|
||||
Note that we have also used the convenience method [``Figure.add_gridspec``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_gridspec)
|
||||
instead of [``gridspec.GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec), potentially saving the user an import,
|
||||
and keeping the namespace cleaner.
|
||||
|
||||
``` python
|
||||
fig3 = plt.figure(constrained_layout=True)
|
||||
gs = fig3.add_gridspec(3, 3)
|
||||
f3_ax1 = fig3.add_subplot(gs[0, :])
|
||||
f3_ax1.set_title('gs[0, :]')
|
||||
f3_ax2 = fig3.add_subplot(gs[1, :-1])
|
||||
f3_ax2.set_title('gs[1, :-1]')
|
||||
f3_ax3 = fig3.add_subplot(gs[1:, -1])
|
||||
f3_ax3.set_title('gs[1:, -1]')
|
||||
f3_ax4 = fig3.add_subplot(gs[-1, 0])
|
||||
f3_ax4.set_title('gs[-1, 0]')
|
||||
f3_ax5 = fig3.add_subplot(gs[-1, -2])
|
||||
f3_ax5.set_title('gs[-1, -2]')
|
||||
```
|
||||
|
||||

|
||||
|
||||
[``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec) is also indispensable for creating subplots
|
||||
of different widths via a couple of methods.
|
||||
|
||||
The method shown here is similar to the one above and initializes a
|
||||
uniform grid specification,
|
||||
and then uses numpy indexing and slices to allocate multiple
|
||||
"cells" for a given subplot.
|
||||
|
||||
``` python
|
||||
fig4 = plt.figure(constrained_layout=True)
|
||||
spec4 = fig4.add_gridspec(ncols=2, nrows=2)
|
||||
anno_opts = dict(xy=(0.5, 0.5), xycoords='axes fraction',
|
||||
va='center', ha='center')
|
||||
|
||||
f4_ax1 = fig4.add_subplot(spec4[0, 0])
|
||||
f4_ax1.annotate('GridSpec[0, 0]', **anno_opts)
|
||||
fig4.add_subplot(spec4[0, 1]).annotate('GridSpec[0, 1:]', **anno_opts)
|
||||
fig4.add_subplot(spec4[1, 0]).annotate('GridSpec[1:, 0]', **anno_opts)
|
||||
fig4.add_subplot(spec4[1, 1]).annotate('GridSpec[1:, 1:]', **anno_opts)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Another option is to use the ``width_ratios`` and ``height_ratios``
|
||||
parameters. These keyword arguments are lists of numbers.
|
||||
Note that absolute values are meaningless, only their relative ratios
|
||||
matter. That means that ``width_ratios=[2, 4, 8]`` is equivalent to
|
||||
``width_ratios=[1, 2, 4]`` within equally wide figures.
|
||||
For the sake of demonstration, we'll blindly create the axes within
|
||||
``for`` loops since we won't need them later.
|
||||
|
||||
``` python
|
||||
fig5 = plt.figure(constrained_layout=True)
|
||||
widths = [2, 3, 1.5]
|
||||
heights = [1, 3, 2]
|
||||
spec5 = fig5.add_gridspec(ncols=3, nrows=3, width_ratios=widths,
|
||||
height_ratios=heights)
|
||||
for row in range(3):
|
||||
for col in range(3):
|
||||
ax = fig5.add_subplot(spec5[row, col])
|
||||
label = 'Width: {}\nHeight: {}'.format(widths[col], heights[row])
|
||||
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
|
||||
```
|
||||
|
||||

|
||||
|
||||
Learning to use ``width_ratios`` and ``height_ratios`` is particularly
|
||||
useful since the top-level function [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots)
|
||||
accepts them within the ``gridspec_kw`` parameter.
|
||||
For that matter, any parameter accepted by
|
||||
[``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec) can be passed to
|
||||
[``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) via the ``gridspec_kw`` parameter.
|
||||
This example recreates the previous figure without directly using a
|
||||
gridspec instance.
|
||||
|
||||
``` python
|
||||
gs_kw = dict(width_ratios=widths, height_ratios=heights)
|
||||
fig6, f6_axes = plt.subplots(ncols=3, nrows=3, constrained_layout=True,
|
||||
gridspec_kw=gs_kw)
|
||||
for r, row in enumerate(f6_axes):
|
||||
for c, ax in enumerate(row):
|
||||
label = 'Width: {}\nHeight: {}'.format(widths[c], heights[r])
|
||||
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
|
||||
```
|
||||
|
||||

|
||||
|
||||
The ``subplots`` and ``gridspec`` methods can be combined since it is
|
||||
sometimes more convenient to make most of the subplots using ``subplots``
|
||||
and then remove some and combine them. Here we create a layout with
|
||||
the bottom two axes in the last column combined.
|
||||
|
||||
``` python
|
||||
fig7, f7_axs = plt.subplots(ncols=3, nrows=3)
|
||||
gs = f7_axs[1, 2].get_gridspec()
|
||||
# remove the underlying axes
|
||||
for ax in f7_axs[1:, -1]:
|
||||
ax.remove()
|
||||
axbig = fig7.add_subplot(gs[1:, -1])
|
||||
axbig.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
|
||||
xycoords='axes fraction', va='center')
|
||||
|
||||
fig7.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Fine Adjustments to a Gridspec Layout
|
||||
|
||||
When a GridSpec is explicitly used, you can adjust the layout
|
||||
parameters of subplots that are created from the GridSpec. Note this
|
||||
option is not compatible with ``constrained_layout`` or
|
||||
[``Figure.tight_layout``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.tight_layout) which both adjust subplot sizes to fill the
|
||||
figure.
|
||||
|
||||
``` python
|
||||
fig8 = plt.figure(constrained_layout=False)
|
||||
gs1 = fig8.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48, wspace=0.05)
|
||||
f8_ax1 = fig8.add_subplot(gs1[:-1, :])
|
||||
f8_ax2 = fig8.add_subplot(gs1[-1, :-1])
|
||||
f8_ax3 = fig8.add_subplot(gs1[-1, -1])
|
||||
```
|
||||
|
||||

|
||||
|
||||
This is similar to [``subplots_adjust()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots_adjust.html#matplotlib.pyplot.subplots_adjust), but it only
|
||||
affects the subplots that are created from the given GridSpec.
|
||||
|
||||
For example, compare the left and right sides of this figure:
|
||||
|
||||
``` python
|
||||
fig9 = plt.figure(constrained_layout=False)
|
||||
gs1 = fig9.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48,
|
||||
wspace=0.05)
|
||||
f9_ax1 = fig9.add_subplot(gs1[:-1, :])
|
||||
f9_ax2 = fig9.add_subplot(gs1[-1, :-1])
|
||||
f9_ax3 = fig9.add_subplot(gs1[-1, -1])
|
||||
|
||||
gs2 = fig9.add_gridspec(nrows=3, ncols=3, left=0.55, right=0.98,
|
||||
hspace=0.05)
|
||||
f9_ax4 = fig9.add_subplot(gs2[:, :-1])
|
||||
f9_ax5 = fig9.add_subplot(gs2[:-1, -1])
|
||||
f9_ax6 = fig9.add_subplot(gs2[-1, -1])
|
||||
```
|
||||
|
||||

|
||||
|
||||
## GridSpec using SubplotSpec
|
||||
|
||||
You can create GridSpec from the [``SubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.SubplotSpec.html#matplotlib.gridspec.SubplotSpec),
|
||||
in which case its layout parameters are set to that of the location of
|
||||
the given SubplotSpec.
|
||||
|
||||
Note this is also available from the more verbose
|
||||
[``gridspec.GridSpecFromSubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpecFromSubplotSpec.html#matplotlib.gridspec.GridSpecFromSubplotSpec).
|
||||
|
||||
``` python
|
||||
fig10 = plt.figure(constrained_layout=True)
|
||||
gs0 = fig10.add_gridspec(1, 2)
|
||||
|
||||
gs00 = gs0[0].subgridspec(2, 3)
|
||||
gs01 = gs0[1].subgridspec(3, 2)
|
||||
|
||||
for a in range(2):
|
||||
for b in range(3):
|
||||
fig10.add_subplot(gs00[a, b])
|
||||
fig10.add_subplot(gs01[b, a])
|
||||
```
|
||||
|
||||

|
||||
|
||||
## A Complex Nested GridSpec using SubplotSpec
|
||||
|
||||
Here's a more sophisticated example of nested GridSpec where we put
|
||||
a box around each cell of the outer 4x4 grid, by hiding appropriate
|
||||
spines in each of the inner 3x3 grids.
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
from itertools import product
|
||||
|
||||
|
||||
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
|
||||
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
|
||||
|
||||
|
||||
fig11 = plt.figure(figsize=(8, 8), constrained_layout=False)
|
||||
|
||||
# gridspec inside gridspec
|
||||
outer_grid = fig11.add_gridspec(4, 4, wspace=0.0, hspace=0.0)
|
||||
|
||||
for i in range(16):
|
||||
inner_grid = outer_grid[i].subgridspec(3, 3, wspace=0.0, hspace=0.0)
|
||||
a, b = int(i/4)+1, i % 4+1
|
||||
for j, (c, d) in enumerate(product(range(1, 4), repeat=2)):
|
||||
ax = fig11.add_subplot(inner_grid[j])
|
||||
ax.plot(*squiggle_xy(a, b, c, d))
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
fig11.add_subplot(ax)
|
||||
|
||||
all_axes = fig11.get_axes()
|
||||
|
||||
# show only the outside spines
|
||||
for ax in all_axes:
|
||||
for sp in ax.spines.values():
|
||||
sp.set_visible(False)
|
||||
if ax.is_first_row():
|
||||
ax.spines['top'].set_visible(True)
|
||||
if ax.is_last_row():
|
||||
ax.spines['bottom'].set_visible(True)
|
||||
if ax.is_first_col():
|
||||
ax.spines['left'].set_visible(True)
|
||||
if ax.is_last_col():
|
||||
ax.spines['right'].set_visible(True)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
### References
|
||||
|
||||
The usage of the following functions and methods is shown in this example:
|
||||
|
||||
``` python
|
||||
matplotlib.pyplot.subplots
|
||||
matplotlib.figure.Figure.add_gridspec
|
||||
matplotlib.figure.Figure.add_subplot
|
||||
matplotlib.gridspec.GridSpec
|
||||
matplotlib.gridspec.SubplotSpec.subgridspec
|
||||
matplotlib.gridspec.GridSpecFromSubplotSpec
|
||||
```
|
||||
|
||||
**Total running time of the script:** ( 0 minutes 8.732 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: gridspec.py](https://matplotlib.org/_downloads/54501e30d0a29665618afe715673cb41/gridspec.py)
|
||||
- [Download Jupyter notebook: gridspec.ipynb](https://matplotlib.org/_downloads/0eaf234b06f4f7a6a52fa9ca11b63755/gridspec.ipynb)
|
||||
|
||||
295
Python/matplotlab/intermediate/imshow_extent.md
Normal file
295
Python/matplotlab/intermediate/imshow_extent.md
Normal file
@@ -0,0 +1,295 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# origin and extent in ``imshow``
|
||||
|
||||
[``imshow()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.imshow.html#matplotlib.axes.Axes.imshow) allows you to render an image (either a 2D array
|
||||
which will be color-mapped (based on *norm* and *cmap*) or and 3D RGB(A)
|
||||
array which will be used as-is) to a rectangular region in dataspace.
|
||||
The orientation of the image in the final rendering is controlled by
|
||||
the *origin* and *extent* kwargs (and attributes on the resulting
|
||||
[``AxesImage``](https://matplotlib.orgapi/image_api.html#matplotlib.image.AxesImage) instance) and the data limits of the axes.
|
||||
|
||||
The *extent* kwarg controls the bounding box in data coordinates that
|
||||
the image will fill specified as ``(left, right, bottom, top)`` in
|
||||
**data coordinates**, the *origin* kwarg controls how the image fills
|
||||
that bounding box, and the orientation in the final rendered image is
|
||||
also affected by the axes limits.
|
||||
|
||||
Most of the code below is used for adding labels and informative
|
||||
text to the plots. The described effects of *origin* and *extent* can be
|
||||
seen in the plots without the need to follow all code details.
|
||||
|
||||
For a quick understanding, you may want to skip the code details below and
|
||||
directly continue with the discussion of the results.
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.gridspec import GridSpec
|
||||
|
||||
|
||||
def index_to_coordinate(index, extent, origin):
|
||||
"""Return the pixel center of an index."""
|
||||
left, right, bottom, top = extent
|
||||
|
||||
hshift = 0.5 * np.sign(right - left)
|
||||
left, right = left + hshift, right - hshift
|
||||
vshift = 0.5 * np.sign(top - bottom)
|
||||
bottom, top = bottom + vshift, top - vshift
|
||||
|
||||
if origin == 'upper':
|
||||
bottom, top = top, bottom
|
||||
|
||||
return {
|
||||
"[0, 0]": (left, bottom),
|
||||
"[M', 0]": (left, top),
|
||||
"[0, N']": (right, bottom),
|
||||
"[M', N']": (right, top),
|
||||
}[index]
|
||||
|
||||
|
||||
def get_index_label_pos(index, extent, origin, inverted_xindex):
|
||||
"""
|
||||
Return the desired position and horizontal alignment of an index label.
|
||||
"""
|
||||
if extent is None:
|
||||
extent = lookup_extent(origin)
|
||||
left, right, bottom, top = extent
|
||||
x, y = index_to_coordinate(index, extent, origin)
|
||||
|
||||
is_x0 = index[-2:] == "0]"
|
||||
halign = 'left' if is_x0 ^ inverted_xindex else 'right'
|
||||
hshift = 0.5 * np.sign(left - right)
|
||||
x += hshift * (1 if is_x0 else -1)
|
||||
return x, y, halign
|
||||
|
||||
|
||||
def get_color(index, data, cmap):
|
||||
"""Return the data color of an index."""
|
||||
val = {
|
||||
"[0, 0]": data[0, 0],
|
||||
"[0, N']": data[0, -1],
|
||||
"[M', 0]": data[-1, 0],
|
||||
"[M', N']": data[-1, -1],
|
||||
}[index]
|
||||
return cmap(val / data.max())
|
||||
|
||||
|
||||
def lookup_extent(origin):
|
||||
"""Return extent for label positioning when not given explicitly."""
|
||||
if origin == 'lower':
|
||||
return (-0.5, 6.5, -0.5, 5.5)
|
||||
else:
|
||||
return (-0.5, 6.5, 5.5, -0.5)
|
||||
|
||||
|
||||
def set_extent_None_text(ax):
|
||||
ax.text(3, 2.5, 'equals\nextent=None', size='large',
|
||||
ha='center', va='center', color='w')
|
||||
|
||||
|
||||
def plot_imshow_with_labels(ax, data, extent, origin, xlim, ylim):
|
||||
"""Actually run ``imshow()`` and add extent and index labels."""
|
||||
im = ax.imshow(data, origin=origin, extent=extent)
|
||||
|
||||
# extent labels (left, right, bottom, top)
|
||||
left, right, bottom, top = im.get_extent()
|
||||
if xlim is None or top > bottom:
|
||||
upper_string, lower_string = 'top', 'bottom'
|
||||
else:
|
||||
upper_string, lower_string = 'bottom', 'top'
|
||||
if ylim is None or left < right:
|
||||
port_string, starboard_string = 'left', 'right'
|
||||
inverted_xindex = False
|
||||
else:
|
||||
port_string, starboard_string = 'right', 'left'
|
||||
inverted_xindex = True
|
||||
bbox_kwargs = {'fc': 'w', 'alpha': .75, 'boxstyle': "round4"}
|
||||
ann_kwargs = {'xycoords': 'axes fraction',
|
||||
'textcoords': 'offset points',
|
||||
'bbox': bbox_kwargs}
|
||||
ax.annotate(upper_string, xy=(.5, 1), xytext=(0, -1),
|
||||
ha='center', va='top', **ann_kwargs)
|
||||
ax.annotate(lower_string, xy=(.5, 0), xytext=(0, 1),
|
||||
ha='center', va='bottom', **ann_kwargs)
|
||||
ax.annotate(port_string, xy=(0, .5), xytext=(1, 0),
|
||||
ha='left', va='center', rotation=90,
|
||||
**ann_kwargs)
|
||||
ax.annotate(starboard_string, xy=(1, .5), xytext=(-1, 0),
|
||||
ha='right', va='center', rotation=-90,
|
||||
**ann_kwargs)
|
||||
ax.set_title('origin: {origin}'.format(origin=origin))
|
||||
|
||||
# index labels
|
||||
for index in ["[0, 0]", "[0, N']", "[M', 0]", "[M', N']"]:
|
||||
tx, ty, halign = get_index_label_pos(index, extent, origin,
|
||||
inverted_xindex)
|
||||
facecolor = get_color(index, data, im.get_cmap())
|
||||
ax.text(tx, ty, index, color='white', ha=halign, va='center',
|
||||
bbox={'boxstyle': 'square', 'facecolor': facecolor})
|
||||
if xlim:
|
||||
ax.set_xlim(*xlim)
|
||||
if ylim:
|
||||
ax.set_ylim(*ylim)
|
||||
|
||||
|
||||
def generate_imshow_demo_grid(extents, xlim=None, ylim=None):
|
||||
N = len(extents)
|
||||
fig = plt.figure(tight_layout=True)
|
||||
fig.set_size_inches(6, N * (11.25) / 5)
|
||||
gs = GridSpec(N, 5, figure=fig)
|
||||
|
||||
columns = {'label': [fig.add_subplot(gs[j, 0]) for j in range(N)],
|
||||
'upper': [fig.add_subplot(gs[j, 1:3]) for j in range(N)],
|
||||
'lower': [fig.add_subplot(gs[j, 3:5]) for j in range(N)]}
|
||||
x, y = np.ogrid[0:6, 0:7]
|
||||
data = x + y
|
||||
|
||||
for origin in ['upper', 'lower']:
|
||||
for ax, extent in zip(columns[origin], extents):
|
||||
plot_imshow_with_labels(ax, data, extent, origin, xlim, ylim)
|
||||
|
||||
for ax, extent in zip(columns['label'], extents):
|
||||
text_kwargs = {'ha': 'right',
|
||||
'va': 'center',
|
||||
'xycoords': 'axes fraction',
|
||||
'xy': (1, .5)}
|
||||
if extent is None:
|
||||
ax.annotate('None', **text_kwargs)
|
||||
ax.set_title('extent=')
|
||||
else:
|
||||
left, right, bottom, top = extent
|
||||
text = ('left: {left:0.1f}\nright: {right:0.1f}\n' +
|
||||
'bottom: {bottom:0.1f}\ntop: {top:0.1f}\n').format(
|
||||
left=left, right=right, bottom=bottom, top=top)
|
||||
|
||||
ax.annotate(text, **text_kwargs)
|
||||
ax.axis('off')
|
||||
return columns
|
||||
```
|
||||
|
||||
## Default extent
|
||||
|
||||
First, let's have a look at the default ``extent=None``
|
||||
|
||||
``` python
|
||||
generate_imshow_demo_grid(extents=[None])
|
||||
```
|
||||
|
||||

|
||||
|
||||
Generally, for an array of shape (M, N), the first index runs along the
|
||||
vertical, the second index runs along the horizontal.
|
||||
The pixel centers are at integer positions ranging from 0 to ``N' = N - 1``
|
||||
horizontally and from 0 to ``M' = M - 1`` vertically.
|
||||
*origin* determines how to the data is filled in the bounding box.
|
||||
|
||||
For ``origin='lower'``:
|
||||
|
||||
- [0, 0] is at (left, bottom)
|
||||
- [M', 0] is at (left, top)
|
||||
- [0, N'] is at (right, bottom)
|
||||
- [M', N'] is at (right, top)
|
||||
|
||||
``origin='upper'`` reverses the vertical axes direction and filling:
|
||||
|
||||
- [0, 0] is at (left, top)
|
||||
- [M', 0] is at (left, bottom)
|
||||
- [0, N'] is at (right, top)
|
||||
- [M', N'] is at (right, bottom)
|
||||
|
||||
In summary, the position of the [0, 0] index as well as the extent are
|
||||
influenced by *origin*:
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
origin
|
||||
[0, 0] position
|
||||
extent
|
||||
|
||||
|
||||
|
||||
upper
|
||||
top left
|
||||
(-0.5, numcols-0.5, numrows-0.5, -0.5)
|
||||
|
||||
lower
|
||||
bottom left
|
||||
(-0.5, numcols-0.5, -0.5, numrows-0.5)
|
||||
|
||||
|
||||
|
||||
|
||||
The default value of *origin* is set by ``rcParams["image.origin"] = 'upper'`` which defaults
|
||||
to ``'upper'`` to match the matrix indexing conventions in math and
|
||||
computer graphics image indexing conventions.
|
||||
|
||||
## Explicit extent
|
||||
|
||||
By setting *extent* we define the coordinates of the image area. The
|
||||
underlying image data is interpolated/resampled to fill that area.
|
||||
|
||||
If the axes is set to autoscale, then the view limits of the axes are set
|
||||
to match the *extent* which ensures that the coordinate set by
|
||||
``(left, bottom)`` is at the bottom left of the axes! However, this
|
||||
may invert the axis so they do not increase in the 'natural' direction.
|
||||
|
||||
``` python
|
||||
extents = [(-0.5, 6.5, -0.5, 5.5),
|
||||
(-0.5, 6.5, 5.5, -0.5),
|
||||
(6.5, -0.5, -0.5, 5.5),
|
||||
(6.5, -0.5, 5.5, -0.5)]
|
||||
|
||||
columns = generate_imshow_demo_grid(extents)
|
||||
set_extent_None_text(columns['upper'][1])
|
||||
set_extent_None_text(columns['lower'][0])
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Explicit extent and axes limits
|
||||
|
||||
If we fix the axes limits by explicitly setting ``set_xlim`` / ``set_ylim``, we
|
||||
force a certain size and orientation of the axes.
|
||||
This can decouple the 'left-right' and 'top-bottom' sense of the image from
|
||||
the orientation on the screen.
|
||||
|
||||
In the example below we have chosen the limits slightly larger than the
|
||||
extent (note the white areas within the Axes).
|
||||
|
||||
While we keep the extents as in the examples before, the coordinate (0, 0)
|
||||
is now explicitly put at the bottom left and values increase to up and to
|
||||
the right (from the viewer point of view).
|
||||
We can see that:
|
||||
|
||||
- The coordinate ``(left, bottom)`` anchors the image which then fills the
|
||||
box going towards the ``(right, top)`` point in data space.
|
||||
- The first column is always closest to the 'left'.
|
||||
- *origin* controls if the first row is closest to 'top' or 'bottom'.
|
||||
- The image may be inverted along either direction.
|
||||
- The 'left-right' and 'top-bottom' sense of the image may be uncoupled from
|
||||
the orientation on the screen.
|
||||
|
||||
``` python
|
||||
generate_imshow_demo_grid(extents=[None] + extents,
|
||||
xlim=(-2, 8), ylim=(-1, 6))
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Total running time of the script:** ( 0 minutes 2.056 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: imshow_extent.py](https://matplotlib.org/_downloads/1b073a3f2fab4eae80964340b65629bc/imshow_extent.py)
|
||||
- [Download Jupyter notebook: imshow_extent.ipynb](https://matplotlib.org/_downloads/e7e77a6502f9e28a843cccc17c2dfd89/imshow_extent.ipynb)
|
||||
|
||||
314
Python/matplotlab/intermediate/legend_guide.md
Normal file
314
Python/matplotlab/intermediate/legend_guide.md
Normal file
@@ -0,0 +1,314 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Legend guide
|
||||
|
||||
Generating legends flexibly in Matplotlib.
|
||||
|
||||
This legend guide is an extension of the documentation available at
|
||||
[``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) - please ensure you are familiar with
|
||||
contents of that documentation before proceeding with this guide.
|
||||
|
||||
This guide makes use of some common terms, which are documented here for clarity:
|
||||
|
||||
legend entry
|
||||
|
||||
legend key
|
||||
|
||||
legend label
|
||||
|
||||
legend handle
|
||||
|
||||
## Controlling the legend entries
|
||||
|
||||
Calling [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) with no arguments automatically fetches the legend
|
||||
handles and their associated labels. This functionality is equivalent to:
|
||||
|
||||
``` python
|
||||
handles, labels = ax.get_legend_handles_labels()
|
||||
ax.legend(handles, labels)
|
||||
```
|
||||
|
||||
The [``get_legend_handles_labels()``](https://matplotlib.orgapi/_as_gen/matplotlib.axes.Axes.get_legend_handles_labels.html#matplotlib.axes.Axes.get_legend_handles_labels) function returns
|
||||
a list of handles/artists which exist on the Axes which can be used to
|
||||
generate entries for the resulting legend - it is worth noting however that
|
||||
not all artists can be added to a legend, at which point a "proxy" will have
|
||||
to be created (see [Creating artists specifically for adding to the legend (aka. Proxy artists)](#proxy-legend-handles) for further details).
|
||||
|
||||
For full control of what is being added to the legend, it is common to pass
|
||||
the appropriate handles directly to [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend):
|
||||
|
||||
``` python
|
||||
line_up, = plt.plot([1,2,3], label='Line 2')
|
||||
line_down, = plt.plot([3,2,1], label='Line 1')
|
||||
plt.legend(handles=[line_up, line_down])
|
||||
```
|
||||
|
||||
In some cases, it is not possible to set the label of the handle, so it is
|
||||
possible to pass through the list of labels to [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend):
|
||||
|
||||
``` python
|
||||
line_up, = plt.plot([1,2,3], label='Line 2')
|
||||
line_down, = plt.plot([3,2,1], label='Line 1')
|
||||
plt.legend([line_up, line_down], ['Line Up', 'Line Down'])
|
||||
```
|
||||
|
||||
## Creating artists specifically for adding to the legend (aka. Proxy artists)
|
||||
|
||||
Not all handles can be turned into legend entries automatically,
|
||||
so it is often necessary to create an artist which *can*. Legend handles
|
||||
don't have to exists on the Figure or Axes in order to be used.
|
||||
|
||||
Suppose we wanted to create a legend which has an entry for some data which
|
||||
is represented by a red color:
|
||||
|
||||
``` python
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
red_patch = mpatches.Patch(color='red', label='The red data')
|
||||
plt.legend(handles=[red_patch])
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
There are many supported legend handles, instead of creating a patch of color
|
||||
we could have created a line with a marker:
|
||||
|
||||
``` python
|
||||
import matplotlib.lines as mlines
|
||||
|
||||
blue_line = mlines.Line2D([], [], color='blue', marker='*',
|
||||
markersize=15, label='Blue stars')
|
||||
plt.legend(handles=[blue_line])
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Legend location
|
||||
|
||||
The location of the legend can be specified by the keyword argument
|
||||
*loc*. Please see the documentation at [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) for more details.
|
||||
|
||||
The ``bbox_to_anchor`` keyword gives a great degree of control for manual
|
||||
legend placement. For example, if you want your axes legend located at the
|
||||
figure's top right-hand corner instead of the axes' corner, simply specify
|
||||
the corner's location, and the coordinate system of that location:
|
||||
|
||||
``` python
|
||||
plt.legend(bbox_to_anchor=(1, 1),
|
||||
bbox_transform=plt.gcf().transFigure)
|
||||
```
|
||||
|
||||
More examples of custom legend placement:
|
||||
|
||||
``` python
|
||||
plt.subplot(211)
|
||||
plt.plot([1, 2, 3], label="test1")
|
||||
plt.plot([3, 2, 1], label="test2")
|
||||
|
||||
# Place a legend above this subplot, expanding itself to
|
||||
# fully use the given bounding box.
|
||||
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left',
|
||||
ncol=2, mode="expand", borderaxespad=0.)
|
||||
|
||||
plt.subplot(223)
|
||||
plt.plot([1, 2, 3], label="test1")
|
||||
plt.plot([3, 2, 1], label="test2")
|
||||
# Place a legend to the right of this smaller subplot.
|
||||
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Multiple legends on the same Axes
|
||||
|
||||
Sometimes it is more clear to split legend entries across multiple
|
||||
legends. Whilst the instinctive approach to doing this might be to call
|
||||
the [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) function multiple times, you will find that only one
|
||||
legend ever exists on the Axes. This has been done so that it is possible
|
||||
to call [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) repeatedly to update the legend to the latest
|
||||
handles on the Axes, so to persist old legend instances, we must add them
|
||||
manually to the Axes:
|
||||
|
||||
``` python
|
||||
line1, = plt.plot([1, 2, 3], label="Line 1", linestyle='--')
|
||||
line2, = plt.plot([3, 2, 1], label="Line 2", linewidth=4)
|
||||
|
||||
# Create a legend for the first line.
|
||||
first_legend = plt.legend(handles=[line1], loc='upper right')
|
||||
|
||||
# Add the legend manually to the current Axes.
|
||||
ax = plt.gca().add_artist(first_legend)
|
||||
|
||||
# Create another legend for the second line.
|
||||
plt.legend(handles=[line2], loc='lower right')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Legend Handlers
|
||||
|
||||
In order to create legend entries, handles are given as an argument to an
|
||||
appropriate [``HandlerBase``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerBase) subclass.
|
||||
The choice of handler subclass is determined by the following rules:
|
||||
|
||||
1. Update [``get_legend_handler_map()``](https://matplotlib.orgapi/legend_api.html#matplotlib.legend.Legend.get_legend_handler_map)
|
||||
with the value in the ``handler_map`` keyword.
|
||||
1. Check if the ``handle`` is in the newly created ``handler_map``.
|
||||
1. Check if the type of ``handle`` is in the newly created
|
||||
``handler_map``.
|
||||
1. Check if any of the types in the ``handle``'s mro is in the newly
|
||||
created ``handler_map``.
|
||||
|
||||
For completeness, this logic is mostly implemented in
|
||||
[``get_legend_handler()``](https://matplotlib.orgapi/legend_api.html#matplotlib.legend.Legend.get_legend_handler).
|
||||
|
||||
All of this flexibility means that we have the necessary hooks to implement
|
||||
custom handlers for our own type of legend key.
|
||||
|
||||
The simplest example of using custom handlers is to instantiate one of the
|
||||
existing [``HandlerBase``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerBase) subclasses. For the
|
||||
sake of simplicity, let's choose [``matplotlib.legend_handler.HandlerLine2D``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerLine2D)
|
||||
which accepts a ``numpoints`` argument (note numpoints is a keyword
|
||||
on the [``legend()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend) function for convenience). We can then pass the mapping
|
||||
of instance to Handler as a keyword to legend.
|
||||
|
||||
``` python
|
||||
from matplotlib.legend_handler import HandlerLine2D
|
||||
|
||||
line1, = plt.plot([3, 2, 1], marker='o', label='Line 1')
|
||||
line2, = plt.plot([1, 2, 3], marker='o', label='Line 2')
|
||||
|
||||
plt.legend(handler_map={line1: HandlerLine2D(numpoints=4)})
|
||||
```
|
||||
|
||||

|
||||
|
||||
As you can see, "Line 1" now has 4 marker points, where "Line 2" has 2 (the
|
||||
default). Try the above code, only change the map's key from ``line1`` to
|
||||
``type(line1)``. Notice how now both [``Line2D``](https://matplotlib.orgapi/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D) instances
|
||||
get 4 markers.
|
||||
|
||||
Along with handlers for complex plot types such as errorbars, stem plots
|
||||
and histograms, the default ``handler_map`` has a special ``tuple`` handler
|
||||
([``HandlerTuple``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerTuple)) which simply plots
|
||||
the handles on top of one another for each item in the given tuple. The
|
||||
following example demonstrates combining two legend keys on top of one another:
|
||||
|
||||
``` python
|
||||
from numpy.random import randn
|
||||
|
||||
z = randn(10)
|
||||
|
||||
red_dot, = plt.plot(z, "ro", markersize=15)
|
||||
# Put a white cross over some of the data.
|
||||
white_cross, = plt.plot(z[:5], "w+", markeredgewidth=3, markersize=15)
|
||||
|
||||
plt.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"])
|
||||
```
|
||||
|
||||

|
||||
|
||||
The [``HandlerTuple``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerTuple) class can also be used to
|
||||
assign several legend keys to the same entry:
|
||||
|
||||
``` python
|
||||
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
|
||||
|
||||
p1, = plt.plot([1, 2.5, 3], 'r-d')
|
||||
p2, = plt.plot([3, 2, 1], 'k-o')
|
||||
|
||||
l = plt.legend([(p1, p2)], ['Two keys'], numpoints=1,
|
||||
handler_map={tuple: HandlerTuple(ndivide=None)})
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Implementing a custom legend handler
|
||||
|
||||
A custom handler can be implemented to turn any handle into a legend key (handles
|
||||
don't necessarily need to be matplotlib artists).
|
||||
The handler must implement a "legend_artist" method which returns a
|
||||
single artist for the legend to use. Signature details about the "legend_artist"
|
||||
are documented at [``legend_artist()``](https://matplotlib.orgapi/legend_handler_api.html#matplotlib.legend_handler.HandlerBase.legend_artist).
|
||||
|
||||
``` python
|
||||
import matplotlib.patches as mpatches
|
||||
|
||||
|
||||
class AnyObject(object):
|
||||
pass
|
||||
|
||||
|
||||
class AnyObjectHandler(object):
|
||||
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
|
||||
x0, y0 = handlebox.xdescent, handlebox.ydescent
|
||||
width, height = handlebox.width, handlebox.height
|
||||
patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red',
|
||||
edgecolor='black', hatch='xx', lw=3,
|
||||
transform=handlebox.get_transform())
|
||||
handlebox.add_artist(patch)
|
||||
return patch
|
||||
|
||||
|
||||
plt.legend([AnyObject()], ['My first handler'],
|
||||
handler_map={AnyObject: AnyObjectHandler()})
|
||||
```
|
||||
|
||||

|
||||
|
||||
Alternatively, had we wanted to globally accept ``AnyObject`` instances without
|
||||
needing to manually set the ``handler_map`` keyword all the time, we could have
|
||||
registered the new handler with:
|
||||
|
||||
``` python
|
||||
from matplotlib.legend import Legend
|
||||
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
|
||||
```
|
||||
|
||||
Whilst the power here is clear, remember that there are already many handlers
|
||||
implemented and what you want to achieve may already be easily possible with
|
||||
existing classes. For example, to produce elliptical legend keys, rather than
|
||||
rectangular ones:
|
||||
|
||||
``` python
|
||||
from matplotlib.legend_handler import HandlerPatch
|
||||
|
||||
|
||||
class HandlerEllipse(HandlerPatch):
|
||||
def create_artists(self, legend, orig_handle,
|
||||
xdescent, ydescent, width, height, fontsize, trans):
|
||||
center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
|
||||
p = mpatches.Ellipse(xy=center, width=width + xdescent,
|
||||
height=height + ydescent)
|
||||
self.update_prop(p, orig_handle, legend)
|
||||
p.set_transform(trans)
|
||||
return [p]
|
||||
|
||||
|
||||
c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green",
|
||||
edgecolor="red", linewidth=3)
|
||||
plt.gca().add_patch(c)
|
||||
|
||||
plt.legend([c], ["An ellipse, not a rectangle"],
|
||||
handler_map={mpatches.Circle: HandlerEllipse()})
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: legend_guide.py](https://matplotlib.org/_downloads/65714cd51723c032709ddf40dd43e3cf/legend_guide.py)
|
||||
- [Download Jupyter notebook: legend_guide.ipynb](https://matplotlib.org/_downloads/89b61becf2e0c701373e39916f7b5428/legend_guide.ipynb)
|
||||
|
||||
418
Python/matplotlab/intermediate/tight_layout_guide.md
Normal file
418
Python/matplotlab/intermediate/tight_layout_guide.md
Normal file
@@ -0,0 +1,418 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Tight Layout guide
|
||||
|
||||
How to use tight-layout to fit plots within your figure cleanly.
|
||||
|
||||
*tight_layout* automatically adjusts subplot params so that the
|
||||
subplot(s) fits in to the figure area. This is an experimental
|
||||
feature and may not work for some cases. It only checks the extents
|
||||
of ticklabels, axis labels, and titles.
|
||||
|
||||
An alternative to *tight_layout* is [constrained_layout](constrainedlayout_guide.html).
|
||||
|
||||
## Simple Example
|
||||
|
||||
In matplotlib, the location of axes (including subplots) are specified in
|
||||
normalized figure coordinates. It can happen that your axis labels or
|
||||
titles (or sometimes even ticklabels) go outside the figure area, and are thus
|
||||
clipped.
|
||||
|
||||
``` python
|
||||
# sphinx_gallery_thumbnail_number = 7
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
plt.rcParams['savefig.facecolor'] = "0.8"
|
||||
|
||||
|
||||
def example_plot(ax, fontsize=12):
|
||||
ax.plot([1, 2])
|
||||
|
||||
ax.locator_params(nbins=3)
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Title', fontsize=fontsize)
|
||||
|
||||
plt.close('all')
|
||||
fig, ax = plt.subplots()
|
||||
example_plot(ax, fontsize=24)
|
||||
```
|
||||
|
||||

|
||||
|
||||
To prevent this, the location of axes needs to be adjusted. For
|
||||
subplots, this can be done by adjusting the subplot params
|
||||
([Move the edge of an axes to make room for tick labels](https://matplotlib.orgfaq/howto_faq.html#howto-subplots-adjust)). Matplotlib v1.1 introduces a new
|
||||
command [``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) that does this
|
||||
automatically for you.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots()
|
||||
example_plot(ax, fontsize=24)
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
Note that [``matplotlib.pyplot.tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) will only adjust the
|
||||
subplot params when it is called. In order to perform this adjustment each
|
||||
time the figure is redrawn, you can call ``fig.set_tight_layout(True)``, or,
|
||||
equivalently, set the ``figure.autolayout`` rcParam to ``True``.
|
||||
|
||||
When you have multiple subplots, often you see labels of different
|
||||
axes overlapping each other.
|
||||
|
||||
``` python
|
||||
plt.close('all')
|
||||
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
```
|
||||
|
||||

|
||||
|
||||
[``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) will also adjust spacing between
|
||||
subplots to minimize the overlaps.
|
||||
|
||||
``` python
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
[``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) can take keyword arguments of
|
||||
*pad*, *w_pad* and *h_pad*. These control the extra padding around the
|
||||
figure border and between subplots. The pads are specified in fraction
|
||||
of fontsize.
|
||||
|
||||
``` python
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
|
||||
```
|
||||
|
||||

|
||||
|
||||
[``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) will work even if the sizes of
|
||||
subplots are different as far as their grid specification is
|
||||
compatible. In the example below, *ax1* and *ax2* are subplots of a 2x2
|
||||
grid, while *ax3* is of a 1x2 grid.
|
||||
|
||||
``` python
|
||||
plt.close('all')
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot(221)
|
||||
ax2 = plt.subplot(223)
|
||||
ax3 = plt.subplot(122)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
It works with subplots created with
|
||||
[``subplot2grid()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid). In general, subplots created
|
||||
from the gridspec ([Customizing Figure Layouts Using GridSpec and Other Functions](gridspec.html)) will work.
|
||||
|
||||
``` python
|
||||
plt.close('all')
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot2grid((3, 3), (0, 0))
|
||||
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
|
||||
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
|
||||
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
Although not thoroughly tested, it seems to work for subplots with
|
||||
aspect != "auto" (e.g., axes with images).
|
||||
|
||||
``` python
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
|
||||
plt.close('all')
|
||||
fig = plt.figure(figsize=(5, 4))
|
||||
|
||||
ax = plt.subplot(111)
|
||||
im = ax.imshow(arr, interpolation="none")
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Caveats
|
||||
|
||||
- [``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) only considers ticklabels, axis
|
||||
labels, and titles. Thus, other artists may be clipped and also may
|
||||
overlap.
|
||||
- It assumes that the extra space needed for ticklabels, axis labels,
|
||||
and titles is independent of original location of axes. This is
|
||||
often true, but there are rare cases where it is not.
|
||||
- pad=0 clips some of the texts by a few pixels. This may be a bug or
|
||||
a limitation of the current algorithm and it is not clear why it
|
||||
happens. Meanwhile, use of pad at least larger than 0.3 is
|
||||
recommended.
|
||||
|
||||
## Use with GridSpec
|
||||
|
||||
GridSpec has its own [``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec.tight_layout) method
|
||||
(the pyplot api [``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout) also works).
|
||||
|
||||
``` python
|
||||
import matplotlib.gridspec as gridspec
|
||||
|
||||
plt.close('all')
|
||||
fig = plt.figure()
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs1.tight_layout(fig)
|
||||
```
|
||||
|
||||

|
||||
|
||||
You may provide an optional *rect* parameter, which specifies the bounding box
|
||||
that the subplots will be fit inside. The coordinates must be in normalized
|
||||
figure coordinates and the default is (0, 0, 1, 1).
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
|
||||
```
|
||||
|
||||

|
||||
|
||||
For example, this can be used for a figure with multiple gridspecs.
|
||||
|
||||
``` python
|
||||
fig = plt.figure()
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
|
||||
|
||||
gs2 = gridspec.GridSpec(3, 1)
|
||||
|
||||
for ss in gs2:
|
||||
ax = fig.add_subplot(ss)
|
||||
example_plot(ax)
|
||||
ax.set_title("")
|
||||
ax.set_xlabel("")
|
||||
|
||||
ax.set_xlabel("x-label", fontsize=12)
|
||||
|
||||
gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.5)
|
||||
|
||||
# We may try to match the top and bottom of two grids ::
|
||||
top = min(gs1.top, gs2.top)
|
||||
bottom = max(gs1.bottom, gs2.bottom)
|
||||
|
||||
gs1.update(top=top, bottom=bottom)
|
||||
gs2.update(top=top, bottom=bottom)
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
While this should be mostly good enough, adjusting top and bottom
|
||||
may require adjustment of hspace also. To update hspace & vspace, we
|
||||
call [``tight_layout()``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec.tight_layout) again with updated
|
||||
rect argument. Note that the rect argument specifies the area including the
|
||||
ticklabels, etc. Thus, we will increase the bottom (which is 0 for the normal
|
||||
case) by the difference between the *bottom* from above and the bottom of each
|
||||
gridspec. Same thing for the top.
|
||||
|
||||
``` python
|
||||
fig = plt.gcf()
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
|
||||
|
||||
gs2 = gridspec.GridSpec(3, 1)
|
||||
|
||||
for ss in gs2:
|
||||
ax = fig.add_subplot(ss)
|
||||
example_plot(ax)
|
||||
ax.set_title("")
|
||||
ax.set_xlabel("")
|
||||
|
||||
ax.set_xlabel("x-label", fontsize=12)
|
||||
|
||||
gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.5)
|
||||
|
||||
top = min(gs1.top, gs2.top)
|
||||
bottom = max(gs1.bottom, gs2.bottom)
|
||||
|
||||
gs1.update(top=top, bottom=bottom)
|
||||
gs2.update(top=top, bottom=bottom)
|
||||
|
||||
top = min(gs1.top, gs2.top)
|
||||
bottom = max(gs1.bottom, gs2.bottom)
|
||||
|
||||
gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom),
|
||||
0.5, 1 - (gs1.top-top)])
|
||||
gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom),
|
||||
None, 1 - (gs2.top-top)],
|
||||
h_pad=0.5)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Legends and Annotations
|
||||
|
||||
Pre Matplotlib 2.2, legends and annotations were excluded from the bounding
|
||||
box calculations that decide the layout. Subsequently these artists were
|
||||
added to the calculation, but sometimes it is undesirable to include them.
|
||||
For instance in this case it might be good to have the axes shring a bit
|
||||
to make room for the legend:
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(figsize=(4, 3))
|
||||
lines = ax.plot(range(10), label='A simple plot')
|
||||
ax.legend(bbox_to_anchor=(0.7, 0.5), loc='center left',)
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
However, sometimes this is not desired (quite often when using
|
||||
``fig.savefig('outname.png', bbox_inches='tight')``). In order to
|
||||
remove the legend from the bounding box calculation, we simply set its
|
||||
bounding ``leg.set_in_layout(False)`` and the legend will be ignored.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(figsize=(4, 3))
|
||||
lines = ax.plot(range(10), label='B simple plot')
|
||||
leg = ax.legend(bbox_to_anchor=(0.7, 0.5), loc='center left',)
|
||||
leg.set_in_layout(False)
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Use with AxesGrid1
|
||||
|
||||
While limited, the axes_grid1 toolkit is also supported.
|
||||
|
||||
``` python
|
||||
from mpl_toolkits.axes_grid1 import Grid
|
||||
|
||||
plt.close('all')
|
||||
fig = plt.figure()
|
||||
grid = Grid(fig, rect=111, nrows_ncols=(2, 2),
|
||||
axes_pad=0.25, label_mode='L',
|
||||
)
|
||||
|
||||
for ax in grid:
|
||||
example_plot(ax)
|
||||
ax.title.set_visible(False)
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Colorbar
|
||||
|
||||
If you create a colorbar with the [``colorbar()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.colorbar.html#matplotlib.pyplot.colorbar)
|
||||
command, the created colorbar is an instance of Axes, *not* Subplot, so
|
||||
tight_layout does not work. With Matplotlib v1.1, you may create a
|
||||
colorbar as a subplot using the gridspec.
|
||||
|
||||
``` python
|
||||
plt.close('all')
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
fig = plt.figure(figsize=(4, 4))
|
||||
im = plt.imshow(arr, interpolation="none")
|
||||
|
||||
plt.colorbar(im, use_gridspec=True)
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
Another option is to use AxesGrid1 toolkit to
|
||||
explicitly create an axes for colorbar.
|
||||
|
||||
``` python
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
|
||||
plt.close('all')
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
fig = plt.figure(figsize=(4, 4))
|
||||
im = plt.imshow(arr, interpolation="none")
|
||||
|
||||
divider = make_axes_locatable(plt.gca())
|
||||
cax = divider.append_axes("right", "5%", pad="3%")
|
||||
plt.colorbar(im, cax=cax)
|
||||
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Total running time of the script:** ( 0 minutes 3.417 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: tight_layout_guide.py](https://matplotlib.org/_downloads/08204f760ca1d178acca434333c21c5c/tight_layout_guide.py)
|
||||
- [Download Jupyter notebook: tight_layout_guide.ipynb](https://matplotlib.org/_downloads/967ce4dd04ce9628b993a5a4e402046a/tight_layout_guide.ipynb)
|
||||
|
||||
Reference in New Issue
Block a user