Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
164 contributors

Users who have contributed to this file

@tacaswell @anntzer @timhoffm @dstansby @NelleV @jklymak @efiring @phobson @QuLogic @mdboom @jenshnielsen @WeatherGod @jankatins @daurer @bcongdon @dopplershift @solvents @afvincent @choldgraf @mobando @klaragerlei @GregoryAshton @dcmarcu @tillahoffmann @sfroid @larrybradley
8166 lines (6601 sloc) 307 KB
import collections.abc
import functools
import itertools
import logging
import math
from numbers import Number
import numpy as np
from numpy import ma
import matplotlib.category as _ # <-registers a category unit converter
import matplotlib.cbook as cbook
import matplotlib.collections as mcoll
import matplotlib.colors as mcolors
import matplotlib.contour as mcontour
import matplotlib.dates as _ # <-registers a date unit converter
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
from matplotlib import _preprocess_data, rcParams
from matplotlib.axes._base import _AxesBase, _process_plot_format
from matplotlib.axes._secondary_axes import SecondaryAxis
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer
try:
from numpy.lib.histograms import (
histogram_bin_edges as _histogram_bin_edges)
except ImportError:
# this function is new in np 1.15
def _histogram_bin_edges(arr, bins, range=None, weights=None):
# this in True for 1D arrays, and False for None and str
if np.ndim(bins) == 1:
return bins
if isinstance(bins, str):
# rather than backporting the internals, just do the full
# computation. If this is too slow for users, they can
# update numpy, or pick a manual number of bins
return np.histogram(arr, bins, range, weights)[1]
else:
if bins is None:
# hard-code numpy's default
bins = 10
if range is None:
range = np.min(arr), np.max(arr)
return np.linspace(*range, bins + 1)
_log = logging.getLogger(__name__)
def _make_inset_locator(bounds, trans, parent):
"""
Helper function to locate inset axes, used in
`.Axes.inset_axes`.
A locator gets used in `Axes.set_aspect` to override the default
locations... It is a function that takes an axes object and
a renderer and tells `set_aspect` where it is to be placed.
Here *rect* is a rectangle [l, b, w, h] that specifies the
location for the axes in the transform given by *trans* on the
*parent*.
"""
_bounds = mtransforms.Bbox.from_bounds(*bounds)
_trans = trans
_parent = parent
def inset_locator(ax, renderer):
bbox = _bounds
bb = mtransforms.TransformedBbox(bbox, _trans)
tr = _parent.figure.transFigure.inverted()
bb = mtransforms.TransformedBbox(bb, tr)
return bb
return inset_locator
# The axes module contains all the wrappers to plotting functions.
# All the other methods should go in the _AxesBase class.
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
@cbook.deprecated("3.1")
@property
def aname(self):
return 'Axes'
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, optional
Which title to get, defaults to 'center'.
Returns
-------
title : str
The title text string.
"""
titles = {'left': self._left_title,
'center': self.title,
'right': self._right_title}
title = cbook._check_getitem(titles, loc=loc.lower())
return title.get_text()
def set_title(self, label, fontdict=None, loc=None, pad=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'}, str, optional
Which title to set.
If *None*, defaults to :rc:`axes.titlelocation`.
pad : float
The offset of the title from the top of the axes, in points.
If *None*, defaults to :rc:`axes.titlepad`.
Returns
-------
text : :class:`~matplotlib.text.Text`
The matplotlib text instance representing the title
Other Parameters
----------------
**kwargs : `~matplotlib.text.Text` properties
Other keyword arguments are text properties, see
:class:`~matplotlib.text.Text` for a list of valid text
properties.
"""
if loc is None:
loc = rcParams['axes.titlelocation']
titles = {'left': self._left_title,
'center': self.title,
'right': self._right_title}
title = cbook._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_xlabel(self):
"""
Get the xlabel text string.
"""
label = self.xaxis.get_label()
return label.get_text()
def set_xlabel(self, xlabel, fontdict=None, labelpad=None, **kwargs):
"""
Set the label for the x-axis.
Parameters
----------
xlabel : str
The label text.
labelpad : scalar, optional, default: None
Spacing in points from the axes bounding box including ticks
and tick labels.
Other Parameters
----------------
**kwargs : `.Text` properties
`.Text` properties control the appearance of the label.
See also
--------
text : for information on how override and the optional args work
"""
if labelpad is not None:
self.xaxis.labelpad = labelpad
return self.xaxis.set_label_text(xlabel, fontdict, **kwargs)
def get_ylabel(self):
"""
Get the ylabel text string.
"""
label = self.yaxis.get_label()
return label.get_text()
def set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs):
"""
Set the label for the y-axis.
Parameters
----------
ylabel : str
The label text.
labelpad : scalar, optional, default: None
Spacing in points from the axes bounding box including ticks
and tick labels.
Other Parameters
----------------
**kwargs : `.Text` properties
`.Text` properties control the appearance of the label.
See also
--------
text : for information on how override and the optional args work
"""
if labelpad is not None:
self.yaxis.labelpad = labelpad
return self.yaxis.set_label_text(ylabel, fontdict, **kwargs)
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(labels)
legend(handles, labels)
The call signatures correspond to three different ways how 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::
line, = 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. Labeling existing plot elements**
To make a legend for lines which already exist on the axes
(via plot for instance), simply call this function with an iterable
of strings, one for each legend item. For example::
ax.plot([1, 2, 3])
ax.legend(['A simple line'])
Note: This way of using is discouraged, because the relation between
plot elements and labels is only implicit by their order and can
easily be mixed up.
**3. Explicitly defining the elements 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::
legend((line1, line2, line3), ('label1', 'label2', 'label3'))
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.
Other Parameters
----------------
%(_legend_kw_doc)s
Returns
-------
legend : `~matplotlib.legend.Legend`
Notes
-----
Not all kinds of artist are supported by the legend command. 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 `.Axes` 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
label = kwargs.pop('label', 'inset_axes')
# This puts the rectangle into figure-relative coordinates.
inset_locator = _make_inset_locator(bounds, transform, self)
bb = inset_locator(None, None)
inset_ax = Axes(self.figure, bb.bounds, zorder=zorder,
label=label, **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
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 co-ordinates. Defaults to
`ax.transAxes`, i.e. the units of *rect* are in axes-relative
coordinates.
facecolor : Matplotlib color
Facecolor of the rectangle (default 'none').
edgecolor : Matplotlib color
Color of the rectangle and color of the connecting lines. Default
is '0.5'.
alpha : float
Transparency of the rectangle and connector lines. Default is 0.5.
zorder : float
Drawing order of the rectangle and connector lines. Default is 4.99
(just below the default level of inset axes).
**kwargs
Other keyword arguments are passed on to the rectangle patch.
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
label = kwargs.pop('label', 'indicate_inset')
x, y, width, height = bounds
rectangle_patch = mpatches.Rectangle(
(x, y), width, height,
facecolor=facecolor, edgecolor=edgecolor, alpha=alpha,
zorder=zorder, label=label, 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.transFigure)
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):
return 1 / x
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"')
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"')
@cbook._delete_parameter("3.1", "withdash")
def text(self, x, y, s, fontdict=None, withdash=False, **kwargs):
"""
Add text to the axes.
Add the text *s* to the axes at location *x*, *y* in data coordinates.
Parameters
----------
x, y : scalars
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 : dictionary, optional, default: None
A dictionary to override the default text properties. If fontdict
is None, the defaults are determined by your rc parameters.
withdash : boolean, optional, default: False
Creates a `~matplotlib.text.TextWithDash` instance instead of a
`~matplotlib.text.Text` instance.
Returns
-------
text : `.Text`
The created `.Text` instance.
Other Parameters
----------------
**kwargs : `~matplotlib.text.Text` properties.
Other miscellaneous text parameters.
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))
"""
if fontdict is None:
fontdict = {}
effective_kwargs = {
'verticalalignment': 'baseline',
'horizontalalignment': 'left',
'transform': self.transData,
'clip_on': False,
**fontdict,
**kwargs,
}
# At some point if we feel confident that TextWithDash
# is robust as a drop-in replacement for Text and that
# the performance impact of the heavier-weight class
# isn't too significant, it may make sense to eliminate
# the withdash kwarg and simply delegate whether there's
# a dash to TextWithDash and dashlength.
if (withdash
and withdash is not cbook.deprecation._deprecated_parameter):
t = mtext.TextWithDash(x, y, text=s)
else:
t = mtext.Text(x, y, text=s)
t.update(effective_kwargs)
t.set_clip_path(self.patch)
self._add_text(t)
return t
@docstring.dedent_interpd
def annotate(self, s, xy, *args, **kwargs):
a = mtext.Annotation(s, 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 axis.
Parameters
----------
y : scalar, optional, default: 0
y position in data coordinates of the horizontal line.
xmin : scalar, optional, default: 0
Should be between 0 and 1, 0 being the far left of the plot, 1 the
far right of the plot.
xmax : scalar, optional, default: 1
Should be between 0 and 1, 0 being the far left of the plot, 1 the
far right of the plot.
Returns
-------
line : `~matplotlib.lines.Line2D`
Other Parameters
----------------
**kwargs
Valid keyword arguments are `.Line2D` properties, with the
exception of 'transform':
%(_Line2D_docstr)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)
"""
if "transform" in kwargs:
raise ValueError(
"'transform' is not allowed as a kwarg;"
+ "axhline generates its own transform.")
ymin, ymax = self.get_ybound()
# We need to strip away the units for comparison with
# non-unitized bounds
self._process_unit_info(ydata=y, kwargs=kwargs)
yy = self.convert_yunits(y)
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)
self._request_autoscale_view(scalex=False, scaley=scaley)
return l
@docstring.dedent_interpd
def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
"""
Add a vertical line across the axes.
Parameters
----------
x : scalar, optional, default: 0
x position in data coordinates of the vertical line.
ymin : scalar, optional, default: 0
Should be between 0 and 1, 0 being the bottom of the plot, 1 the
top of the plot.
ymax : scalar, optional, default: 1
Should be between 0 and 1, 0 being the bottom of the plot, 1 the
top of the plot.
Returns
-------
line : `~matplotlib.lines.Line2D`
Other Parameters
----------------
**kwargs
Valid keyword arguments are `.Line2D` properties, with the
exception of 'transform':
%(_Line2D_docstr)s
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)
See also
--------
vlines : Add vertical lines in data coordinates.
axvspan : Add a vertical span (rectangle) across the axis.
axline : Add a line with an abritrary slope.
"""
if "transform" in kwargs:
raise ValueError(
"'transform' is not allowed as a kwarg;"
+ "axvline generates its own transform.")
xmin, xmax = self.get_xbound()
# We need to strip away the units for comparison with
# non-unitized bounds
self._process_unit_info(xdata=x, kwargs=kwargs)
xx = self.convert_xunits(x)
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)
self._request_autoscale_view(scalex=scalex, scaley=False)
return l
@docstring.dedent_interpd
def axline(self, xy1, xy2, **kwargs):
"""
Add an infinitely long straight line that passes through two points.
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.
Parameters
----------
xy1, xy2 : (float, float)
Points for the line to pass through.
Returns
-------
:class:`~matplotlib.lines.Line2D`
Other Parameters
----------------
**kwargs
Valid kwargs are :class:`~matplotlib.lines.Line2D` properties,
with the exception of 'transform':
%(_Line2D_docstr)s
Examples
--------
Draw a thick red line passing through (0, 0) and (1, 1)::
>>> axline((0, 0), (1, 1), linewidth=4, color='r')
See Also
--------
axhline : for horizontal lines
axvline : for vertical lines
"""
if "transform" in kwargs:
raise TypeError("'transform' is not allowed as a kwarg; "
"axline generates its own transform")
x1, y1 = xy1
x2, y2 = xy2
line = mlines._AxLine([x1, x2], [y1, y2], **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"_line{len(self.lines)}")
self.lines.append(line)
line._remove_method = self.lines.remove
self.update_datalim([xy1, xy2])
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 axis.
Draw a horizontal span (rectangle) from *ymin* to *ymax*.
With the default values of *xmin* = 0 and *xmax* = 1, this
always spans the xrange, regardless of the xlim settings, even
if you change them, e.g., with the :meth:`set_xlim` command.
That is, the horizontal extent is in axes coords: 0=left,
0.5=middle, 1.0=right but the *y* location is in data
coordinates.
Parameters
----------
ymin : float
Lower limit of the horizontal span in data units.
ymax : float
Upper limit of the horizontal span in data units.
xmin : float, optional, default: 0
Lower limit of the vertical span in axes (relative
0-1) units.
xmax : float, optional, default: 1
Upper limit of the vertical span in axes (relative
0-1) units.
Returns
-------
Polygon : `~matplotlib.patches.Polygon`
Other Parameters
----------------
**kwargs : `~matplotlib.patches.Polygon` properties.
%(Polygon)s
See Also
--------
axvspan : Add a vertical span across the axes.
"""
trans = self.get_yaxis_transform(which='grid')
# process the unit information
self._process_unit_info([xmin, xmax], [ymin, ymax], kwargs=kwargs)
# first we need to strip away the units
xmin, xmax = self.convert_xunits([xmin, xmax])
ymin, ymax = self.convert_yunits([ymin, ymax])
verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
self.add_patch(p)
self._request_autoscale_view(scalex=False)
return p
def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
"""
Add a vertical span (rectangle) across the axes.
Draw a vertical span (rectangle) from *xmin* to *xmax*. With
the default values of *ymin* = 0 and *ymax* = 1. This always
spans the yrange, regardless of the ylim settings, even if you
change them, e.g., with the :meth:`set_ylim` command. That is,
the vertical extent is in axes coords: 0=bottom, 0.5=middle,
1.0=top but the x location is in data coordinates.
Parameters
----------
xmin : scalar
Number indicating the first X-axis coordinate of the vertical
span rectangle in data units.
xmax : scalar
Number indicating the second X-axis coordinate of the vertical
span rectangle in data units.
ymin : scalar, optional
Number indicating the first Y-axis coordinate of the vertical
span rectangle in relative Y-axis units (0-1). Default to 0.
ymax : scalar, optional
Number indicating the second Y-axis coordinate of the vertical
span rectangle in relative Y-axis units (0-1). Default to 1.
Returns
-------
rectangle : `~matplotlib.patches.Polygon`
Vertical span (rectangle) from (xmin, ymin) to (xmax, ymax).
Other Parameters
----------------
**kwargs
Optional parameters are properties of the class `.Polygon`.
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)
"""
trans = self.get_xaxis_transform(which='grid')
# process the unit information
self._process_unit_info([xmin, xmax], [ymin, ymax], kwargs=kwargs)
# first we need to strip away the units
xmin, xmax = self.convert_xunits([xmin, xmax])
ymin, ymax = self.convert_yunits([ymin, ymax])
verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
self.add_patch(p)
self._request_autoscale_view(scaley=False)
return p
@_preprocess_data(replace_names=["y", "xmin", "xmax", "colors"],
label_namer="y")
def hlines(self, y, xmin, xmax, colors='k', linestyles='solid',
label='', **kwargs):
"""
Plot horizontal lines at each *y* from *xmin* to *xmax*.
Parameters
----------
y : scalar or sequence of scalar
y-indexes where to plot the lines.
xmin, xmax : scalar or 1D array-like
Respective beginning and end of each line. If scalars are
provided, all lines will have same length.
colors : array-like of colors, optional, default: 'k'
linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, optional
label : str, optional, default: ''
Returns
-------
lines : `~matplotlib.collections.LineCollection`
Other Parameters
----------------
**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
# process the unit information
self._process_unit_info([xmin, xmax], y, kwargs=kwargs)
y = self.convert_yunits(y)
xmin = self.convert_xunits(xmin)
xmax = self.convert_xunits(xmax)
if not np.iterable(y):
y = [y]
if not np.iterable(xmin):
xmin = [xmin]
if not np.iterable(xmax):
xmax = [xmax]
y, xmin, xmax = cbook.delete_masked_points(y, xmin, xmax)
y = np.ravel(y)
xmin = np.resize(xmin, y.shape)
xmax = np.resize(xmax, y.shape)
verts = [((thisxmin, thisy), (thisxmax, thisy))
for thisxmin, thisxmax, thisy in zip(xmin, xmax, y)]
lines = mcoll.LineCollection(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='k', linestyles='solid',
label='', **kwargs):
"""
Plot vertical lines.
Plot vertical lines at each *x* from *ymin* to *ymax*.
Parameters
----------
x : scalar or 1D array-like
x-indexes where to plot the lines.
ymin, ymax : scalar or 1D array-like
Respective beginning and end of each line. If scalars are
provided, all lines will have same length.
colors : array-like of colors, optional, default: 'k'
linestyles : {'solid', 'dashed', 'dashdot', 'dotted'}, optional
label : str, optional, default: ''
Returns
-------
lines : `~matplotlib.collections.LineCollection`
Other Parameters
----------------
**kwargs : `~matplotlib.collections.LineCollection` properties.
See also
--------
hlines : horizontal lines
axvline: vertical line across the axes
"""
self._process_unit_info(xdata=x, ydata=[ymin, ymax], kwargs=kwargs)
# We do the conversion first since not all unitized data is uniform
x = self.convert_xunits(x)
ymin = self.convert_yunits(ymin)
ymax = self.convert_yunits(ymax)
if not np.iterable(x):
x = [x]
if not np.iterable(ymin):
ymin = [ymin]
if not np.iterable(ymax):
ymax = [ymax]
x, ymin, ymax = cbook.delete_masked_points(x, ymin, ymax)
x = np.ravel(x)
ymin = np.resize(ymin, x.shape)
ymax = np.resize(ymax, x.shape)
verts = [((thisx, thisymin), (thisx, thisymax))
for thisx, thisymin, thisymax in zip(x, ymin, ymax)]
lines = mcoll.LineCollection(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.
*positions* should be a 1D or 2D array-like object, with each row
corresponding to a row or column of lines.
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 : 1D or 2D array-like object
Each value is an event. If *positions* is a 2D array-like, each
row corresponds to a row or a column of lines (depending on the
*orientation* parameter).
orientation : {'horizontal', 'vertical'}, optional
Controls the direction of the event collections:
- 'horizontal' : the lines are arranged horizontally in rows,
and are vertical.
- 'vertical' : the lines are arranged vertically in columns,
and are horizontal.
lineoffsets : scalar or sequence of scalars, optional, default: 1
The offset of the center of the lines from the origin, in the
direction orthogonal to *orientation*.
linelengths : scalar or sequence of scalars, optional, default: 1
The total height of the lines (i.e. the lines stretches from
``lineoffset - linelength/2`` to ``lineoffset + linelength/2``).
linewidths : scalar, scalar sequence or None, optional, default: None
The line width(s) of the event lines, in points. If it is None,
defaults to its rcParams setting.
colors : color, sequence of colors or None, optional, default: None
The color(s) of the event lines. If it is None, defaults to its
rcParams setting.
linestyles : str or tuple or a sequence of such values, optional
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.
**kwargs : optional
Other keyword arguments are line collection properties. See
:class:`~matplotlib.collections.LineCollection` for a list of
the valid properties.
Returns
-------
list : A list of :class:`~.collections.EventCollection` objects.
Contains the :class:`~.collections.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
"""
self._process_unit_info(xdata=positions,
ydata=[lineoffsets, linelengths],
kwargs=kwargs)
# We do the conversion first since not all unitized data is uniform
positions = self.convert_xunits(positions)
lineoffsets = self.convert_yunits(lineoffsets)
linelengths = self.convert_yunits(linelengths)
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 []
# 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 is not None and
orientation.lower() == "vertical"):
corners = (minline, minpos), (maxline, maxpos)
else: # "horizontal", None or "none" (see EventCollection)
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.DataFame` 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')
- Alternatively, if your data is already a 2d array, you can pass it
directly to *x*, *y*. A separate data set will be drawn for every
column.
Example: an array ``a`` where the first column represents the *x*
values and the other columns are the *y* columns::
>>> plot(a[0], a[1:])
- 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)`.
Other Parameters
----------------
scalex, scaley : bool, optional, default: True
These parameters determined 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 make multiple lines with one plot command, the kwargs
apply to all those lines.
Here is a list of available `.Line2D` properties:
%(_Line2D_docstr)s
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
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
``'s'`` square marker
``'p'`` pentagon marker
``'*'`` star marker
``'h'`` hexagon1 marker
``'H'`` hexagon2 marker
``'+'`` plus marker
``'x'`` x 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)
self._request_autoscale_view(scalex=scalex, scaley=scaley)
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 data that contains dates.
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*.
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 `tzinfo` or None
The time zone to use in labeling dates. If *None*, defaults to
:rc:`timezone`.
xdate : bool, optional, default: True
If *True*, the *x*-axis will be interpreted as Matplotlib dates.
ydate : bool, optional, default: False
If *True*, the *y*-axis will be interpreted as Matplotlib dates.
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
Other Parameters
----------------
**kwargs
Keyword arguments control the `.Line2D` properties:
%(_Line2D_docstr)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)
ret = self.plot(x, y, fmt, **kwargs)
self._request_autoscale_view()
return ret
# @_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 *basex/y*, *subsx/y* and *nonposx/y* control
the x/y-axis properties. They are just forwarded to `.Axes.set_xscale`
and `.Axes.set_yscale`.
Parameters
----------
basex, basey : scalar, optional, default 10
Base of the x/y logarithm.
subsx, subsy : sequence, optional
The location of the minor x/y 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.
nonposx, nonposy : {'mask', 'clip'}, optional, default 'mask'
Non-positive values in x or y can be masked as invalid, or clipped
to a very small positive number.
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
Other Parameters
----------------
**kwargs
All parameters supported by `.plot`.
"""
dx = {k: kwargs.pop(k) for k in ['basex', 'subsx', 'nonposx']
if k in kwargs}
dy = {k: kwargs.pop(k) for k in ['basey', 'subsy', 'nonposy']
if k in kwargs}
self.set_xscale('log', **dx)
self.set_yscale('log', **dy)
l = self.plot(*args, **kwargs)
return l
# @_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 *basex*, *subsx* and *nonposx* control the
x-axis properties. They are just forwarded to `.Axes.set_xscale`.
Parameters
----------
basex : scalar, optional, default 10
Base of the x logarithm.
subsx : 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.
nonposx : {'mask', 'clip'}, optional, default 'mask'
Non-positive values in x can be masked as invalid, or clipped to a
very small positive number.
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
Other Parameters
----------------
**kwargs
All parameters supported by `.plot`.
"""
d = {k: kwargs.pop(k) for k in ['basex', 'subsx', 'nonposx']
if k in kwargs}
self.set_xscale('log', **d)
l = self.plot(*args, **kwargs)
return l
# @_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 *basey*, *subsy* and *nonposy* control the
y-axis properties. They are just forwarded to `.Axes.set_yscale`.
Parameters
----------
basey : scalar, optional, default 10
Base of the y logarithm.
subsy : 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.
nonposy : {'mask', 'clip'}, optional, default 'mask'
Non-positive values in y can be masked as invalid, or clipped to a
very small positive number.
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
Other Parameters
----------------
**kwargs
All parameters supported by `.plot`.
"""
d = {k: kwargs.pop(k) for k in ['basey', 'subsy', 'nonposy']
if k in kwargs}
self.set_yscale('log', **d)
l = self.plot(*args, **kwargs)
return l
@_preprocess_data(replace_names=["x"], label_namer="x")
def acorr(self, x, **kwargs):
"""
Plot the autocorrelation of *x*.
Parameters
----------
x : array-like
detrend : callable, optional, default: `mlab.detrend_none`
*x* is detrended by the *detrend* callable. This must be a
function ``x = detrend(x)`` accepting and returning an
`numpy.array`. Default is no normalization.
normed : bool, optional, default: True
If ``True``, input vectors are normalised to unit length.
usevlines : bool, optional, 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, optional, 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, optional, default: 'o'
The marker for plotting the data points.
Only used if *usevlines* is ``False``.
Notes
-----
The cross correlation is performed with :func:`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 : array-like of length n
y : array-like of length n
detrend : callable, optional, default: `mlab.detrend_none`
*x* and *y* are detrended by the *detrend* callable. This must be a
function ``x = detrend(x)`` accepting and returning an
`numpy.array`. Default is no normalization.
normed : bool, optional, default: True
If ``True``, input vectors are normalised to unit length.
usevlines : bool, optional, 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, optional, 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, optional, default: 'o'
The marker for plotting the data points.
Only used if *usevlines* is ``False``.
Notes
-----
The cross correlation is performed with :func:`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.
Parameters
----------
x : array-like
1-D sequence of x positions. It is assumed, but not checked, that
it is uniformly increasing.
y : array-like
1-D 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.
data : indexable object, optional
An object with labelled data. If given, provide the label names to
plot in *x* and *y*.
where : {'pre', 'post', 'mid'}, optional, 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.
Returns
-------
lines
A list of `.Line2D` objects representing the plotted data.
Other Parameters
----------------
**kwargs
Additional parameters are the same as those for `.plot`.
Notes
-----
.. [notes section required to get data note injection right]
"""
cbook._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):
x0 = x0
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 *width* and *height*. The vertical baseline
is *bottom* (default 0).
Each of *x*, *height*, *width*, and *bottom* may either be a scalar
applying to all bars, or it may be a sequence of length N providing a
separate value for each bar.
Parameters
----------
x : sequence of scalars
The x coordinates of the bars. See also *align* for the
alignment of the bars to the coordinates.
height : scalar or sequence of scalars
The height(s) of the bars.
width : scalar or array-like, optional
The width(s) of the bars (default: 0.8).
bottom : scalar or array-like, optional
The y coordinate(s) of the bars bases (default: 0).
align : {'center', 'edge'}, optional, 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
-------
container : `.BarContainer`
Container with all the bars and optionally errorbars.
Other Parameters
----------------
color : scalar or array-like, optional
The colors of the bar faces.
edgecolor : scalar or array-like, optional
The colors of the bar edges.
linewidth : scalar or array-like, optional
Width of the bar edge(s). If 0, don't draw edges.
tick_label : str or array-like, optional
The tick labels of the bars.
Default: None (Use default numeric labels.)
xerr, yerr : scalar 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 : scalar or array-like, optional, default: 'black'
The line color of the errorbars.
capsize : scalar, optional
The length of the error bar caps in points.
Default: None, which will take the value from
:rc:`errorbar.capsize`.
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, optional, default: False
If *True*, set the y-axis to be log scale.
orientation : {'vertical', 'horizontal'}, optional
*This is for internal use only.* Please use `barh` for
horizontal bar plots. Default: 'vertical'.
See also
--------
barh: Plot a horizontal bar plot.
Notes
-----
The optional arguments *color*, *edgecolor*, *linewidth*,
*xerr*, and *yerr* can be either scalars or sequences of
length equal to the number of bars. This enables you to use
bar as the basis for stacked bar charts, or candlestick plots.
Detail: *xerr* and *yerr* are passed directly to
:meth:`errorbar`, so they can also have shape 2xN for
independent specification of lower and upper errors.
Other optional kwargs:
%(Rectangle)s
"""
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)
# 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)
orientation = kwargs.pop('orientation', 'vertical')
cbook._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(xdata=x, ydata=height, kwargs=kwargs)
if log:
self.set_yscale('log', nonposy='clip')
elif orientation == 'horizontal':
self._process_unit_info(xdata=width, ydata=y, kwargs=kwargs)
if log:
self.set_xscale('log', nonposx='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 = np.broadcast_arrays(
# Make args iterable too.
np.atleast_1d(x), height, width, y, linewidth)
# 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))
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
cbook._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)
for l, b, w, h, c, e, lw in args:
r = mpatches.Rectangle(
xy=(l, b), width=w, height=h,
facecolor=c,
edgecolor=e,
linewidth=lw,
label='_nolegend_',
)
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()
bar_container = BarContainer(patches, errorbar, 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).
Each of *y*, *width*, *height*, and *left* may either be a scalar
applying to all bars, or it may be a sequence of length N providing a
separate value for each bar.
Parameters
----------
y : scalar or array-like
The y coordinates of the bars. See also *align* for the
alignment of the bars to the coordinates.
width : scalar or array-like
The width(s) of the bars.
height : sequence of scalars, optional, default: 0.8
The heights of the bars.
left : sequence of scalars
The x coordinates of the left sides of the bars (default: 0).
align : {'center', 'edge'}, optional, 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
-------
container : `.BarContainer`
Container with all the bars and optionally errorbars.
Other Parameters
----------------
color : scalar or array-like, optional
The colors of the bar faces.
edgecolor : scalar or array-like, optional
The colors of the bar edges.
linewidth : scalar or array-like, optional
Width of the bar edge(s). If 0, don't draw edges.
tick_label : str or array-like, optional
The tick labels of the bars.
Default: None (Use default numeric labels.)
xerr, yerr : scalar 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 : scalar or array-like, optional, default: 'black'
The line color of the errorbars.
capsize : scalar, optional
The length of the error bar caps in points.
Default: None, which will take the value from
:rc:`errorbar.capsize`.
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, optional, default: False
If ``True``, set the x-axis to be log scale.
See also
--------
bar: Plot a vertical bar plot.
Notes
-----
The optional arguments *color*, *edgecolor*, *linewidth*,
*xerr*, and *yerr* can be either scalars or sequences of
length equal to the number of bars. This enables you to use
bar as the basis for stacked bar charts, or candlestick plots.
Detail: *xerr* and *yerr* are passed directly to
:meth:`errorbar`, so they can also have shape 2xN for
independent specification of lower and upper errors.
Other optional kwargs:
%(Rectangle)s
"""
kwargs.setdefault('orientation', 'horizontal')
patches = self.bar(x=left, height=height, width=width, bottom=y,
align=align, **kwargs)
return patches
@_preprocess_data()
@docstring.dedent_interpd
def broken_barh(self, xranges, yrange, **kwargs):
"""
Plot a horizontal sequence of rectangles.
A rectangle is drawn for each element of *xranges*. All rectangles
have the same vertical position and size defined by *yrange*.
This is a convenience function for instantiating a
`.BrokenBarHCollection`, adding it to the axes and autoscaling the
view.
Parameters
----------
xranges : sequence of tuples (*xmin*, *xwidth*)
The x-positions and extends of the rectangles. For each tuple
(*xmin*, *xwidth*) a rectangle is drawn from *xmin* to *xmin* +
*xwidth*.
yrange : (*ymin*, *yheight*)
The y-position and extend for all the rectangles.
Other Parameters
----------------
**kwargs : :class:`.BrokenBarHCollection` properties
Each *kwarg* can be either a single argument applying to all
rectangles, e.g.::
facecolors='black'
or a sequence of arguments over which is cycled, e.g.::
facecolors=('black', 'blue')
would create interleaving black and blue rectangles.
Supported keywords:
%(BrokenBarHCollection)s
Returns
-------
collection : A :class:`~.collections.BrokenBarHCollection`
"""
# process the unit information
if len(xranges):
xdata = cbook.safe_first_element(xranges)
else:
xdata = None
if len(yrange):
ydata = cbook.safe_first_element(yrange)
else:
ydata = None
self._process_unit_info(xdata=xdata,
ydata=ydata,
kwargs=kwargs)
xranges_conv = []
for xr in xranges:
if len(xr) != 2:
raise ValueError('each range in xrange must be a sequence '
'with two elements (i.e. an Nx2 array)')
# convert the absolute values, not the x and dx...
x_conv = np.asarray(self.convert_xunits(xr[0]))
x1 = self._convert_dx(xr[1], xr[0], x_conv, self.convert_xunits)
xranges_conv.append((x_conv, x1))
yrange_conv = self.convert_yunits(yrange)
col = mcoll.BrokenBarHCollection(xranges_conv, yrange_conv, **kwargs)
self.add_collection(col, autolim=True)
self._request_autoscale_view()
return col
@_preprocess_data()
def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0,
label=None, use_line_collection=False):
"""
Create a stem plot.
A stem plot plots vertical lines at each *x* location from the baseline
to *y*, and places a marker there.
Call signature::
stem([x,] y, linefmt=None, markerfmt=None, basefmt=None)
The x-positions are optional. The formats may be provided either as
positional or as keyword-arguments.
Parameters
----------
x : array-like, optional
The x-positions of the stems. Default: (0, 1, ..., len(y) - 1).
y : array-like
The y-values of the stem heads.
linefmt : str, optional
A string defining the properties of the vertical lines. Usually,
this will be a color or a color and a linestyle:
========= =============
Character Line Style
========= =============
``'-'`` solid line
``'--'`` dashed line
``'-.'`` dash-dot line
``':'`` dotted line
========= =============
Default: 'C0-', i.e. solid line with the first color of the color
cycle.
Note: While it is technically possible to specify valid formats
other than color or color and linestyle (e.g. 'rx' or '-.'), this
is beyond the intention of the method and will most likely not
result in a reasonable reasonable plot.
markerfmt : str, optional
A string defining the properties of the markers at the stem heads.
Default: 'C0o', i.e. filled circles with the first color of the
color cycle.
basefmt : str, optional
A format string defining the properties of the baseline.
Default: 'C3-' ('C2-' in classic mode).
bottom : float, optional, default: 0
The y-position of the baseline.
label : str, optional, default: None
The label to use for the stems in legends.
use_line_collection : bool, optional, default: False
If ``True``, store and plot the stem lines as a
`~.collections.LineCollection` instead of individual lines. This
significantly increases performance, and will become the default
option in Matplotlib 3.3. If ``False``, defaults to the old
behavior of using a list of `.Line2D` objects.
Returns
-------
container : :class:`~matplotlib.container.StemContainer`
The container may be treated like a tuple
(*markerline*, *stemlines*, *baseline*)
Notes
-----
.. seealso::
The MATLAB function
`stem <http://www.mathworks.com/help/techdoc/ref/stem.html>`_
which inspired this method.
"""
if not 1 <= len(args) <= 5:
raise TypeError('stem expected between 1 and 5 positional '
'arguments, got {}'.format(args))
if len(args) == 1:
y, = args
x = np.arange(len(y))
args = ()
else:
x, y, *args = args
self._process_unit_info(xdata=x, ydata=y)
x = self.convert_xunits(x)
y = self.convert_yunits(y)
# defaults for formats
if linefmt is None:
try:
# fallback to positional argument
linefmt = args[0]
except IndexError:
linecolor = 'C0'
linemarker = 'None'
linestyle = '-'
else:
linestyle, linemarker, linecolor = \
_process_plot_format(linefmt)
else:
linestyle, linemarker, linecolor = _process_plot_format(linefmt)
if markerfmt is None:
try:
# fallback to positional argument
markerfmt = args[1]
except IndexError:
markercolor = 'C0'
markermarker = 'o'
markerstyle = 'None'
else:
markerstyle, markermarker, markercolor = \
_process_plot_format(markerfmt)
else:
markerstyle, markermarker, markercolor = \
_process_plot_format(markerfmt)
if basefmt is None:
try:
# fallback to positional argument
basefmt = args[2]
except IndexError:
if rcParams['_internal.classic_mode']:
basecolor = 'C2'
else:
basecolor = 'C3'
basemarker = 'None'
basestyle = '-'
else:
basestyle, basemarker, basecolor = \
_process_plot_format(basefmt)
else:
basestyle, basemarker, basecolor = _process_plot_format(basefmt)
# New behaviour in 3.1 is to use a LineCollection for the stemlines
if use_line_collection:
stemlines = [((xi, bottom), (xi, yi)) for xi, yi in zip(x, y)]
if linestyle is None:
linestyle = rcParams['lines.linestyle']
stemlines = mcoll.LineCollection(stemlines, linestyles=linestyle,
colors=linecolor,
label='_nolegend_')
self.add_collection(stemlines)
# Old behaviour is to plot each of the lines individually
else:
cbook._warn_external(
'In Matplotlib 3.3 individual lines on a stem plot will be '
'added as a LineCollection instead of individual lines. '
'This significantly improves the performance of a stem plot. '
'To remove this warning and switch to the new behaviour, '
'set the "use_line_collection" keyword argument to True.')
stemlines = []
for xi, yi in zip(x, y):
l, = self.plot([xi, xi], [bottom, yi],
color=linecolor, linestyle=linestyle,
marker=linemarker, label="_nolegend_")
stemlines.append(l)
markerline, = self.plot(x, y, color=markercolor, linestyle=markerstyle,
marker=markermarker, label="_nolegend_")
baseline, = self.plot([np.min(x), np.max(x)], [bottom, bottom],
color=basecolor, linestyle=basestyle,
marker=basemarker, label="_nolegend_")
stem_container = StemContainer((markerline, stemlines, baseline),
label=label)
self.add_container(stem_container)
return stem_container
@_preprocess_data(replace_names=["x", "explode", "labels", "colors"])
def pie(self, x, explode=None, labels=None, colors=None,
autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1,
startangle=None, radius=None, counterclock=True,
wedgeprops=None, textprops=None, center=(0, 0),
frame=False, rotatelabels=False):
"""
Plot a pie chart.
Make a pie chart of array *x*. The fractional area of each wedge is
given by ``x/sum(x)``. If ``sum(x) < 1``, then the values of *x* give
the fractional area directly and the array will not be normalized. The
resulting pie will have an empty wedge of size ``1 - sum(x)``.
The wedges are plotted counterclockwise, by default starting from the
x-axis.
Parameters
----------
x : array-like
The wedge sizes.
explode : array-like, optional, default: None
If not *None*, is a ``len(x)`` array which specifies the fraction
of the radius with which to offset each wedge.
labels : list, optional, default: None
A sequence of strings providing the labels for each wedge
colors : array-like, optional, default: None
A sequence of matplotlib color args through which the pie chart
will cycle. If *None*, will use the colors in the currently
active cycle.
autopct : None (default), str, or function, optional
If not *None*, is a string or function used to label the wedges
with their numeric value. The label will be placed inside the
wedge. If it is a format string, the label will be ``fmt%pct``.
If it is a function, it will be called.
pctdistance : float, optional, default: 0.6
The ratio between the center of each pie slice and the start of
the text generated by *autopct*. Ignored if *autopct* is *None*.
shadow : bool, optional, default: False
Draw a shadow beneath the pie.
labeldistance : float or None, optional, default: 1.1
The radial distance at which the pie labels are drawn.
If set to ``None``, label are not drawn, but are stored for use in
``legend()``
startangle : float, optional, default: None
If not *None*, rotates the start of the pie chart by *angle*
degrees counterclockwise from the x-axis.
radius : float, optional, default: None
The radius of the pie, if *radius* is *None* it will be set to 1.
counterclock : bool, optional, default: True
Specify fractions direction, clockwise or counterclockwise.
wedgeprops : dict, optional, default: None
Dict of arguments passed to the wedge objects making the pie.
For example, you can pass in ``wedgeprops = {'linewidth': 3}``
to set the width of the wedge border lines equal to 3.
For more details, look at the doc/arguments of the wedge object.
By default ``clip_on=False``.
textprops : dict, optional, default: None
Dict of arguments to pass to the text objects.
center : list of float, optional, default: (0, 0)
Center position of the chart. Takes value (0, 0) or is a sequence
of 2 scalars.
frame : bool, optional, default: False
Plot axes frame with the chart if true.
rotatelabels : bool, optional, default: False
Rotate each label to the angle of the corresponding slice if true.
Returns
-------
patches : list
A sequence of :class:`matplotlib.patches.Wedge` instances
texts : list
A list of the label :class:`matplotlib.text.Text` instances.
autotexts : list
A list of :class:`~matplotlib.text.Text` instances for the numeric
labels. This will only be returned if the parameter *autopct* is
not *None*.
Notes
-----
The pie chart will probably look best if the figure and axes are
square, or the Axes aspect is equal.
This method sets the aspect ratio of the axis to "equal".
The axes aspect ratio can be controlled with `Axes.set_aspect`.
"""
self.set_aspect('equal')
# The use of float32 is "historical", but can't be changed without
# regenerating the test baselines.
x = np.asarray(x, np.float32)
if x.ndim != 1 and x.squeeze().ndim <= 1:
cbook.warn_deprecated(
"3.1", message="Non-1D inputs to pie() are currently "
"squeeze()d, but this behavior is deprecated since %(since)s "
"and will be removed %(removal)s; pass a 1D array instead.")
x = np.atleast_1d(x.squeeze())
sx = x.sum()
if sx > 1:
x = x / sx
if labels is None:
labels = [''] * len(x)
if explode is None:
explode = [0] * len(x)
if len(x) != len(labels):
raise ValueError("'label' must be of length 'x'")
if len(x) != len(explode):
raise ValueError("'explode' must be of length 'x'")
if colors is None:
get_next_color = self._get_patches_for_fill.get_next_color
else:
color_cycle = itertools.cycle(colors)
def get_next_color():
return next(color_cycle)
if radius is None:
radius = 1
# Starting theta1 is the start fraction of the circle
if startangle is None:
theta1 = 0
else:
theta1 = startangle / 360.0
# set default values in wedge_prop
if wedgeprops is None:
wedgeprops = {}
wedgeprops.setdefault('clip_on', False)
if textprops is None:
textprops = {}
textprops.setdefault('clip_on', False)
texts = []
slices = []
autotexts = []
for frac, label, expl in zip(x, labels, explode):
x, y = center
theta2 = (theta1 + frac) if counterclock else (theta1 - frac)
thetam = 2 * np.pi * 0.5 * (theta1 + theta2)
x += expl * math.cos(thetam)
y += expl * math.sin(thetam)
w = mpatches.Wedge((x, y), radius, 360. * min(theta1, theta2),
360. * max(theta1, theta2),
facecolor=get_next_color(),
**wedgeprops)
slices.append(w)
self.add_patch(w)
w.set_label(label)
if shadow:
# make sure to add a shadow after the call to
# add_patch so the figure and transform props will be
# set
shad = mpatches.Shadow(w, -0.02, -0.02)
shad.set_zorder(0.9 * w.get_zorder())
shad.set_label('_nolegend_')
self.add_patch(shad)
if labeldistance is not None:
xt = x + labeldistance * radius * math.cos(thetam)
yt = y + labeldistance * radius * math.sin(thetam)
label_alignment_h = 'left' if xt > 0 else 'right'
label_alignment_v = 'center'
label_rotation = 'horizontal'
if rotatelabels:
label_alignment_v = 'bottom' if yt > 0 else 'top'
label_rotation = (np.rad2deg(thetam)
+ (0 if xt > 0 else 180))
props = dict(horizontalalignment=label_alignment_h,
verticalalignment=label_alignment_v,
rotation=label_rotation,
size=rcParams['xtick.labelsize'])
props.update(textprops)
t = self.text(xt, yt, label, **props)
texts.append(t)
if autopct is not None:
xt = x + pctdistance * radius * math.cos(thetam)
yt = y + pctdistance * radius * math.sin(thetam)
if isinstance(autopct, str):
s = autopct % (100. * frac)
elif callable(autopct):
s = autopct(100. * frac)
else:
raise TypeError(
'autopct must be callable or a format string')
props = dict(horizontalalignment='center',
verticalalignment='center')
props.update(textprops)
t = self.text(xt, yt, s, **props)
autotexts.append(t)
theta1 = theta2
if not frame:
self.set_frame_on(False)
self.set_xlim((-1.25 + center[0],
1.25 + center[0]))
self.set_ylim((-1.25 + center[1],
1.25 + center[1]))
self.set_xticks([])
self.set_yticks([])
if autopct is None:
return slices, texts
else:
return slices, texts, autotexts
@_preprocess_data(replace_names=["x", "y", "xerr", "yerr"],
label_namer="y")
@docstring.dedent_interpd
def errorbar(self, x, y, yerr=None, xerr=None,
fmt='', ecolor=None, elinewidth=None, capsize=None,
barsabove=False, lolims=False, uplims=False,
xlolims=False, xuplims=False, errorevery=1, capthick=None,
**kwargs):
"""
Plot y versus x as lines and/or markers with attached errorbars.
*x*, *y* define the data locations, *xerr*, *yerr* define the errorbar
sizes. By default, this draws the data markers/lines as well the
errorbars. Use fmt='none' to draw errorbars without any data markers.
Parameters
----------
x, y : scalar or array-like
The data positions.
xerr, yerr : scalar or array-like, shape(N,) or shape(2, N), optional
The errorbar sizes:
- scalar: Symmetric +/- values for all data points.
- shape(N,): Symmetric +/-values for each data point.
- 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.
Note that all error arrays should have *positive* values.
See :doc:`/gallery/statistics/errorbar_features`
for an example on the usage of ``xerr`` and ``yerr``.
fmt : str, optional, default: ''
The format for the data points / data lines. See `.plot` for
details.
Use 'none' (case insensitive) to plot errorbars without any data
markers.
ecolor : color, optional, default: None
The color of the errorbar lines. If None, use the color of the
line connecting the markers.
elinewidth : scalar, optional, default: None
The linewidth of the errorbar lines. If None, the linewidth of
the current style is used.
capsize : scalar, optional, default: None
The length of the error bar caps in points. If None, it will take
the value from :rc:`errorbar.capsize`.
capthick : scalar, optional, default: None
An alias to the keyword argument *markeredgewidth* (a.k.a. *mew*).
This setting is a more sensible name for the property that
controls the thickness of the error bar cap in points. For
backwards compatibility, if *mew* or *markeredgewidth* are given,
then they will over-ride *capthick*. This may change in future
releases.
barsabove : bool, optional, default: False
If True, will plot the errorbars above the plot
symbols. Default is below.
lolims, uplims, xlolims, xuplims : bool, optional, default: False
These arguments can be used to indicate that a value gives only
upper/lower limits. In that case a caret symbol is used to
indicate this. *lims*-arguments may be of the same type as *xerr*
and *yerr*. To use limits with inverted axes, :meth:`set_xlim`
or :meth:`set_ylim` must be called before :meth:`errorbar`.
errorevery : int or (int, int), optional, default: 1
draws error bars on a subset of the data. *errorevery* =N draws
error bars on the points (x[::N], y[::N]).
*errorevery* =(start, N) draws error bars on the points
(x[start::N], y[start::N]). e.g. errorevery=(6, 3)
adds error bars to the data at (x[6], x[9], x[12], x[15], ...).
Used to avoid overlapping error bars when two series share x-axis
values.
Returns
-------
container : :class:`~.container.ErrorbarContainer`
The container contains:
- plotline: `.Line2D` instance of x, y plot markers and/or line.
- caplines: A tuple of `.Line2D` instances of the error bar caps.
- barlinecols: A tuple of
:class:`~matplotlib.collections.LineCollection` with the
horizontal and vertical error ranges.
Other Parameters
----------------
**kwargs
All other keyword arguments are passed on to the plot
command for the markers. For example, this code makes big red
squares with thick green edges::
x, y, yerr = rand(3, 10)
errorbar(x, y, yerr, marker='s', mfc='red',
mec='green', ms=20, mew=4)
where *mfc*, *mec*, *ms* and *mew* are aliases for the longer
property names, *markerfacecolor*, *markeredgecolor*, *markersize*
and *markeredgewidth*.
Valid kwargs for the marker properties are `.Lines2D` properties:
%(_Line2D_docstr)s
"""
kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
# anything that comes in as 'None', drop so the default thing
# happens down stream
kwargs = {k: v for k, v in kwargs.items() if v is not None}
kwargs.setdefault('zorder', 2)
try:
offset, errorevery = errorevery
except TypeError:
offset = 0
if errorevery < 1 or int(errorevery) != errorevery:
raise ValueError(
'errorevery must be positive integer or tuple of integers')
if int(offset) != offset:
raise ValueError("errorevery's starting index must be an integer")
self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
plot_line = (fmt.lower() != 'none')
label = kwargs.pop("label", None)
if fmt == '':
fmt_style_kwargs = {}
else:
fmt_style_kwargs = {k: v for k, v in
zip(('linestyle', 'marker', 'color'),
_process_plot_format(fmt))
if v is not None}
if fmt == 'none':
# Remove alpha=0 color that _process_plot_format returns
fmt_style_kwargs.pop('color')
if ('color' in kwargs or 'color' in fmt_style_kwargs or
ecolor is not None):
base_style = {}
if 'color' in kwargs:
base_style['color'] = kwargs.pop('color')
else:
base_style = next(self._get_lines.prop_cycler)
base_style['label'] = '_nolegend_'
base_style.update(fmt_style_kwargs)
if 'color' not in base_style:
base_style['color'] = 'C0'
if ecolor is None:
ecolor = base_style['color']
# make sure all the args are iterable; use lists not arrays to
# preserve units
if not np.iterable(x):
x = [x]
if not np.iterable(y):
y = [y]
if xerr is not None:
if not np.iterable(xerr):
xerr = [xerr] * len(x)
if yerr is not None:
if not np.iterable(yerr):
yerr = [yerr] * len(y)
# make the style dict for the 'normal' plot line
plot_line_style = {
**base_style,
**kwargs,
'zorder': (kwargs['zorder'] - .1 if barsabove else
kwargs['zorder'] + .1),
}
# make the style dict for the line collections (the bars)
eb_lines_style = dict(base_style)
eb_lines_style.pop('marker', None)
eb_lines_style.pop('linestyle', None)
eb_lines_style['color'] = ecolor
if elinewidth:
eb_lines_style['linewidth'] = elinewidth
elif 'linewidth' in kwargs:
eb_lines_style['linewidth'] = kwargs['linewidth']
for key in ('transform', 'alpha', 'zorder', 'rasterized'):
if key in kwargs:
eb_lines_style[key] = kwargs[key]
# set up cap style dictionary
eb_cap_style = dict(base_style)
# eject any marker information from format string
eb_cap_style.pop('marker', None)
eb_lines_style.pop('markerfacecolor', None)
eb_lines_style.pop('markeredgewidth', None)
eb_lines_style.pop('markeredgecolor', None)
eb_cap_style.pop('ls', None)
eb_cap_style['linestyle'] = 'none'
if capsize is None:
capsize = rcParams["errorbar.capsize"]
if capsize > 0:
eb_cap_style['markersize'] = 2. * capsize
if capthick is not None:
eb_cap_style['markeredgewidth'] = capthick
# For backwards-compat, allow explicit setting of
# 'markeredgewidth' to over-ride capthick.
for key in ('markeredgewidth', 'transform', 'alpha',
'zorder', 'rasterized'):
if key in kwargs:
eb_cap_style[key] = kwargs[key]
eb_cap_style['color'] = ecolor
data_line = None
if plot_line:
data_line = mlines.Line2D(x, y, **plot_line_style)
self.add_line(data_line)
barcols = []
caplines = []
# arrays fine here, they are booleans and hence not units
lolims = np.broadcast_to(lolims, len(x)).astype(bool)
uplims = np.broadcast_to(uplims, len(x)).astype(bool)
xlolims = np.broadcast_to(xlolims, len(x)).astype(bool)
xuplims = np.broadcast_to(xuplims, len(x)).astype(bool)
everymask = np.zeros(len(x), bool)
everymask[offset::errorevery] = True
def xywhere(xs, ys, mask):
"""
return xs[mask], ys[mask] where mask is True but xs and
ys are not arrays
"""
assert len(xs) == len(ys)
assert len(xs) == len(mask)
xs = [thisx for thisx, b in zip(xs, mask) if b]
ys = [thisy for thisy, b in zip(ys, mask) if b]
return xs, ys
def extract_err(err, data):
"""
Private function to parse *err* and subtract/add it to *data*.
Both *err* and *data* are already iterables at this point.
"""
try: # Asymmetric error: pair of 1D iterables.
a, b = err
iter(a)
iter(b)
except (TypeError, ValueError):
a = b = err # Symmetric error: 1D iterable.
# This could just be `np.ndim(a) > 1 and np.ndim(b) > 1`, except
# for the (undocumented, but tested) support for (n, 1) arrays.
a_sh = np.shape(a)
b_sh = np.shape(b)
if (len(a_sh) > 2 or (len(a_sh) == 2 and a_sh[1] != 1)
or len(b_sh) > 2 or (len(b_sh) == 2 and b_sh[1] != 1)):
raise ValueError(
"err must be a scalar or a 1D or (2, n) array-like")
if len(a_sh) == 2 or len(b_sh) == 2:
cbook.warn_deprecated(
"3.1", message="Support for passing a (n, 1)-shaped error "
"array to errorbar() is deprecated since Matplotlib "
"%(since)s and will be removed %(removal)s; pass a 1D "
"array instead.")
# Using list comprehensions rather than arrays to preserve units.
for e in [a, b]:
if len(data) != len(e):
raise ValueError(
f"The lengths of the data ({len(data)}) and the "
f"error {len(e)} do not match")
low = [v - e for v, e in zip(data, a)]
high = [v + e for v, e in zip(data, b)]
return low, high
if xerr is not None:
left, right = extract_err(xerr, x)
# select points without upper/lower limits in x and
# draw normal errorbars for these points
noxlims = ~(xlolims | xuplims)
if noxlims.any() or len(noxlims) == 0:
yo, _ = xywhere(y, right, noxlims & everymask)
lo, ro = xywhere(left, right, noxlims & everymask)
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
if capsize > 0:
caplines.append(mlines.Line2D(lo, yo, marker='|',
**eb_cap_style))
caplines.append(mlines.Line2D(ro, yo, marker='|',
**eb_cap_style))
if xlolims.any():
yo, _ = xywhere(y, right, xlolims & everymask)
lo, ro = xywhere(x, right, xlolims & everymask)
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
rightup, yup = xywhere(right, y, xlolims & everymask)
if self.xaxis_inverted():
marker = mlines.CARETLEFTBASE
else:
marker = mlines.CARETRIGHTBASE
caplines.append(
mlines.Line2D(rightup, yup, ls='None', marker=marker,
**eb_cap_style))
if capsize > 0:
xlo, ylo = xywhere(x, y, xlolims & everymask)
caplines.append(mlines.Line2D(xlo, ylo, marker='|',
**eb_cap_style))
if xuplims.any():
yo, _ = xywhere(y, right, xuplims & everymask)
lo, ro = xywhere(left, x, xuplims & everymask)
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
leftlo, ylo = xywhere(left, y, xuplims & everymask)
if self.xaxis_inverted():
marker = mlines.CARETRIGHTBASE
else:
marker = mlines.CARETLEFTBASE
caplines.append(
mlines.Line2D(leftlo, ylo, ls='None', marker=marker,
**eb_cap_style))
if capsize > 0:
xup, yup = xywhere(x, y, xuplims & everymask)
caplines.append(mlines.Line2D(xup, yup, marker='|',
**eb_cap_style))
if yerr is not None:
lower, upper = extract_err(yerr, y)
# select points without upper/lower limits in y and
# draw normal errorbars for these points
noylims = ~(lolims | uplims)
if noylims.any() or len(noylims) == 0:
xo, _ = xywhere(x, lower, noylims & everymask)
lo, uo = xywhere(lower, upper, noylims & everymask)
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
if capsize > 0:
caplines.append(mlines.Line2D(xo, lo, marker='_',
**eb_cap_style))
caplines.append(mlines.Line2D(xo, uo, marker='_',
**eb_cap_style))
if lolims.any():
xo, _ = xywhere(x, lower, lolims & everymask)
lo, uo = xywhere(y, upper, lolims & everymask)
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
xup, upperup = xywhere(x, upper, lolims & everymask)
if self.yaxis_inverted():
marker = mlines.CARETDOWNBASE
else:
marker = mlines.CARETUPBASE
caplines.append(
mlines.Line2D(xup, upperup, ls='None', marker=marker,
**eb_cap_style))
if capsize > 0:
xlo, ylo = xywhere(x, y, lolims & everymask)
caplines.append(mlines.Line2D(xlo, ylo, marker='_',
**eb_cap_style))
if uplims.any():
xo, _ = xywhere(x, lower, uplims & everymask)
lo, uo = xywhere(lower, y, uplims & everymask)
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
xlo, lowerlo = xywhere(x, lower, uplims & everymask)
if self.yaxis_inverted():
marker = mlines.CARETUPBASE
else:
marker = mlines.CARETDOWNBASE
caplines.append(
mlines.Line2D(xlo, lowerlo, ls='None', marker=marker,
**eb_cap_style))
if capsize > 0:
xup, yup = xywhere(x, y, uplims & everymask)
caplines.append(mlines.Line2D(xup, yup, marker='_',
**eb_cap_style))
for l in caplines:
self.add_line(l)
self._request_autoscale_view()
errorbar_container = ErrorbarContainer((data_line, tuple(caplines),
tuple(barcols)),
has_xerr=(xerr is not None),
has_yerr=(yerr is not None),
label=label)
self.containers.append(errorbar_container)
return errorbar_container # (l0, caplines, barcols)
@cbook._rename_parameter("3.1", "manage_xticks", "manage_ticks")
@_preprocess_data()
def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
positions=None, widths=None, patch_artist=None,
bootstrap=None, usermedians=None, conf_intervals=None,
meanline=None, showmeans=None, showcaps=None,
showbox=None, showfliers=None, boxprops=None,
labels=None, flierprops=None, medianprops=None,
meanprops=None, capprops=None, whiskerprops=None,
manage_ticks=True, autorange=False, zorder=None):
"""
Make a box and whisker plot.
Make a box and whisker plot for each column of ``x`` or each
vector in sequence ``x``. The box extends from the lower to
upper quartile values of the data, with a line at the median.
The whiskers extend from the box to show the range of the
data. Flier points are those past the end of the whiskers.
Parameters
----------
x : Array or a sequence of vectors.
The input data.
notch : bool, optional (False)
If `True`, will produce a notched box plot. Otherwise, a
rectangular boxplot is produced. The notches represent the
confidence interval (CI) around the median. See the entry
for the ``bootstrap`` parameter for information regarding
how the locations of the notches are computed.
.. note::
In cases where the values of the CI are less than the
lower quartile or greater than the upper quartile, the
notches will extend beyond the box, giving it a
distinctive "flipped" appearance. This is expected
behavior and consistent with other statistical
visualization packages.
sym : str, optional
The default symbol for flier points. Enter an empty string
('') if you don't want to show fliers. If `None`, then the
fliers default to 'b+' If you want more control use the
flierprops kwarg.
vert : bool, optional (True)
If `True` (default), makes the boxes vertical. If `False`,
everything is drawn horizontally.
whis : float or (float, float) (default = 1.5)
The position of the whiskers.
If a float, the lower whisker is at the lowest datum above
``Q1 - whis*(Q3-Q1)``, and the upper whisker at the highest datum
below ``Q3 + whis*(Q3-Q1)``, where Q1 and Q3 are the first and
third quartiles. The default value of ``whis = 1.5`` corresponds
to Tukey's original definition of boxplots.
If a pair of floats, they indicate the percentiles at which to
draw the whiskers (e.g., (5, 95)). In particular, setting this to
(0, 100) results in whiskers covering the whole range of the data.
"range" is a deprecated synonym for (0, 100).
In the edge case where ``Q1 == Q3``, *whis* is automatically set
to (0, 100) (cover the whole range of the data) if *autorange* is
True.
Beyond the whiskers, data are considered outliers and are plotted
as individual points.
bootstrap : int, optional
Specifies whether to bootstrap the confidence intervals
around the median for notched boxplots. If ``bootstrap`` is
None, no bootstrapping is performed, and notches are
calculated using a Gaussian-based asymptotic approximation
(see McGill, R., Tukey, J.W., and Larsen, W.A., 1978, and
Kendall and Stuart, 1967). Otherwise, bootstrap specifies
the number of times to bootstrap the median to determine its
95% confidence intervals. Values between 1000 and 10000 are
recommended.
usermedians : array-like, optional
An array or sequence whose first dimension (or length) is
compatible with ``x``. This overrides the medians computed
by matplotlib for each element of ``usermedians`` that is not
`None`. When an element of ``usermedians`` is None, the median
will be computed by matplotlib as normal.
conf_intervals : array-like, optional
Array or sequence whose first dimension (or length) is
compatible with ``x`` and whose second dimension is 2. When
the an element of ``conf_intervals`` is not None, the
notch locations computed by matplotlib are overridden
(provided ``notch`` is `True`). When an element of
``conf_intervals`` is `None`, the notches are computed by the
method specified by the other kwargs (e.g., ``bootstrap``).
positions : array-like, optional
Sets the positions of the boxes. The ticks and limits are
automatically set to match the positions. Defaults to
`range(1, N+1)` where N is the number of boxes to be drawn.
widths : scalar or array-like
Sets the width of each box either with a scalar or a
sequence. The default is 0.5, or ``0.15*(distance between
extreme positions)``, if that is smaller.
patch_artist : bool, optional (False)
If `False` produces boxes with the Line2D artist. Otherwise,
boxes and drawn with Patch artists.
labels : sequence, optional
Labels for each dataset. Length must be compatible with
dimensions of ``x``.
manage_ticks : bool, optional (True)
If True, the tick locations and labels will be adjusted to match
the boxplot positions.
autorange : bool, optional (False)
When `True` and the data are distributed such that the 25th and
75th percentiles are equal, ``whis`` is set to (0, 100) such
that the whisker ends are at the minimum and maximum of the data.
meanline : bool, optional (False)
If `True` (and ``showmeans`` is `True`), will try to render
the mean as a line spanning the full width of the box
according to ``meanprops`` (see below). Not recommended if
``shownotches`` is also True. Otherwise, means will be shown
as points.
zorder : scalar, optional (None)
Sets the zorder of the boxplot.