Skip to content
Permalink
main
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
import functools
import itertools
import logging
import math
from numbers import Integral, Number
import numpy as np
from numpy import ma
import matplotlib.category # Register category unit converter as side-effect.
import matplotlib.cbook as cbook
import matplotlib.collections as mcoll
import matplotlib.colors as mcolors
import matplotlib.contour as mcontour
import matplotlib.dates # Register date unit converter as side-effect.
import matplotlib.docstring as docstring
import matplotlib.image as mimage
import matplotlib.legend as mlegend
import matplotlib.lines as mlines
import matplotlib.markers as mmarkers
import matplotlib.mlab as mlab
import matplotlib.patches as mpatches
import matplotlib.path as mpath
import matplotlib.quiver as mquiver
import matplotlib.stackplot as mstack
import matplotlib.streamplot as mstream
import matplotlib.table as mtable
import matplotlib.text as mtext
import matplotlib.ticker as mticker
import matplotlib.transforms as mtransforms
import matplotlib.tri as mtri
import matplotlib.units as munits
from matplotlib import _api, _preprocess_data, rcParams
from matplotlib.axes._base import (
_AxesBase, _TransformedBoundsLocator, _process_plot_format)
from matplotlib.axes._secondary_axes import SecondaryAxis
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer
_log = logging.getLogger(__name__)
# The axes module contains all the wrappers to plotting functions.
# All the other methods should go in the _AxesBase class.
@docstring.interpd
class Axes(_AxesBase):
"""
The `Axes` contains most of the figure elements: `~.axis.Axis`,
`~.axis.Tick`, `~.lines.Line2D`, `~.text.Text`, `~.patches.Polygon`, etc.,
and sets the coordinate system.
The `Axes` instance supports callbacks through a callbacks attribute which
is a `~.cbook.CallbackRegistry` instance. The events you can connect to
are 'xlim_changed' and 'ylim_changed' and the callback will be called with
func(*ax*) where *ax* is the `Axes` instance.
Attributes
----------
dataLim : `.Bbox`
The bounding box enclosing all data displayed in the Axes.
viewLim : `.Bbox`
The view limits in data coordinates.
"""
### Labelling, legend and texts
def get_title(self, loc="center"):
"""
Get an Axes title.
Get one of the three available Axes titles. The available titles
are positioned above the Axes in the center, flush with the left
edge, and flush with the right edge.
Parameters
----------
loc : {'center', 'left', 'right'}, str, default: 'center'
Which title to return.
Returns
-------
str
The title text string.
"""
titles = {'left': self._left_title,
'center': self.title,
'right': self._right_title}
title = _api.check_getitem(titles, loc=loc.lower())
return title.get_text()
def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None,
**kwargs):
"""
Set a title for the Axes.
Set one of the three available Axes titles. The available titles
are positioned above the Axes in the center, flush with the left
edge, and flush with the right edge.
Parameters
----------
label : str
Text to use for the title
fontdict : dict
A dictionary controlling the appearance of the title text,
the default *fontdict* is::
{'fontsize': rcParams['axes.titlesize'],
'fontweight': rcParams['axes.titleweight'],
'color': rcParams['axes.titlecolor'],
'verticalalignment': 'baseline',
'horizontalalignment': loc}
loc : {'center', 'left', 'right'}, default: :rc:`axes.titlelocation`
Which title to set.
y : float, default: :rc:`axes.titley`
Vertical Axes location for the title (1.0 is the top). If
None (the default) and :rc:`axes.titley` is also None, y is
determined automatically to avoid decorators on the Axes.
pad : float, default: :rc:`axes.titlepad`
The offset of the title from the top of the Axes, in points.
Returns
-------
`.Text`
The matplotlib text instance representing the title
Other Parameters
----------------
**kwargs : `.Text` properties
Other keyword arguments are text properties, see `.Text` for a list
of valid text properties.
"""
if loc is None:
loc = rcParams['axes.titlelocation']
if y is None:
y = rcParams['axes.titley']
if y is None:
y = 1.0
else:
self._autotitlepos = False
kwargs['y'] = y
titles = {'left': self._left_title,
'center': self.title,
'right': self._right_title}
title = _api.check_getitem(titles, loc=loc.lower())
default = {
'fontsize': rcParams['axes.titlesize'],
'fontweight': rcParams['axes.titleweight'],
'verticalalignment': 'baseline',
'horizontalalignment': loc.lower()}
titlecolor = rcParams['axes.titlecolor']
if not cbook._str_lower_equal(titlecolor, 'auto'):
default["color"] = titlecolor
if pad is None:
pad = rcParams['axes.titlepad']
self._set_title_offset_trans(float(pad))
title.set_text(label)
title.update(default)
if fontdict is not None:
title.update(fontdict)
title.update(kwargs)
return title
def get_legend_handles_labels(self, legend_handler_map=None):
"""
Return handles and labels for legend
``ax.legend()`` is equivalent to ::
h, l = ax.get_legend_handles_labels()
ax.legend(h, l)
"""
# pass through to legend.
handles, labels = mlegend._get_legend_handles_labels(
[self], legend_handler_map)
return handles, labels
@docstring.dedent_interpd
def legend(self, *args, **kwargs):
"""
Place a legend on the Axes.
Call signatures::
legend()
legend(handles, labels)
legend(handles=handles)
legend(labels)
The call signatures correspond to the following different ways to use
this method:
**1. Automatic detection of elements to be shown in the legend**
The elements to be added to the legend are automatically determined,
when you do not pass in any extra arguments.
In this case, the labels are taken from the artist. You can specify
them either at artist creation or by calling the
:meth:`~.Artist.set_label` method on the artist::
ax.plot([1, 2, 3], label='Inline label')
ax.legend()
or::
line, = ax.plot([1, 2, 3])
line.set_label('Label via method')
ax.legend()
Specific lines can be excluded from the automatic legend element
selection by defining a label starting with an underscore.
This is default for all artists, so calling `.Axes.legend` without
any arguments and without setting the labels manually will result in
no legend being drawn.
**2. Explicitly listing the artists and labels in the legend**
For full control of which artists have a legend entry, it is possible
to pass an iterable of legend artists followed by an iterable of
legend labels respectively::
ax.legend([line1, line2, line3], ['label1', 'label2', 'label3'])
**3. Explicitly listing the artists in the legend**
This is similar to 2, but the labels are taken from the artists'
label properties. Example::
line1, = ax.plot([1, 2, 3], label='label1')
line2, = ax.plot([1, 2, 3], label='label2')
ax.legend(handles=[line1, line2])
**4. Labeling existing plot elements**
.. admonition:: Discouraged
This call signature is discouraged, because the relation between
plot elements and labels is only implicit by their order and can
easily be mixed up.
To make a legend for all artists on an Axes, call this function with
an iterable of strings, one for each legend item. For example::
ax.plot([1, 2, 3])
ax.plot([5, 6, 7])
ax.legend(['First line', 'Second line'])
Parameters
----------
handles : sequence of `.Artist`, optional
A list of Artists (lines, patches) to be added to the legend.
Use this together with *labels*, if you need full control on what
is shown in the legend and the automatic mechanism described above
is not sufficient.
The length of handles and labels should be the same in this
case. If they are not, they are truncated to the smaller length.
labels : list of str, optional
A list of labels to show next to the artists.
Use this together with *handles*, if you need full control on what
is shown in the legend and the automatic mechanism described above
is not sufficient.
Returns
-------
`~matplotlib.legend.Legend`
Other Parameters
----------------
%(_legend_kw_doc)s
See Also
--------
.Figure.legend
Notes
-----
Some artists are not supported by this function. See
:doc:`/tutorials/intermediate/legend_guide` for details.
Examples
--------
.. plot:: gallery/text_labels_and_annotations/legend.py
"""
handles, labels, extra_args, kwargs = mlegend._parse_legend_args(
[self],
*args,
**kwargs)
if len(extra_args):
raise TypeError('legend only accepts two non-keyword arguments')
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
self.legend_._remove_method = self._remove_legend
return self.legend_
def _remove_legend(self, legend):
self.legend_ = None
def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs):
"""
Add a child inset Axes to this existing Axes.
Warnings
--------
This method is experimental as of 3.0, and the API may change.
Parameters
----------
bounds : [x0, y0, width, height]
Lower-left corner of inset Axes, and its width and height.
transform : `.Transform`
Defaults to `ax.transAxes`, i.e. the units of *rect* are in
Axes-relative coordinates.
zorder : number
Defaults to 5 (same as `.Axes.legend`). Adjust higher or lower
to change whether it is above or below data plotted on the
parent Axes.
**kwargs
Other keyword arguments are passed on to the child `.Axes`.
Returns
-------
ax
The created `~.axes.Axes` instance.
Examples
--------
This example makes two inset Axes, the first is in Axes-relative
coordinates, and the second in data-coordinates::
fig, ax = plt.subplots()
ax.plot(range(10))
axin1 = ax.inset_axes([0.8, 0.1, 0.15, 0.15])
axin2 = ax.inset_axes(
[5, 7, 2.3, 2.3], transform=ax.transData)
"""
if transform is None:
transform = self.transAxes
kwargs.setdefault('label', 'inset_axes')
# This puts the rectangle into figure-relative coordinates.
inset_locator = _TransformedBoundsLocator(bounds, transform)
bounds = inset_locator(self, None).bounds
inset_ax = Axes(self.figure, bounds, zorder=zorder, **kwargs)
# this locator lets the axes move if in data coordinates.
# it gets called in `ax.apply_aspect() (of all places)
inset_ax.set_axes_locator(inset_locator)
self.add_child_axes(inset_ax)
return inset_ax
@docstring.dedent_interpd
def indicate_inset(self, bounds, inset_ax=None, *, transform=None,
facecolor='none', edgecolor='0.5', alpha=0.5,
zorder=4.99, **kwargs):
"""
Add an inset indicator to the Axes. This is a rectangle on the plot
at the position indicated by *bounds* that optionally has lines that
connect the rectangle to an inset Axes (`.Axes.inset_axes`).
Warnings
--------
This method is experimental as of 3.0, and the API may change.
Parameters
----------
bounds : [x0, y0, width, height]
Lower-left corner of rectangle to be marked, and its width
and height.
inset_ax : `.Axes`
An optional inset Axes to draw connecting lines to. Two lines are
drawn connecting the indicator box to the inset Axes on corners
chosen so as to not overlap with the indicator box.
transform : `.Transform`
Transform for the rectangle coordinates. Defaults to
`ax.transAxes`, i.e. the units of *rect* are in Axes-relative
coordinates.
facecolor : color, default: 'none'
Facecolor of the rectangle.
edgecolor : color, default: '0.5'
Color of the rectangle and color of the connecting lines.
alpha : float, default: 0.5
Transparency of the rectangle and connector lines.
zorder : float, default: 4.99
Drawing order of the rectangle and connector lines. The default,
4.99, is just below the default level of inset Axes.
**kwargs
Other keyword arguments are passed on to the `.Rectangle` patch:
%(Rectangle:kwdoc)s
Returns
-------
rectangle_patch : `.patches.Rectangle`
The indicator frame.
connector_lines : 4-tuple of `.patches.ConnectionPatch`
The four connector lines connecting to (lower_left, upper_left,
lower_right upper_right) corners of *inset_ax*. Two lines are
set with visibility to *False*, but the user can set the
visibility to True if the automatic choice is not deemed correct.
"""
# to make the axes connectors work, we need to apply the aspect to
# the parent axes.
self.apply_aspect()
if transform is None:
transform = self.transData
kwargs.setdefault('label', '_indicate_inset')
x, y, width, height = bounds
rectangle_patch = mpatches.Rectangle(
(x, y), width, height,
facecolor=facecolor, edgecolor=edgecolor, alpha=alpha,
zorder=zorder, transform=transform, **kwargs)
self.add_patch(rectangle_patch)
connects = []
if inset_ax is not None:
# connect the inset_axes to the rectangle
for xy_inset_ax in [(0, 0), (0, 1), (1, 0), (1, 1)]:
# inset_ax positions are in axes coordinates
# The 0, 1 values define the four edges if the inset_ax
# lower_left, upper_left, lower_right upper_right.
ex, ey = xy_inset_ax
if self.xaxis.get_inverted():
ex = 1 - ex
if self.yaxis.get_inverted():
ey = 1 - ey
xy_data = x + ex * width, y + ey * height
p = mpatches.ConnectionPatch(
xyA=xy_inset_ax, coordsA=inset_ax.transAxes,
xyB=xy_data, coordsB=self.transData,
arrowstyle="-", zorder=zorder,
edgecolor=edgecolor, alpha=alpha)
connects.append(p)
self.add_patch(p)
# decide which two of the lines to keep visible....
pos = inset_ax.get_position()
bboxins = pos.transformed(self.figure.transSubfigure)
rectbbox = mtransforms.Bbox.from_bounds(
*bounds
).transformed(transform)
x0 = rectbbox.x0 < bboxins.x0
x1 = rectbbox.x1 < bboxins.x1
y0 = rectbbox.y0 < bboxins.y0
y1 = rectbbox.y1 < bboxins.y1
connects[0].set_visible(x0 ^ y0)
connects[1].set_visible(x0 == y1)
connects[2].set_visible(x1 == y0)
connects[3].set_visible(x1 ^ y1)
return rectangle_patch, tuple(connects) if connects else None
def indicate_inset_zoom(self, inset_ax, **kwargs):
"""
Add an inset indicator rectangle to the Axes based on the axis
limits for an *inset_ax* and draw connectors between *inset_ax*
and the rectangle.
Warnings
--------
This method is experimental as of 3.0, and the API may change.
Parameters
----------
inset_ax : `.Axes`
Inset Axes to draw connecting lines to. Two lines are
drawn connecting the indicator box to the inset Axes on corners
chosen so as to not overlap with the indicator box.
**kwargs
Other keyword arguments are passed on to `.Axes.indicate_inset`
Returns
-------
rectangle_patch : `.patches.Rectangle`
Rectangle artist.
connector_lines : 4-tuple of `.patches.ConnectionPatch`
Each of four connector lines coming from the rectangle drawn on
this axis, in the order lower left, upper left, lower right,
upper right.
Two are set with visibility to *False*, but the user can
set the visibility to *True* if the automatic choice is not deemed
correct.
"""
xlim = inset_ax.get_xlim()
ylim = inset_ax.get_ylim()
rect = (xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0])
return self.indicate_inset(rect, inset_ax, **kwargs)
@docstring.dedent_interpd
def secondary_xaxis(self, location, *, functions=None, **kwargs):
"""
Add a second x-axis to this Axes.
For example if we want to have a second scale for the data plotted on
the xaxis.
%(_secax_docstring)s
Examples
--------
The main axis shows frequency, and the secondary axis shows period.
.. plot::
fig, ax = plt.subplots()
ax.loglog(range(1, 360, 5), range(1, 360, 5))
ax.set_xlabel('frequency [Hz]')
def invert(x):
# 1/x with special treatment of x == 0
x = np.array(x).astype(float)
near_zero = np.isclose(x, 0)
x[near_zero] = np.inf
x[~near_zero] = 1 / x[~near_zero]
return x
# the inverse of 1/x is itself
secax = ax.secondary_xaxis('top', functions=(invert, invert))
secax.set_xlabel('Period [s]')
plt.show()
"""
if location in ['top', 'bottom'] or isinstance(location, Number):
secondary_ax = SecondaryAxis(self, 'x', location, functions,
**kwargs)
self.add_child_axes(secondary_ax)
return secondary_ax
else:
raise ValueError('secondary_xaxis location must be either '
'a float or "top"/"bottom"')
@docstring.dedent_interpd
def secondary_yaxis(self, location, *, functions=None, **kwargs):
"""
Add a second y-axis to this Axes.
For example if we want to have a second scale for the data plotted on
the yaxis.
%(_secax_docstring)s
Examples
--------
Add a secondary Axes that converts from radians to degrees
.. plot::
fig, ax = plt.subplots()
ax.plot(range(1, 360, 5), range(1, 360, 5))
ax.set_ylabel('degrees')
secax = ax.secondary_yaxis('right', functions=(np.deg2rad,
np.rad2deg))
secax.set_ylabel('radians')
"""
if location in ['left', 'right'] or isinstance(location, Number):
secondary_ax = SecondaryAxis(self, 'y', location,
functions, **kwargs)
self.add_child_axes(secondary_ax)
return secondary_ax
else:
raise ValueError('secondary_yaxis location must be either '
'a float or "left"/"right"')
@docstring.dedent_interpd
def text(self, x, y, s, fontdict=None, **kwargs):
"""
Add text to the Axes.
Add the text *s* to the Axes at location *x*, *y* in data coordinates.
Parameters
----------
x, y : float
The position to place the text. By default, this is in data
coordinates. The coordinate system can be changed using the
*transform* parameter.
s : str
The text.
fontdict : dict, default: None
A dictionary to override the default text properties. If fontdict
is None, the defaults are determined by `.rcParams`.
Returns
-------
`.Text`
The created `.Text` instance.
Other Parameters
----------------
**kwargs : `~matplotlib.text.Text` properties.
Other miscellaneous text parameters.
%(Text:kwdoc)s
Examples
--------
Individual keyword arguments can be used to override any given
parameter::
>>> text(x, y, s, fontsize=12)
The default transform specifies that text is in data coords,
alternatively, you can specify text in axis coords ((0, 0) is
lower-left and (1, 1) is upper-right). The example below places
text in the center of the Axes::
>>> text(0.5, 0.5, 'matplotlib', horizontalalignment='center',
... verticalalignment='center', transform=ax.transAxes)
You can put a rectangular box around the text instance (e.g., to
set a background color) by using the keyword *bbox*. *bbox* is
a dictionary of `~matplotlib.patches.Rectangle`
properties. For example::
>>> text(x, y, s, bbox=dict(facecolor='red', alpha=0.5))
"""
effective_kwargs = {
'verticalalignment': 'baseline',
'horizontalalignment': 'left',
'transform': self.transData,
'clip_on': False,
**(fontdict if fontdict is not None else {}),
**kwargs,
}
t = mtext.Text(x, y, text=s, **effective_kwargs)
t.set_clip_path(self.patch)
self._add_text(t)
return t
@docstring.dedent_interpd
def annotate(self, text, xy, *args, **kwargs):
a = mtext.Annotation(text, xy, *args, **kwargs)
a.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
a.set_clip_path(self.patch)
self._add_text(a)
return a
annotate.__doc__ = mtext.Annotation.__init__.__doc__
#### Lines and spans
@docstring.dedent_interpd
def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
"""
Add a horizontal line across the Axes.
Parameters
----------
y : float, default: 0
y position in data coordinates of the horizontal line.
xmin : float, default: 0
Should be between 0 and 1, 0 being the far left of the plot, 1 the
far right of the plot.
xmax : float, default: 1
Should be between 0 and 1, 0 being the far left of the plot, 1 the
far right of the plot.
Returns
-------
`~matplotlib.lines.Line2D`
Other Parameters
----------------
**kwargs
Valid keyword arguments are `.Line2D` properties, with the
exception of 'transform':
%(Line2D:kwdoc)s
See Also
--------
hlines : Add horizontal lines in data coordinates.
axhspan : Add a horizontal span (rectangle) across the axis.
axline : Add a line with an arbitrary slope.
Examples
--------
* draw a thick red hline at 'y' = 0 that spans the xrange::
>>> axhline(linewidth=4, color='r')
* draw a default hline at 'y' = 1 that spans the xrange::
>>> axhline(y=1)
* draw a default hline at 'y' = .5 that spans the middle half of
the xrange::
>>> axhline(y=.5, xmin=0.25, xmax=0.75)
"""
self._check_no_units([xmin, xmax], ['xmin', 'xmax'])
if "transform" in kwargs:
raise ValueError("'transform' is not allowed as a keyword "
"argument; axhline generates its own transform.")
ymin, ymax = self.get_ybound()
# Strip away the units for comparison with non-unitized bounds.
yy, = self._process_unit_info([("y", y)], kwargs)
scaley = (yy < ymin) or (yy > ymax)
trans = self.get_yaxis_transform(which='grid')
l = mlines.Line2D([xmin, xmax], [y, y], transform=trans, **kwargs)
self.add_line(l)
if scaley:
self._request_autoscale_view("y")
return l
@docstring.dedent_interpd
def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
"""
Add a vertical line across the Axes.
Parameters
----------
x : float, default: 0
x position in data coordinates of the vertical line.
ymin : float, default: 0
Should be between 0 and 1, 0 being the bottom of the plot, 1 the
top of the plot.
ymax : float, default: 1
Should be between 0 and 1, 0 being the bottom of the plot, 1 the
top of the plot.
Returns
-------
`~matplotlib.lines.Line2D`
Other Parameters
----------------
**kwargs
Valid keyword arguments are `.Line2D` properties, with the
exception of 'transform':
%(Line2D:kwdoc)s
See Also
--------
vlines : Add vertical lines in data coordinates.
axvspan : Add a vertical span (rectangle) across the axis.
axline : Add a line with an arbitrary slope.
Examples
--------
* draw a thick red vline at *x* = 0 that spans the yrange::
>>> axvline(linewidth=4, color='r')
* draw a default vline at *x* = 1 that spans the yrange::
>>> axvline(x=1)
* draw a default vline at *x* = .5 that spans the middle half of
the yrange::
>>> axvline(x=.5, ymin=0.25, ymax=0.75)
"""
self._check_no_units([ymin, ymax], ['ymin', 'ymax'])
if "transform" in kwargs:
raise ValueError("'transform' is not allowed as a keyword "
"argument; axvline generates its own transform.")
xmin, xmax = self.get_xbound()
# Strip away the units for comparison with non-unitized bounds.
xx, = self._process_unit_info([("x", x)], kwargs)
scalex = (xx < xmin) or (xx > xmax)
trans = self.get_xaxis_transform(which='grid')
l = mlines.Line2D([x, x], [ymin, ymax], transform=trans, **kwargs)
self.add_line(l)
if scalex:
self._request_autoscale_view("x")
return l
@staticmethod
def _check_no_units(vals, names):
# Helper method to check that vals are not unitized
for val, name in zip(vals, names):
if not munits._is_natively_supported(val):
raise ValueError(f"{name} must be a single scalar value, "
f"but got {val}")
@docstring.dedent_interpd
def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
"""
Add an infinitely long straight line.
The line can be defined either by two points *xy1* and *xy2*, or
by one point *xy1* and a *slope*.
This draws a straight line "on the screen", regardless of the x and y
scales, and is thus also suitable for drawing exponential decays in
semilog plots, power laws in loglog plots, etc. However, *slope*
should only be used with linear scales; It has no clear meaning for
all other scales, and thus the behavior is undefined. Please specify
the line using the points *xy1*, *xy2* for non-linear scales.
The *transform* keyword argument only applies to the points *xy1*,
*xy2*. The *slope* (if given) is always in data coordinates. This can
be used e.g. with ``ax.transAxes`` for drawing grid lines with a fixed
slope.
Parameters
----------
xy1, xy2 : (float, float)
Points for the line to pass through.
Either *xy2* or *slope* has to be given.
slope : float, optional
The slope of the line. Either *xy2* or *slope* has to be given.
Returns
-------
`.Line2D`
Other Parameters
----------------
**kwargs
Valid kwargs are `.Line2D` properties
%(Line2D:kwdoc)s
See Also
--------
axhline : for horizontal lines
axvline : for vertical lines
Examples
--------
Draw a thick red line passing through (0, 0) and (1, 1)::
>>> axline((0, 0), (1, 1), linewidth=4, color='r')
"""
if slope is not None and (self.get_xscale() != 'linear' or
self.get_yscale() != 'linear'):
raise TypeError("'slope' cannot be used with non-linear scales")
datalim = [xy1] if xy2 is None else [xy1, xy2]
if "transform" in kwargs:
# if a transform is passed (i.e. line points not in data space),
# data limits should not be adjusted.
datalim = []
line = mlines._AxLine(xy1, xy2, slope, **kwargs)
# Like add_line, but correctly handling data limits.
self._set_artist_props(line)
if line.get_clip_path() is None:
line.set_clip_path(self.patch)
if not line.get_label():
line.set_label(f"_child{len(self._children)}")
self._children.append(line)
line._remove_method = self._children.remove
self.update_datalim(datalim)
self._request_autoscale_view()
return line
@docstring.dedent_interpd
def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
"""
Add a horizontal span (rectangle) across the Axes.
The rectangle spans from *ymin* to *ymax* vertically, and, by default,
the whole x-axis horizontally. The x-span can be set using *xmin*
(default: 0) and *xmax* (default: 1) which are in axis units; e.g.
``xmin = 0.5`` always refers to the middle of the x-axis regardless of
the limits set by `~.Axes.set_xlim`.
Parameters
----------
ymin : float
Lower y-coordinate of the span, in data units.
ymax : float
Upper y-coordinate of the span, in data units.
xmin : float, default: 0
Lower x-coordinate of the span, in x-axis (0-1) units.
xmax : float, default: 1
Upper x-coordinate of the span, in x-axis (0-1) units.
Returns
-------
`~matplotlib.patches.Polygon`
Horizontal span (rectangle) from (xmin, ymin) to (xmax, ymax).
Other Parameters
----------------
**kwargs : `~matplotlib.patches.Polygon` properties
%(Polygon:kwdoc)s
See Also
--------
axvspan : Add a vertical span across the Axes.
"""
# Strip units away.
self._check_no_units([xmin, xmax], ['xmin', 'xmax'])
(ymin, ymax), = self._process_unit_info([("y", [ymin, ymax])], kwargs)
verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(self.get_yaxis_transform(which="grid"))
self.add_patch(p)
self._request_autoscale_view("y")
return p
@docstring.dedent_interpd
def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
"""
Add a vertical span (rectangle) across the Axes.
The rectangle spans from *xmin* to *xmax* horizontally, and, by
default, the whole y-axis vertically. The y-span can be set using
*ymin* (default: 0) and *ymax* (default: 1) which are in axis units;
e.g. ``ymin = 0.5`` always refers to the middle of the y-axis
regardless of the limits set by `~.Axes.set_ylim`.
Parameters
----------
xmin : float
Lower x-coordinate of the span, in data units.
xmax : float
Upper x-coordinate of the span, in data units.
ymin : float, default: 0
Lower y-coordinate of the span, in y-axis units (0-1).
ymax : float, default: 1
Upper y-coordinate of the span, in y-axis units (0-1).
Returns
-------
`~matplotlib.patches.Polygon`
Vertical span (rectangle) from (xmin, ymin) to (xmax, ymax).
Other Parameters
----------------
**kwargs : `~matplotlib.patches.Polygon` properties
%(Polygon:kwdoc)s
See Also
--------
axhspan : Add a horizontal span across the Axes.
Examples
--------
Draw a vertical, green, translucent rectangle from x = 1.25 to
x = 1.55 that spans the yrange of the Axes.
>>> axvspan(1.25, 1.55, facecolor='g', alpha=0.5)
"""
# Strip units away.
self._check_no_units([ymin, ymax], ['ymin', 'ymax'])
(xmin, xmax), = self._process_unit_info([("x", [xmin, xmax])], kwargs)
verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(self.get_xaxis_transform(which="grid"))
p.get_path()._interpolation_steps = 100
self.add_patch(p)
self._request_autoscale_view("x")
return p
@_preprocess_data(replace_names=["y", "xmin", "xmax", "colors"],
label_namer="y")
def hlines(self, y, xmin, xmax, colors=None, linestyles='solid',
label='', **kwargs):
"""
Plot horizontal lines at each *y* from *xmin* to *xmax*.
Parameters
----------
y : float or array-like
y-indexes where to plot the lines.
xmin, xmax : float or array-like
Respective beginning and end of each line. If scalars are
provided, all lines will have same length.
colors : list of colors, default: :rc:`lines.color`
linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, optional
label : str, default: ''
Returns
-------
`~matplotlib.collections.LineCollection`
Other Parameters
----------------
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs : `~matplotlib.collections.LineCollection` properties.
See Also
--------
vlines : vertical lines
axhline : horizontal line across the Axes
"""
# We do the conversion first since not all unitized data is uniform
xmin, xmax, y = self._process_unit_info(
[("x", xmin), ("x", xmax), ("y", y)], kwargs)
if not np.iterable(y):
y = [y]
if not np.iterable(xmin):
xmin = [xmin]
if not np.iterable(xmax):
xmax = [xmax]
# Create and combine masked_arrays from input
y, xmin, xmax = cbook._combine_masks(y, xmin, xmax)
y = np.ravel(y)
xmin = np.ravel(xmin)
xmax = np.ravel(xmax)
masked_verts = np.ma.empty((len(y), 2, 2))
masked_verts[:, 0, 0] = xmin
masked_verts[:, 0, 1] = y
masked_verts[:, 1, 0] = xmax
masked_verts[:, 1, 1] = y
lines = mcoll.LineCollection(masked_verts, colors=colors,
linestyles=linestyles, label=label)
self.add_collection(lines, autolim=False)
lines.update(kwargs)
if len(y) > 0:
minx = min(xmin.min(), xmax.min())
maxx = max(xmin.max(), xmax.max())
miny = y.min()
maxy = y.max()
corners = (minx, miny), (maxx, maxy)
self.update_datalim(corners)
self._request_autoscale_view()
return lines
@_preprocess_data(replace_names=["x", "ymin", "ymax", "colors"],
label_namer="x")
def vlines(self, x, ymin, ymax, colors=None, linestyles='solid',
label='', **kwargs):
"""
Plot vertical lines at each *x* from *ymin* to *ymax*.
Parameters
----------
x : float or array-like
x-indexes where to plot the lines.
ymin, ymax : float or array-like
Respective beginning and end of each line. If scalars are
provided, all lines will have same length.
colors : list of colors, default: :rc:`lines.color`
linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, optional
label : str, default: ''
Returns
-------
`~matplotlib.collections.LineCollection`
Other Parameters
----------------
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs : `~matplotlib.collections.LineCollection` properties.
See Also
--------
hlines : horizontal lines
axvline : vertical line across the Axes
"""
# We do the conversion first since not all unitized data is uniform
x, ymin, ymax = self._process_unit_info(
[("x", x), ("y", ymin), ("y", ymax)], kwargs)
if not np.iterable(x):
x = [x]
if not np.iterable(ymin):
ymin = [ymin]
if not np.iterable(ymax):
ymax = [ymax]
# Create and combine masked_arrays from input
x, ymin, ymax = cbook._combine_masks(x, ymin, ymax)
x = np.ravel(x)
ymin = np.ravel(ymin)
ymax = np.ravel(ymax)
masked_verts = np.ma.empty((len(x), 2, 2))
masked_verts[:, 0, 0] = x
masked_verts[:, 0, 1] = ymin
masked_verts[:, 1, 0] = x
masked_verts[:, 1, 1] = ymax
lines = mcoll.LineCollection(masked_verts, colors=colors,
linestyles=linestyles, label=label)
self.add_collection(lines, autolim=False)
lines.update(kwargs)
if len(x) > 0:
minx = x.min()
maxx = x.max()
miny = min(ymin.min(), ymax.min())
maxy = max(ymin.max(), ymax.max())
corners = (minx, miny), (maxx, maxy)
self.update_datalim(corners)
self._request_autoscale_view()
return lines
@_preprocess_data(replace_names=["positions", "lineoffsets",
"linelengths", "linewidths",
"colors", "linestyles"])
@docstring.dedent_interpd
def eventplot(self, positions, orientation='horizontal', lineoffsets=1,
linelengths=1, linewidths=None, colors=None,
linestyles='solid', **kwargs):
"""
Plot identical parallel lines at the given positions.
This type of plot is commonly used in neuroscience for representing
neural events, where it is usually called a spike raster, dot raster,
or raster plot.
However, it is useful in any situation where you wish to show the
timing or position of multiple sets of discrete events, such as the
arrival times of people to a business on each day of the month or the
date of hurricanes each year of the last century.
Parameters
----------
positions : array-like or list of array-like
A 1D array-like defines the positions of one sequence of events.
Multiple groups of events may be passed as a list of array-likes.
Each group can be styled independently by passing lists of values
to *lineoffsets*, *linelengths*, *linewidths*, *colors* and
*linestyles*.
Note that *positions* can be a 2D array, but in practice different
event groups usually have different counts so that one will use a
list of different-length arrays rather than a 2D array.
orientation : {'horizontal', 'vertical'}, default: 'horizontal'
The direction of the event sequence:
- 'horizontal': the events are arranged horizontally.
The indicator lines are vertical.
- 'vertical': the events are arranged vertically.
The indicator lines are horizontal.
lineoffsets : float or array-like, default: 1
The offset of the center of the lines from the origin, in the
direction orthogonal to *orientation*.
If *positions* is 2D, this can be a sequence with length matching
the length of *positions*.
linelengths : float or array-like, default: 1
The total height of the lines (i.e. the lines stretches from
``lineoffset - linelength/2`` to ``lineoffset + linelength/2``).
If *positions* is 2D, this can be a sequence with length matching
the length of *positions*.
linewidths : float or array-like, default: :rc:`lines.linewidth`
The line width(s) of the event lines, in points.
If *positions* is 2D, this can be a sequence with length matching
the length of *positions*.
colors : color or list of colors, default: :rc:`lines.color`
The color(s) of the event lines.
If *positions* is 2D, this can be a sequence with length matching
the length of *positions*.
linestyles : str or tuple or list of such values, default: 'solid'
Default is 'solid'. Valid strings are ['solid', 'dashed',
'dashdot', 'dotted', '-', '--', '-.', ':']. Dash tuples
should be of the form::
(offset, onoffseq),
where *onoffseq* is an even length tuple of on and off ink
in points.
If *positions* is 2D, this can be a sequence with length matching
the length of *positions*.
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs
Other keyword arguments are line collection properties. See
`.LineCollection` for a list of the valid properties.
Returns
-------
list of `.EventCollection`
The `.EventCollection` that were added.
Notes
-----
For *linelengths*, *linewidths*, *colors*, and *linestyles*, if only
a single value is given, that value is applied to all lines. If an
array-like is given, it must have the same length as *positions*, and
each value will be applied to the corresponding row of the array.
Examples
--------
.. plot:: gallery/lines_bars_and_markers/eventplot_demo.py
"""
lineoffsets, linelengths = self._process_unit_info(
[("y", lineoffsets), ("y", linelengths)], kwargs)
# fix positions, noting that it can be a list of lists:
if not np.iterable(positions):
positions = [positions]
elif any(np.iterable(position) for position in positions):
positions = [np.asanyarray(position) for position in positions]
else:
positions = [np.asanyarray(positions)]
if len(positions) == 0:
return []
poss = []
for position in positions:
poss += self._process_unit_info([("x", position)], kwargs)
positions = poss
# prevent 'singular' keys from **kwargs dict from overriding the effect
# of 'plural' keyword arguments (e.g. 'color' overriding 'colors')
colors = cbook._local_over_kwdict(colors, kwargs, 'color')
linewidths = cbook._local_over_kwdict(linewidths, kwargs, 'linewidth')
linestyles = cbook._local_over_kwdict(linestyles, kwargs, 'linestyle')
if not np.iterable(lineoffsets):
lineoffsets = [lineoffsets]
if not np.iterable(linelengths):
linelengths = [linelengths]
if not np.iterable(linewidths):
linewidths = [linewidths]
if not np.iterable(colors):
colors = [colors]
if hasattr(linestyles, 'lower') or not np.iterable(linestyles):
linestyles = [linestyles]
lineoffsets = np.asarray(lineoffsets)
linelengths = np.asarray(linelengths)
linewidths = np.asarray(linewidths)
if len(lineoffsets) == 0:
lineoffsets = [None]
if len(linelengths) == 0:
linelengths = [None]
if len(linewidths) == 0:
lineoffsets = [None]
if len(linewidths) == 0:
lineoffsets = [None]
if len(colors) == 0:
colors = [None]
try:
# Early conversion of the colors into RGBA values to take care
# of cases like colors='0.5' or colors='C1'. (Issue #8193)
colors = mcolors.to_rgba_array(colors)
except ValueError:
# Will fail if any element of *colors* is None. But as long
# as len(colors) == 1 or len(positions), the rest of the
# code should process *colors* properly.
pass
if len(lineoffsets) == 1 and len(positions) != 1:
lineoffsets = np.tile(lineoffsets, len(positions))
lineoffsets[0] = 0
lineoffsets = np.cumsum(lineoffsets)
if len(linelengths) == 1:
linelengths = np.tile(linelengths, len(positions))
if len(linewidths) == 1:
linewidths = np.tile(linewidths, len(positions))
if len(colors) == 1:
colors = list(colors)
colors = colors * len(positions)
if len(linestyles) == 1:
linestyles = [linestyles] * len(positions)
if len(lineoffsets) != len(positions):
raise ValueError('lineoffsets and positions are unequal sized '
'sequences')
if len(linelengths) != len(positions):
raise ValueError('linelengths and positions are unequal sized '
'sequences')
if len(linewidths) != len(positions):
raise ValueError('linewidths and positions are unequal sized '
'sequences')
if len(colors) != len(positions):
raise ValueError('colors and positions are unequal sized '
'sequences')
if len(linestyles) != len(positions):
raise ValueError('linestyles and positions are unequal sized '
'sequences')
colls = []
for position, lineoffset, linelength, linewidth, color, linestyle in \
zip(positions, lineoffsets, linelengths, linewidths,
colors, linestyles):
coll = mcoll.EventCollection(position,
orientation=orientation,
lineoffset=lineoffset,
linelength=linelength,
linewidth=linewidth,
color=color,
linestyle=linestyle)
self.add_collection(coll, autolim=False)
coll.update(kwargs)
colls.append(coll)
if len(positions) > 0:
# try to get min/max
min_max = [(np.min(_p), np.max(_p)) for _p in positions
if len(_p) > 0]
# if we have any non-empty positions, try to autoscale
if len(min_max) > 0:
mins, maxes = zip(*min_max)
minpos = np.min(mins)
maxpos = np.max(maxes)
minline = (lineoffsets - linelengths).min()
maxline = (lineoffsets + linelengths).max()
if orientation == "vertical":
corners = (minline, minpos), (maxline, maxpos)
else: # "horizontal"
corners = (minpos, minline), (maxpos, maxline)
self.update_datalim(corners)
self._request_autoscale_view()
return colls
#### Basic plotting
# Uses a custom implementation of data-kwarg handling in
# _process_plot_var_args.
@docstring.dedent_interpd
def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs):
"""
Plot y versus x as lines and/or markers.
Call signatures::
plot([x], y, [fmt], *, data=None, **kwargs)
plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
The coordinates of the points or line nodes are given by *x*, *y*.
The optional parameter *fmt* is a convenient way for defining basic
formatting like color, marker and linestyle. It's a shortcut string
notation described in the *Notes* section below.
>>> plot(x, y) # plot x and y using default line style and color
>>> plot(x, y, 'bo') # plot x and y using blue circle markers
>>> plot(y) # plot y using x as index array 0..N-1
>>> plot(y, 'r+') # ditto, but with red plusses
You can use `.Line2D` properties as keyword arguments for more
control on the appearance. Line properties and *fmt* can be mixed.
The following two calls yield identical results:
>>> plot(x, y, 'go--', linewidth=2, markersize=12)
>>> plot(x, y, color='green', marker='o', linestyle='dashed',
... linewidth=2, markersize=12)
When conflicting with *fmt*, keyword arguments take precedence.
**Plotting labelled data**
There's a convenient way for plotting objects with labelled data (i.e.
data that can be accessed by index ``obj['y']``). Instead of giving
the data in *x* and *y*, you can provide the object in the *data*
parameter and just give the labels for *x* and *y*::
>>> plot('xlabel', 'ylabel', data=obj)
All indexable objects are supported. This could e.g. be a `dict`, a
`pandas.DataFrame` or a structured numpy array.
**Plotting multiple sets of data**
There are various ways to plot multiple sets of data.
- The most straight forward way is just to call `plot` multiple times.
Example:
>>> plot(x1, y1, 'bo')
>>> plot(x2, y2, 'go')
- If *x* and/or *y* are 2D arrays a separate data set will be drawn
for every column. If both *x* and *y* are 2D, they must have the
same shape. If only one of them is 2D with shape (N, m) the other
must have length N and will be used for every data set m.
Example:
>>> x = [1, 2, 3]
>>> y = np.array([[1, 2], [3, 4], [5, 6]])
>>> plot(x, y)
is equivalent to:
>>> for col in range(y.shape[1]):
... plot(x, y[:, col])
- The third way is to specify multiple sets of *[x]*, *y*, *[fmt]*
groups::
>>> plot(x1, y1, 'g^', x2, y2, 'g-')
In this case, any additional keyword argument applies to all
datasets. Also this syntax cannot be combined with the *data*
parameter.
By default, each line is assigned a different style specified by a
'style cycle'. The *fmt* and line property parameters are only
necessary if you want explicit deviations from these defaults.
Alternatively, you can also change the style cycle using
:rc:`axes.prop_cycle`.
Parameters
----------
x, y : array-like or scalar
The horizontal / vertical coordinates of the data points.
*x* values are optional and default to ``range(len(y))``.
Commonly, these parameters are 1D arrays.
They can also be scalars, or two-dimensional (in that case, the
columns represent separate data sets).
These arguments cannot be passed as keywords.
fmt : str, optional
A format string, e.g. 'ro' for red circles. See the *Notes*
section for a full description of the format strings.
Format strings are just an abbreviation for quickly setting
basic line properties. All of these and more can also be
controlled by keyword arguments.
This argument cannot be passed as keyword.
data : indexable object, optional
An object with labelled data. If given, provide the label names to
plot in *x* and *y*.
.. note::
Technically there's a slight ambiguity in calls where the
second label is a valid *fmt*. ``plot('n', 'o', data=obj)``
could be ``plt(x, y)`` or ``plt(y, fmt)``. In such cases,
the former interpretation is chosen, but a warning is issued.
You may suppress the warning by adding an empty format string
``plot('n', 'o', '', data=obj)``.
Returns
-------
list of `.Line2D`
A list of lines representing the plotted data.
Other Parameters
----------------
scalex, scaley : bool, default: True
These parameters determine if the view limits are adapted to the
data limits. The values are passed on to `autoscale_view`.
**kwargs : `.Line2D` properties, optional
*kwargs* are used to specify properties like a line label (for
auto legends), linewidth, antialiasing, marker face color.
Example::
>>> plot([1, 2, 3], [1, 2, 3], 'go-', label='line 1', linewidth=2)
>>> plot([1, 2, 3], [1, 4, 9], 'rs', label='line 2')
If you specify multiple lines with one plot call, the kwargs apply
to all those lines. In case the label object is iterable, each
element is used as labels for each set of data.
Here is a list of available `.Line2D` properties:
%(Line2D:kwdoc)s
See Also
--------
scatter : XY scatter plot with markers of varying size and/or color (
sometimes also called bubble chart).
Notes
-----
**Format Strings**
A format string consists of a part for color, marker and line::
fmt = '[marker][line][color]'
Each of them is optional. If not provided, the value from the style
cycle is used. Exception: If ``line`` is given, but no ``marker``,
the data will be a line without markers.
Other combinations such as ``[color][marker][line]`` are also
supported, but note that their parsing may be ambiguous.
**Markers**
============= ===============================
character description
============= ===============================
``'.'`` point marker
``','`` pixel marker
``'o'`` circle marker
``'v'`` triangle_down marker
``'^'`` triangle_up marker
``'<'`` triangle_left marker
``'>'`` triangle_right marker
``'1'`` tri_down marker
``'2'`` tri_up marker
``'3'`` tri_left marker
``'4'`` tri_right marker
``'8'`` octagon marker
``'s'`` square marker
``'p'`` pentagon marker
``'P'`` plus (filled) marker
``'*'`` star marker
``'h'`` hexagon1 marker
``'H'`` hexagon2 marker
``'+'`` plus marker
``'x'`` x marker
``'X'`` x (filled) marker
``'D'`` diamond marker
``'d'`` thin_diamond marker
``'|'`` vline marker
``'_'`` hline marker
============= ===============================
**Line Styles**
============= ===============================
character description
============= ===============================
``'-'`` solid line style
``'--'`` dashed line style
``'-.'`` dash-dot line style
``':'`` dotted line style
============= ===============================
Example format strings::
'b' # blue markers with default shape
'or' # red circles
'-g' # green solid line
'--' # dashed line with default color
'^k:' # black triangle_up markers connected by a dotted line
**Colors**
The supported color abbreviations are the single letter codes
============= ===============================
character color
============= ===============================
``'b'`` blue
``'g'`` green
``'r'`` red
``'c'`` cyan
``'m'`` magenta
``'y'`` yellow
``'k'`` black
``'w'`` white
============= ===============================
and the ``'CN'`` colors that index into the default property cycle.
If the color is the only part of the format string, you can
additionally use any `matplotlib.colors` spec, e.g. full names
(``'green'``) or hex strings (``'#008000'``).
"""
kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
lines = [*self._get_lines(*args, data=data, **kwargs)]
for line in lines:
self.add_line(line)
if scalex:
self._request_autoscale_view("x")
if scaley:
self._request_autoscale_view("y")
return lines
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
@docstring.dedent_interpd
def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False,
**kwargs):
"""
Plot coercing the axis to treat floats as dates.
.. admonition:: Discouraged
This method exists for historic reasons and will be deprecated in
the future.
- ``datetime``-like data should directly be plotted using
`~.Axes.plot`.
- If you need to plot plain numeric data as :ref:`date-format` or
need to set a timezone, call ``ax.xaxis.axis_date`` /
``ax.yaxis.axis_date`` before `~.Axes.plot`. See
`.Axis.axis_date`.
Similar to `.plot`, this plots *y* vs. *x* as lines or markers.
However, the axis labels are formatted as dates depending on *xdate*
and *ydate*. Note that `.plot` will work with `datetime` and
`numpy.datetime64` objects without resorting to this method.
Parameters
----------
x, y : array-like
The coordinates of the data points. If *xdate* or *ydate* is
*True*, the respective values *x* or *y* are interpreted as
:ref:`Matplotlib dates <date-format>`.
fmt : str, optional
The plot format string. For details, see the corresponding
parameter in `.plot`.
tz : timezone string or `datetime.tzinfo`, default: :rc:`timezone`
The time zone to use in labeling dates.
xdate : bool, default: True
If *True*, the *x*-axis will be interpreted as Matplotlib dates.
ydate : bool, default: False
If *True*, the *y*-axis will be interpreted as Matplotlib dates.
Returns
-------
list of `.Line2D`
Objects representing the plotted data.
Other Parameters
----------------
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs
Keyword arguments control the `.Line2D` properties:
%(Line2D:kwdoc)s
See Also
--------
matplotlib.dates : Helper functions on dates.
matplotlib.dates.date2num : Convert dates to num.
matplotlib.dates.num2date : Convert num to dates.
matplotlib.dates.drange : Create an equally spaced sequence of dates.
Notes
-----
If you are using custom date tickers and formatters, it may be
necessary to set the formatters/locators after the call to
`.plot_date`. `.plot_date` will set the default tick locator to
`.AutoDateLocator` (if the tick locator is not already set to a
`.DateLocator` instance) and the default tick formatter to
`.AutoDateFormatter` (if the tick formatter is not already set to a
`.DateFormatter` instance).
"""
if xdate:
self.xaxis_date(tz)
if ydate:
self.yaxis_date(tz)
return self.plot(x, y, fmt, **kwargs)
# @_preprocess_data() # let 'plot' do the unpacking..
@docstring.dedent_interpd
def loglog(self, *args, **kwargs):
"""
Make a plot with log scaling on both the x and y axis.
Call signatures::
loglog([x], y, [fmt], data=None, **kwargs)
loglog([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
This is just a thin wrapper around `.plot` which additionally changes
both the x-axis and the y-axis to log scaling. All of the concepts and
parameters of plot can be used here as well.
The additional parameters *base*, *subs* and *nonpositive* control the
x/y-axis properties. They are just forwarded to `.Axes.set_xscale` and
`.Axes.set_yscale`. To use different properties on the x-axis and the
y-axis, use e.g.
``ax.set_xscale("log", base=10); ax.set_yscale("log", base=2)``.
Parameters
----------
base : float, default: 10
Base of the logarithm.
subs : sequence, optional
The location of the minor ticks. If *None*, reasonable locations
are automatically chosen depending on the number of decades in the
plot. See `.Axes.set_xscale`/`.Axes.set_yscale` for details.
nonpositive : {'mask', 'clip'}, default: 'mask'
Non-positive values can be masked as invalid, or clipped to a very
small positive number.
**kwargs
All parameters supported by `.plot`.
Returns
-------
list of `.Line2D`
Objects representing the plotted data.
"""
dx = {k: v for k, v in kwargs.items()
if k in ['base', 'subs', 'nonpositive',
'basex', 'subsx', 'nonposx']}
self.set_xscale('log', **dx)
dy = {k: v for k, v in kwargs.items()
if k in ['base', 'subs', 'nonpositive',
'basey', 'subsy', 'nonposy']}
self.set_yscale('log', **dy)
return self.plot(
*args, **{k: v for k, v in kwargs.items() if k not in {*dx, *dy}})
# @_preprocess_data() # let 'plot' do the unpacking..
@docstring.dedent_interpd
def semilogx(self, *args, **kwargs):
"""
Make a plot with log scaling on the x axis.
Call signatures::
semilogx([x], y, [fmt], data=None, **kwargs)
semilogx([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
This is just a thin wrapper around `.plot` which additionally changes
the x-axis to log scaling. All of the concepts and parameters of plot
can be used here as well.
The additional parameters *base*, *subs*, and *nonpositive* control the
x-axis properties. They are just forwarded to `.Axes.set_xscale`.
Parameters
----------
base : float, default: 10
Base of the x logarithm.
subs : array-like, optional
The location of the minor xticks. If *None*, reasonable locations
are automatically chosen depending on the number of decades in the
plot. See `.Axes.set_xscale` for details.
nonpositive : {'mask', 'clip'}, default: 'mask'
Non-positive values in x can be masked as invalid, or clipped to a
very small positive number.
**kwargs
All parameters supported by `.plot`.
Returns
-------
list of `.Line2D`
Objects representing the plotted data.
"""
d = {k: v for k, v in kwargs.items()
if k in ['base', 'subs', 'nonpositive',
'basex', 'subsx', 'nonposx']}
self.set_xscale('log', **d)
return self.plot(
*args, **{k: v for k, v in kwargs.items() if k not in d})
# @_preprocess_data() # let 'plot' do the unpacking..
@docstring.dedent_interpd
def semilogy(self, *args, **kwargs):
"""
Make a plot with log scaling on the y axis.
Call signatures::
semilogy([x], y, [fmt], data=None, **kwargs)
semilogy([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
This is just a thin wrapper around `.plot` which additionally changes
the y-axis to log scaling. All of the concepts and parameters of plot
can be used here as well.
The additional parameters *base*, *subs*, and *nonpositive* control the
y-axis properties. They are just forwarded to `.Axes.set_yscale`.
Parameters
----------
base : float, default: 10
Base of the y logarithm.
subs : array-like, optional
The location of the minor yticks. If *None*, reasonable locations
are automatically chosen depending on the number of decades in the
plot. See `.Axes.set_yscale` for details.
nonpositive : {'mask', 'clip'}, default: 'mask'
Non-positive values in y can be masked as invalid, or clipped to a
very small positive number.
**kwargs
All parameters supported by `.plot`.
Returns
-------
list of `.Line2D`
Objects representing the plotted data.
"""
d = {k: v for k, v in kwargs.items()
if k in ['base', 'subs', 'nonpositive',
'basey', 'subsy', 'nonposy']}
self.set_yscale('log', **d)
return self.plot(
*args, **{k: v for k, v in kwargs.items() if k not in d})
@_preprocess_data(replace_names=["x"], label_namer="x")
def acorr(self, x, **kwargs):
"""
Plot the autocorrelation of *x*.
Parameters
----------
x : array-like
detrend : callable, default: `.mlab.detrend_none` (no detrending)
A detrending function applied to *x*. It must have the
signature ::
detrend(x: np.ndarray) -> np.ndarray
normed : bool, default: True
If ``True``, input vectors are normalised to unit length.
usevlines : bool, default: True
Determines the plot style.
If ``True``, vertical lines are plotted from 0 to the acorr value
using `.Axes.vlines`. Additionally, a horizontal line is plotted
at y=0 using `.Axes.axhline`.
If ``False``, markers are plotted at the acorr values using
`.Axes.plot`.
maxlags : int, default: 10
Number of lags to show. If ``None``, will return all
``2 * len(x) - 1`` lags.
Returns
-------
lags : array (length ``2*maxlags+1``)
The lag vector.
c : array (length ``2*maxlags+1``)
The auto correlation vector.
line : `.LineCollection` or `.Line2D`
`.Artist` added to the Axes of the correlation:
- `.LineCollection` if *usevlines* is True.
- `.Line2D` if *usevlines* is False.
b : `.Line2D` or None
Horizontal line at 0 if *usevlines* is True
None *usevlines* is False.
Other Parameters
----------------
linestyle : `.Line2D` property, optional
The linestyle for plotting the data points.
Only used if *usevlines* is ``False``.
marker : str, default: 'o'
The marker for plotting the data points.
Only used if *usevlines* is ``False``.
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs
Additional parameters are passed to `.Axes.vlines` and
`.Axes.axhline` if *usevlines* is ``True``; otherwise they are
passed to `.Axes.plot`.
Notes
-----
The cross correlation is performed with `numpy.correlate` with
``mode = "full"``.
"""
return self.xcorr(x, x, **kwargs)
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none,
usevlines=True, maxlags=10, **kwargs):
r"""
Plot the cross correlation between *x* and *y*.
The correlation with lag k is defined as
:math:`\sum_n x[n+k] \cdot y^*[n]`, where :math:`y^*` is the complex
conjugate of :math:`y`.
Parameters
----------
x, y : array-like of length n
detrend : callable, default: `.mlab.detrend_none` (no detrending)
A detrending function applied to *x* and *y*. It must have the
signature ::
detrend(x: np.ndarray) -> np.ndarray
normed : bool, default: True
If ``True``, input vectors are normalised to unit length.
usevlines : bool, default: True
Determines the plot style.
If ``True``, vertical lines are plotted from 0 to the xcorr value
using `.Axes.vlines`. Additionally, a horizontal line is plotted
at y=0 using `.Axes.axhline`.
If ``False``, markers are plotted at the xcorr values using
`.Axes.plot`.
maxlags : int, default: 10
Number of lags to show. If None, will return all ``2 * len(x) - 1``
lags.
Returns
-------
lags : array (length ``2*maxlags+1``)
The lag vector.
c : array (length ``2*maxlags+1``)
The auto correlation vector.
line : `.LineCollection` or `.Line2D`
`.Artist` added to the Axes of the correlation:
- `.LineCollection` if *usevlines* is True.
- `.Line2D` if *usevlines* is False.
b : `.Line2D` or None
Horizontal line at 0 if *usevlines* is True
None *usevlines* is False.
Other Parameters
----------------
linestyle : `.Line2D` property, optional
The linestyle for plotting the data points.
Only used if *usevlines* is ``False``.
marker : str, default: 'o'
The marker for plotting the data points.
Only used if *usevlines* is ``False``.
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs
Additional parameters are passed to `.Axes.vlines` and
`.Axes.axhline` if *usevlines* is ``True``; otherwise they are
passed to `.Axes.plot`.
Notes
-----
The cross correlation is performed with `numpy.correlate` with
``mode = "full"``.
"""
Nx = len(x)
if Nx != len(y):
raise ValueError('x and y must be equal length')
x = detrend(np.asarray(x))
y = detrend(np.asarray(y))
correls = np.correlate(x, y, mode="full")
if normed:
correls /= np.sqrt(np.dot(x, x) * np.dot(y, y))
if maxlags is None:
maxlags = Nx - 1
if maxlags >= Nx or maxlags < 1:
raise ValueError('maxlags must be None or strictly '
'positive < %d' % Nx)
lags = np.arange(-maxlags, maxlags + 1)
correls = correls[Nx - 1 - maxlags:Nx + maxlags]
if usevlines:
a = self.vlines(lags, [0], correls, **kwargs)
# Make label empty so only vertical lines get a legend entry
kwargs.pop('label', '')
b = self.axhline(**kwargs)
else:
kwargs.setdefault('marker', 'o')
kwargs.setdefault('linestyle', 'None')
a, = self.plot(lags, correls, **kwargs)
b = None
return lags, correls, a, b
#### Specialized plotting
# @_preprocess_data() # let 'plot' do the unpacking..
def step(self, x, y, *args, where='pre', data=None, **kwargs):
"""
Make a step plot.
Call signatures::
step(x, y, [fmt], *, data=None, where='pre', **kwargs)
step(x, y, [fmt], x2, y2, [fmt2], ..., *, where='pre', **kwargs)
This is just a thin wrapper around `.plot` which changes some
formatting options. Most of the concepts and parameters of plot can be
used here as well.
.. note::
This method uses a standard plot with a step drawstyle: The *x*
values are the reference positions and steps extend left/right/both
directions depending on *where*.
For the common case where you know the values and edges of the
steps, use `~.Axes.stairs` instead.
Parameters
----------
x : array-like
1D sequence of x positions. It is assumed, but not checked, that
it is uniformly increasing.
y : array-like
1D sequence of y levels.
fmt : str, optional
A format string, e.g. 'g' for a green line. See `.plot` for a more
detailed description.
Note: While full format strings are accepted, it is recommended to
only specify the color. Line styles are currently ignored (use
the keyword argument *linestyle* instead). Markers are accepted
and plotted on the given positions, however, this is a rarely
needed feature for step plots.
where : {'pre', 'post', 'mid'}, default: 'pre'
Define where the steps should be placed:
- 'pre': The y value is continued constantly to the left from
every *x* position, i.e. the interval ``(x[i-1], x[i]]`` has the
value ``y[i]``.
- 'post': The y value is continued constantly to the right from
every *x* position, i.e. the interval ``[x[i], x[i+1])`` has the
value ``y[i]``.
- 'mid': Steps occur half-way between the *x* positions.
data : indexable object, optional
An object with labelled data. If given, provide the label names to
plot in *x* and *y*.
**kwargs
Additional parameters are the same as those for `.plot`.
Returns
-------
list of `.Line2D`
Objects representing the plotted data.
"""
_api.check_in_list(('pre', 'post', 'mid'), where=where)
kwargs['drawstyle'] = 'steps-' + where
return self.plot(x, y, *args, data=data, **kwargs)
@staticmethod
def _convert_dx(dx, x0, xconv, convert):
"""
Small helper to do logic of width conversion flexibly.
*dx* and *x0* have units, but *xconv* has already been converted
to unitless (and is an ndarray). This allows the *dx* to have units
that are different from *x0*, but are still accepted by the
``__add__`` operator of *x0*.
"""
# x should be an array...
assert type(xconv) is np.ndarray
if xconv.size == 0:
# xconv has already been converted, but maybe empty...
return convert(dx)
try:
# attempt to add the width to x0; this works for
# datetime+timedelta, for instance
# only use the first element of x and x0. This saves
# having to be sure addition works across the whole
# vector. This is particularly an issue if
# x0 and dx are lists so x0 + dx just concatenates the lists.
# We can't just cast x0 and dx to numpy arrays because that
# removes the units from unit packages like `pint` that
# wrap numpy arrays.
try:
x0 = cbook.safe_first_element(x0)
except (TypeError, IndexError, KeyError):
pass
try:
x = cbook.safe_first_element(xconv)
except (TypeError, IndexError, KeyError):
x = xconv
delist = False
if not np.iterable(dx):
dx = [dx]
delist = True
dx = [convert(x0 + ddx) - x for ddx in dx]
if delist:
dx = dx[0]
except (ValueError, TypeError, AttributeError):
# if the above fails (for any reason) just fallback to what
# we do by default and convert dx by itself.
dx = convert(dx)
return dx
@_preprocess_data()
@docstring.dedent_interpd
def bar(self, x, height, width=0.8, bottom=None, *, align="center",
**kwargs):
r"""
Make a bar plot.
The bars are positioned at *x* with the given *align*\ment. Their
dimensions are given by *height* and *width*. The vertical baseline
is *bottom* (default 0).
Many parameters can take either a single value applying to all bars
or a sequence of values, one for each bar.
Parameters
----------
x : float or array-like
The x coordinates of the bars. See also *align* for the
alignment of the bars to the coordinates.
height : float or array-like
The height(s) of the bars.
width : float or array-like, default: 0.8
The width(s) of the bars.
bottom : float or array-like, default: 0
The y coordinate(s) of the bars bases.
align : {'center', 'edge'}, default: 'center'
Alignment of the bars to the *x* coordinates:
- 'center': Center the base on the *x* positions.
- 'edge': Align the left edges of the bars with the *x* positions.
To align the bars on the right edge pass a negative *width* and
``align='edge'``.
Returns
-------
`.BarContainer`
Container with all the bars and optionally errorbars.
Other Parameters
----------------
color : color or list of color, optional
The colors of the bar faces.
edgecolor : color or list of color, optional
The colors of the bar edges.
linewidth : float or array-like, optional
Width of the bar edge(s). If 0, don't draw edges.
tick_label : str or list of str, optional
The tick labels of the bars.
Default: None (Use default numeric labels.)
xerr, yerr : float or array-like of shape(N,) or shape(2, N), optional
If not *None*, add horizontal / vertical errorbars to the bar tips.
The values are +/- sizes relative to the data:
- scalar: symmetric +/- values for all bars
- shape(N,): symmetric +/- values for each bar
- shape(2, N): Separate - and + values for each bar. First row
contains the lower errors, the second row contains the upper
errors.
- *None*: No errorbar. (Default)
See :doc:`/gallery/statistics/errorbar_features`
for an example on the usage of ``xerr`` and ``yerr``.
ecolor : color or list of color, default: 'black'
The line color of the errorbars.
capsize : float, default: :rc:`errorbar.capsize`
The length of the error bar caps in points.
error_kw : dict, optional
Dictionary of kwargs to be passed to the `~.Axes.errorbar`
method. Values of *ecolor* or *capsize* defined here take
precedence over the independent kwargs.
log : bool, default: False
If *True*, set the y-axis to be log scale.
data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER
**kwargs : `.Rectangle` properties
%(Rectangle:kwdoc)s
See Also
--------
barh : Plot a horizontal bar plot.
Notes
-----
Stacked bars can be achieved by passing individual *bottom* values per
bar. See :doc:`/gallery/lines_bars_and_markers/bar_stacked`.
"""
kwargs = cbook.normalize_kwargs(kwargs, mpatches.Patch)
color = kwargs.pop('color', None)
if color is None:
color = self._get_patches_for_fill.get_next_color()
edgecolor = kwargs.pop('edgecolor', None)
linewidth = kwargs.pop('linewidth', None)
hatch = kwargs.pop('hatch', None)
# Because xerr and yerr will be passed to errorbar, most dimension
# checking and processing will be left to the errorbar method.
xerr = kwargs.pop('xerr', None)
yerr = kwargs.pop('yerr', None)
error_kw = kwargs.pop('error_kw', {})
ezorder = error_kw.pop('zorder', None)
if ezorder is None:
ezorder = kwargs.get('zorder', None)
if ezorder is not None:
# If using the bar zorder, increment slightly to make sure
# errorbars are drawn on top of bars
ezorder += 0.01
error_kw.setdefault('zorder', ezorder)
ecolor = kwargs.pop('ecolor', 'k')
capsize = kwargs.pop('capsize', rcParams["errorbar.capsize"])
error_kw.setdefault('ecolor', ecolor)
error_kw.setdefault('capsize', capsize)
# The keyword argument *orientation* is used by barh() to defer all
# logic and drawing to bar(). It is considered internal and is
# intentionally not mentioned in the docstring.
orientation = kwargs.pop('orientation', 'vertical')
_api.check_in_list(['vertical', 'horizontal'], orientation=orientation)
log = kwargs.pop('log', False)
label = kwargs.pop('label', '')
tick_labels = kwargs.pop('tick_label', None)
y = bottom # Matches barh call signature.
if orientation == 'vertical':
if y is None:
y = 0
elif orientation == 'horizontal':
if x is None:
x = 0
if orientation == 'vertical':
self._process_unit_info(
[("x", x), ("y", height)], kwargs, convert=False)
if log:
self.set_yscale('log', nonpositive='clip')
elif orientation == 'horizontal':
self._process_unit_info(
[("x", width), ("y", y)], kwargs, convert=False)
if log:
self.set_xscale('log', nonpositive='clip')
# lets do some conversions now since some types cannot be
# subtracted uniformly
if self.xaxis is not None:
x0 = x
x = np.asarray(self.convert_xunits(x))
width = self._convert_dx(width, x0, x, self.convert_xunits)
if xerr is not None:
xerr = self._convert_dx(xerr, x0, x, self.convert_xunits)
if self.yaxis is not None:
y0 = y
y = np.asarray(self.convert_yunits(y))
height = self._convert_dx(height, y0, y, self.convert_yunits)
if yerr is not None:
yerr = self._convert_dx(yerr, y0, y, self.convert_yunits)
x, height, width, y, linewidth, hatch = np.broadcast_arrays(
# Make args iterable too.
np.atleast_1d(x), height, width, y, linewidth, hatch)
# Now that units have been converted, set the tick locations.
if orientation == 'vertical':
tick_label_axis = self.xaxis
tick_label_position = x
elif orientation == 'horizontal':
tick_label_axis = self.yaxis
tick_label_position = y
linewidth = itertools.cycle(np.atleast_1d(linewidth))
hatch = itertools.cycle(np.atleast_1d(hatch))
color = itertools.chain(itertools.cycle(mcolors.to_rgba_array(color)),
# Fallback if color == "none".
itertools.repeat('none'))
if edgecolor is None:
edgecolor = itertools.repeat(None)
else:
edgecolor = itertools.chain(
itertools.cycle(mcolors.to_rgba_array(edgecolor)),
# Fallback if edgecolor == "none".
itertools.repeat('none'))
# We will now resolve the alignment and really have
# left, bottom, width, height vectors
_api.check_in_list(['center', 'edge'], align=align)
if align == 'center':
if orientation == 'vertical':
try:
left = x - width / 2
except TypeError as e:
raise TypeError(f'the dtypes of parameters x ({x.dtype}) '
f'and width ({width.dtype}) '
f'are incompatible') from e
bottom = y
elif orientation == 'horizontal':
try:
bottom = y - height / 2
except TypeError as e:
raise TypeError(f'the dtypes of parameters y ({y.dtype}) '
f'and height ({height.dtype}) '
f'are incompatible') from e
left = x
elif align == 'edge':
left = x
bottom = y
patches = []
args = zip(left, bottom, width, height, color, edgecolor, linewidth,
hatch)
for l, b, w, h, c, e, lw, htch in args:
r = mpatches.Rectangle(
xy=(l, b), width=w, height=h,
facecolor=c,
edgecolor=e,
linewidth=lw,
label='_nolegend_',
hatch=htch,
)
r.update(kwargs)
r.get_path()._interpolation_steps = 100
if orientation == 'vertical':
r.sticky_edges.y.append(b)
elif orientation == 'horizontal':
r.sticky_edges.x.append(l)
self.add_patch(r)
patches.append(r)
if xerr is not None or yerr is not None:
if orientation == 'vertical':
# using list comps rather than arrays to preserve unit info
ex = [l + 0.5 * w for l, w in zip(left, width)]
ey = [b + h for b, h in zip(bottom, height)]
elif orientation == 'horizontal':
# using list comps rather than arrays to preserve unit info
ex = [l + w for l, w in zip(left, width)]
ey = [b + 0.5 * h for b, h in zip(bottom, height)]
error_kw.setdefault("label", '_nolegend_')
errorbar = self.errorbar(ex, ey,
yerr=yerr, xerr=xerr,
fmt='none', **error_kw)
else:
errorbar = None
self._request_autoscale_view()
if orientation == 'vertical':
datavalues = height
elif orientation == 'horizontal':
datavalues = width
bar_container = BarContainer(patches, errorbar, datavalues=datavalues,
orientation=orientation, label=label)
self.add_container(bar_container)
if tick_labels is not None:
tick_labels = np.broadcast_to(tick_labels, len(patches))
tick_label_axis.set_ticks(tick_label_position)
tick_label_axis.set_ticklabels(tick_labels)
return bar_container
@docstring.dedent_interpd
def barh(self, y, width, height=0.8, left=None, *, align="center",
**kwargs):
r"""
Make a horizontal bar plot.
The bars are positioned at *y* with the given *align*\ment. Their
dimensions are given by *width* and *height*. The horizontal baseline
is *left* (default 0).
Many parameters can take either a single value applying to all bars
or a sequence of values, one for each bar.
Parameters
----------
y : float or array-like
The y coordinates of the bars. See also *align* for the
alignment of the bars to the coordinates.
width : float or array-like
The width(s) of the bars.
height : float or array-like, default: 0.8
The heights of the bars.
left : float or array-like, default: 0
The x coordinates of the left sides of the bars.
align : {'center', 'edge'}, default: 'center'
Alignment of the base to the *y* coordinates*:
- 'center': Center the bars on the *y* positions.
- 'edge': Align the bottom edges of the bars with the *y*
positions.
To align the bars on the top edge pass a negative *height* and
``align='edge'``.
Returns
-------
`.BarContainer`
Container with all the bars and optionally errorbars.
Other Parameters
----------------
color : color or list of color, optional
The colors of the bar faces.
edgecolor : color or list of color, optional
The colors of the bar edges.
linewidth : float or array-like, optional
Width of the bar edge(s). If 0, don't draw edges.
tick_label : str or list of str, optional
The tick labels of the bars.
Default: None (Use default numeric labels.)
xerr, yerr : float or array-like of shape(N,) or shape(2, N), optional
If not ``None``, add horizontal / vertical errorbars to the
bar tips. The values are +/- sizes relative to the data:
- scalar: symmetric +/- values for all bars
- shape(N,): symmetric +/- values for each bar
- shape(2, N): Separate - and + values for each bar. First row
contains the lower errors, the second row contains the upper
errors.
- *None*: No errorbar. (default)
See :doc:`/gallery/statistics/errorbar_features`
for an example on the usage of ``xerr`` and ``yerr``.
ecolor : color or list of color, default: 'black'
The line color of the errorbars.
capsize : float, default: :rc:`errorbar.capsize`
The length of the error bar caps in points.
error_kw : dict, optional
Dictionary of kwargs to be passed to the `~.Axes.errorbar`
method. Values of *ecolor* or *capsize* defined here take
precedence over the independent kwargs.
log : bool, default: False
If ``True``, set the x-axis to be log scale.
**kwargs : `.Rectangle` properties
%(Rectangle:kwdoc)s
See Also
--------
bar : Plot a vertical bar plot.
Notes
-----
Stacked bars can be achieved by passing individual *left* values per
bar. See
:doc:`/gallery/lines_bars_and_markers/horizontal_barchart_distribution`
.
"""
kwargs.setdefault('orientation', 'horizontal')
patches = self.bar(x=left, height=height, width=width, bottom=y,
align=align, **kwargs)
return patches
def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge",
padding=0, **kwargs):
"""