Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/axes/_base.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from collections import OrderedDict
2import itertools
3import logging
4import math
5from numbers import Real
6from operator import attrgetter
7import types
9import numpy as np
11import matplotlib as mpl
12from matplotlib import cbook, rcParams
13from matplotlib.cbook import _OrderedSet, _check_1d, index_of
14from matplotlib import docstring
15import matplotlib.colors as mcolors
16import matplotlib.lines as mlines
17import matplotlib.patches as mpatches
18import matplotlib.artist as martist
19import matplotlib.transforms as mtransforms
20import matplotlib.ticker as mticker
21import matplotlib.axis as maxis
22import matplotlib.spines as mspines
23import matplotlib.font_manager as font_manager
24import matplotlib.text as mtext
25import matplotlib.image as mimage
26from matplotlib.rcsetup import cycler, validate_axisbelow
28_log = logging.getLogger(__name__)
31def _process_plot_format(fmt):
32 """
33 Convert a MATLAB style color/line style format string to a (*linestyle*,
34 *marker*, *color*) tuple.
36 Example format strings include:
38 * 'ko': black circles
39 * '.b': blue dots
40 * 'r--': red dashed lines
41 * 'C2--': the third color in the color cycle, dashed lines
43 See Also
44 --------
45 matplotlib.Line2D.lineStyles, matplotlib.colors.cnames
46 All possible styles and color format strings.
47 """
49 linestyle = None
50 marker = None
51 color = None
53 # Is fmt just a colorspec?
54 try:
55 color = mcolors.to_rgba(fmt)
57 # We need to differentiate grayscale '1.0' from tri_down marker '1'
58 try:
59 fmtint = str(int(fmt))
60 except ValueError:
61 return linestyle, marker, color # Yes
62 else:
63 if fmt != fmtint:
64 # user definitely doesn't want tri_down marker
65 return linestyle, marker, color # Yes
66 else:
67 # ignore converted color
68 color = None
69 except ValueError:
70 pass # No, not just a color.
72 i = 0
73 while i < len(fmt):
74 c = fmt[i]
75 if fmt[i:i+2] in mlines.lineStyles: # First, the two-char styles.
76 if linestyle is not None:
77 raise ValueError(
78 'Illegal format string "%s"; two linestyle symbols' % fmt)
79 linestyle = fmt[i:i+2]
80 i += 2
81 elif c in mlines.lineStyles:
82 if linestyle is not None:
83 raise ValueError(
84 'Illegal format string "%s"; two linestyle symbols' % fmt)
85 linestyle = c
86 i += 1
87 elif c in mlines.lineMarkers:
88 if marker is not None:
89 raise ValueError(
90 'Illegal format string "%s"; two marker symbols' % fmt)
91 marker = c
92 i += 1
93 elif c in mcolors.get_named_colors_mapping():
94 if color is not None:
95 raise ValueError(
96 'Illegal format string "%s"; two color symbols' % fmt)
97 color = c
98 i += 1
99 elif c == 'C' and i < len(fmt) - 1:
100 color_cycle_number = int(fmt[i + 1])
101 color = mcolors.to_rgba("C{}".format(color_cycle_number))
102 i += 2
103 else:
104 raise ValueError(
105 'Unrecognized character %c in format string' % c)
107 if linestyle is None and marker is None:
108 linestyle = rcParams['lines.linestyle']
109 if linestyle is None:
110 linestyle = 'None'
111 if marker is None:
112 marker = 'None'
114 return linestyle, marker, color
117class _process_plot_var_args:
118 """
119 Process variable length arguments to the plot command, so that
120 plot commands like the following are supported::
122 plot(t, s)
123 plot(t1, s1, t2, s2)
124 plot(t1, s1, 'ko', t2, s2)
125 plot(t1, s1, 'ko', t2, s2, 'r--', t3, e3)
127 an arbitrary number of *x*, *y*, *fmt* are allowed
128 """
129 def __init__(self, axes, command='plot'):
130 self.axes = axes
131 self.command = command
132 self.set_prop_cycle()
134 def __getstate__(self):
135 # note: it is not possible to pickle a generator (and thus a cycler).
136 return {'axes': self.axes, 'command': self.command}
138 def __setstate__(self, state):
139 self.__dict__ = state.copy()
140 self.set_prop_cycle()
142 def set_prop_cycle(self, *args, **kwargs):
143 # Can't do `args == (None,)` as that crashes cycler.
144 if not (args or kwargs) or (len(args) == 1 and args[0] is None):
145 prop_cycler = rcParams['axes.prop_cycle']
146 else:
147 prop_cycler = cycler(*args, **kwargs)
149 self.prop_cycler = itertools.cycle(prop_cycler)
150 # This should make a copy
151 self._prop_keys = prop_cycler.keys
153 def __call__(self, *args, **kwargs):
154 self.axes._process_unit_info(kwargs=kwargs)
156 for pos_only in "xy":
157 if pos_only in kwargs:
158 raise TypeError("{} got an unexpected keyword argument {!r}"
159 .format(self.command, pos_only))
161 if not args:
162 return
164 # Process the 'data' kwarg.
165 data = kwargs.pop("data", None)
166 if data is not None:
167 replaced = [mpl._replacer(data, arg) for arg in args]
168 if len(args) == 1:
169 label_namer_idx = 0
170 elif len(args) == 2: # Can be x, y or y, c.
171 # Figure out what the second argument is.
172 # 1) If the second argument cannot be a format shorthand, the
173 # second argument is the label_namer.
174 # 2) Otherwise (it could have been a format shorthand),
175 # a) if we did perform a substitution, emit a warning, and
176 # use it as label_namer.
177 # b) otherwise, it is indeed a format shorthand; use the
178 # first argument as label_namer.
179 try:
180 _process_plot_format(args[1])
181 except ValueError: # case 1)
182 label_namer_idx = 1
183 else:
184 if replaced[1] is not args[1]: # case 2a)
185 cbook._warn_external(
186 f"Second argument {args[1]!r} is ambiguous: could "
187 f"be a format string but is in 'data'; using as "
188 f"data. If it was intended as data, set the "
189 f"format string to an empty string to suppress "
190 f"this warning. If it was intended as a format "
191 f"string, explicitly pass the x-values as well. "
192 f"Alternatively, rename the entry in 'data'.",
193 RuntimeWarning)
194 label_namer_idx = 1
195 else: # case 2b)
196 label_namer_idx = 0
197 elif len(args) == 3:
198 label_namer_idx = 1
199 else:
200 raise ValueError(
201 "Using arbitrary long args with data is not supported due "
202 "to ambiguity of arguments; use multiple plotting calls "
203 "instead")
204 if kwargs.get("label") is None:
205 kwargs["label"] = mpl._label_from_arg(
206 replaced[label_namer_idx], args[label_namer_idx])
207 args = replaced
209 # Repeatedly grab (x, y) or (x, y, format) from the front of args and
210 # massage them into arguments to plot() or fill().
211 while args:
212 this, args = args[:2], args[2:]
213 if args and isinstance(args[0], str):
214 this += args[0],
215 args = args[1:]
216 yield from self._plot_args(this, kwargs)
218 def get_next_color(self):
219 """Return the next color in the cycle."""
220 if 'color' not in self._prop_keys:
221 return 'k'
222 return next(self.prop_cycler)['color']
224 def _getdefaults(self, ignore, kw):
225 """
226 If some keys in the property cycle (excluding those in the set
227 *ignore*) are absent or set to None in the dict *kw*, return a copy
228 of the next entry in the property cycle, excluding keys in *ignore*.
229 Otherwise, don't advance the property cycle, and return an empty dict.
230 """
231 prop_keys = self._prop_keys - ignore
232 if any(kw.get(k, None) is None for k in prop_keys):
233 # Need to copy this dictionary or else the next time around
234 # in the cycle, the dictionary could be missing entries.
235 default_dict = next(self.prop_cycler).copy()
236 for p in ignore:
237 default_dict.pop(p, None)
238 else:
239 default_dict = {}
240 return default_dict
242 def _setdefaults(self, defaults, kw):
243 """
244 Add to the dict *kw* the entries in the dict *default* that are absent
245 or set to None in *kw*.
246 """
247 for k in defaults:
248 if kw.get(k, None) is None:
249 kw[k] = defaults[k]
251 def _makeline(self, x, y, kw, kwargs):
252 kw = {**kw, **kwargs} # Don't modify the original kw.
253 default_dict = self._getdefaults(set(), kw)
254 self._setdefaults(default_dict, kw)
255 seg = mlines.Line2D(x, y, **kw)
256 return seg
258 def _makefill(self, x, y, kw, kwargs):
259 # Polygon doesn't directly support unitized inputs.
260 x = self.axes.convert_xunits(x)
261 y = self.axes.convert_yunits(y)
263 kw = kw.copy() # Don't modify the original kw.
264 kwargs = kwargs.copy()
266 # Ignore 'marker'-related properties as they aren't Polygon
267 # properties, but they are Line2D properties, and so they are
268 # likely to appear in the default cycler construction.
269 # This is done here to the defaults dictionary as opposed to the
270 # other two dictionaries because we do want to capture when a
271 # *user* explicitly specifies a marker which should be an error.
272 # We also want to prevent advancing the cycler if there are no
273 # defaults needed after ignoring the given properties.
274 ignores = {'marker', 'markersize', 'markeredgecolor',
275 'markerfacecolor', 'markeredgewidth'}
276 # Also ignore anything provided by *kwargs*.
277 for k, v in kwargs.items():
278 if v is not None:
279 ignores.add(k)
281 # Only using the first dictionary to use as basis
282 # for getting defaults for back-compat reasons.
283 # Doing it with both seems to mess things up in
284 # various places (probably due to logic bugs elsewhere).
285 default_dict = self._getdefaults(ignores, kw)
286 self._setdefaults(default_dict, kw)
288 # Looks like we don't want "color" to be interpreted to
289 # mean both facecolor and edgecolor for some reason.
290 # So the "kw" dictionary is thrown out, and only its
291 # 'color' value is kept and translated as a 'facecolor'.
292 # This design should probably be revisited as it increases
293 # complexity.
294 facecolor = kw.get('color', None)
296 # Throw out 'color' as it is now handled as a facecolor
297 default_dict.pop('color', None)
299 # To get other properties set from the cycler
300 # modify the kwargs dictionary.
301 self._setdefaults(default_dict, kwargs)
303 seg = mpatches.Polygon(np.column_stack((x, y)),
304 facecolor=facecolor,
305 fill=kwargs.get('fill', True),
306 closed=kw['closed'])
307 seg.set(**kwargs)
308 return seg
310 def _plot_args(self, tup, kwargs):
311 if len(tup) > 1 and isinstance(tup[-1], str):
312 linestyle, marker, color = _process_plot_format(tup[-1])
313 tup = tup[:-1]
314 elif len(tup) == 3:
315 raise ValueError('third arg must be a format string')
316 else:
317 linestyle, marker, color = None, None, None
319 # Don't allow any None value; these would be up-converted to one
320 # element array of None which causes problems downstream.
321 if any(v is None for v in tup):
322 raise ValueError("x, y, and format string must not be None")
324 kw = {}
325 for k, v in zip(('linestyle', 'marker', 'color'),
326 (linestyle, marker, color)):
327 if v is not None:
328 kw[k] = v
330 if len(tup) == 2:
331 x = _check_1d(tup[0])
332 y = _check_1d(tup[-1])
333 else:
334 x, y = index_of(tup[-1])
336 if self.axes.xaxis is not None:
337 self.axes.xaxis.update_units(x)
338 if self.axes.yaxis is not None:
339 self.axes.yaxis.update_units(y)
341 if x.shape[0] != y.shape[0]:
342 raise ValueError(f"x and y must have same first dimension, but "
343 f"have shapes {x.shape} and {y.shape}")
344 if x.ndim > 2 or y.ndim > 2:
345 raise ValueError(f"x and y can be no greater than 2-D, but have "
346 f"shapes {x.shape} and {y.shape}")
347 if x.ndim == 1:
348 x = x[:, np.newaxis]
349 if y.ndim == 1:
350 y = y[:, np.newaxis]
352 if self.command == 'plot':
353 func = self._makeline
354 else:
355 kw['closed'] = kwargs.get('closed', True)
356 func = self._makefill
358 ncx, ncy = x.shape[1], y.shape[1]
359 if ncx > 1 and ncy > 1 and ncx != ncy:
360 cbook.warn_deprecated(
361 "2.2", message="cycling among columns of inputs with "
362 "non-matching shapes is deprecated.")
363 return [func(x[:, j % ncx], y[:, j % ncy], kw, kwargs)
364 for j in range(max(ncx, ncy))]
367class _AxesBase(martist.Artist):
368 name = "rectilinear"
370 _shared_x_axes = cbook.Grouper()
371 _shared_y_axes = cbook.Grouper()
372 _twinned_axes = cbook.Grouper()
374 def __str__(self):
375 return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format(
376 type(self).__name__, self._position.bounds)
378 def __init__(self, fig, rect,
379 facecolor=None, # defaults to rc axes.facecolor
380 frameon=True,
381 sharex=None, # use Axes instance's xaxis info
382 sharey=None, # use Axes instance's yaxis info
383 label='',
384 xscale=None,
385 yscale=None,
386 **kwargs
387 ):
388 """
389 Build an axes in a figure.
391 Parameters
392 ----------
393 fig : `~matplotlib.figure.Figure`
394 The axes is build in the `.Figure` *fig*.
396 rect : [left, bottom, width, height]
397 The axes is build in the rectangle *rect*. *rect* is in
398 `.Figure` coordinates.
400 sharex, sharey : `~.axes.Axes`, optional
401 The x or y `~.matplotlib.axis` is shared with the x or
402 y axis in the input `~.axes.Axes`.
404 frameon : bool, optional
405 True means that the axes frame is visible.
407 **kwargs
408 Other optional keyword arguments:
410 %(Axes)s
412 Returns
413 -------
414 axes : `~.axes.Axes`
415 The new `~.axes.Axes` object.
416 """
418 martist.Artist.__init__(self)
419 if isinstance(rect, mtransforms.Bbox):
420 self._position = rect
421 else:
422 self._position = mtransforms.Bbox.from_bounds(*rect)
423 if self._position.width < 0 or self._position.height < 0:
424 raise ValueError('Width and height specified must be non-negative')
425 self._originalPosition = self._position.frozen()
426 self.axes = self
427 self._aspect = 'auto'
428 self._adjustable = 'box'
429 self._anchor = 'C'
430 self._stale_viewlim_x = False
431 self._stale_viewlim_y = False
432 self._sharex = sharex
433 self._sharey = sharey
434 if sharex is not None:
435 self._shared_x_axes.join(self, sharex)
436 if sharey is not None:
437 self._shared_y_axes.join(self, sharey)
438 self.set_label(label)
439 self.set_figure(fig)
441 self.set_axes_locator(kwargs.get("axes_locator", None))
443 self.spines = self._gen_axes_spines()
445 # this call may differ for non-sep axes, e.g., polar
446 self._init_axis()
447 if facecolor is None:
448 facecolor = rcParams['axes.facecolor']
449 self._facecolor = facecolor
450 self._frameon = frameon
451 self.set_axisbelow(rcParams['axes.axisbelow'])
453 self._rasterization_zorder = None
454 self.cla()
456 # funcs used to format x and y - fall back on major formatters
457 self.fmt_xdata = None
458 self.fmt_ydata = None
460 self.set_navigate(True)
461 self.set_navigate_mode(None)
463 if xscale:
464 self.set_xscale(xscale)
465 if yscale:
466 self.set_yscale(yscale)
468 self.update(kwargs)
470 if self.xaxis is not None:
471 self._xcid = self.xaxis.callbacks.connect(
472 'units finalize', lambda: self._on_units_changed(scalex=True))
474 if self.yaxis is not None:
475 self._ycid = self.yaxis.callbacks.connect(
476 'units finalize', lambda: self._on_units_changed(scaley=True))
478 self.tick_params(
479 top=rcParams['xtick.top'] and rcParams['xtick.minor.top'],
480 bottom=rcParams['xtick.bottom'] and rcParams['xtick.minor.bottom'],
481 labeltop=(rcParams['xtick.labeltop'] and
482 rcParams['xtick.minor.top']),
483 labelbottom=(rcParams['xtick.labelbottom'] and
484 rcParams['xtick.minor.bottom']),
485 left=rcParams['ytick.left'] and rcParams['ytick.minor.left'],
486 right=rcParams['ytick.right'] and rcParams['ytick.minor.right'],
487 labelleft=(rcParams['ytick.labelleft'] and
488 rcParams['ytick.minor.left']),
489 labelright=(rcParams['ytick.labelright'] and
490 rcParams['ytick.minor.right']),
491 which='minor')
493 self.tick_params(
494 top=rcParams['xtick.top'] and rcParams['xtick.major.top'],
495 bottom=rcParams['xtick.bottom'] and rcParams['xtick.major.bottom'],
496 labeltop=(rcParams['xtick.labeltop'] and
497 rcParams['xtick.major.top']),
498 labelbottom=(rcParams['xtick.labelbottom'] and
499 rcParams['xtick.major.bottom']),
500 left=rcParams['ytick.left'] and rcParams['ytick.major.left'],
501 right=rcParams['ytick.right'] and rcParams['ytick.major.right'],
502 labelleft=(rcParams['ytick.labelleft'] and
503 rcParams['ytick.major.left']),
504 labelright=(rcParams['ytick.labelright'] and
505 rcParams['ytick.major.right']),
506 which='major')
508 self._layoutbox = None
509 self._poslayoutbox = None
511 def __getstate__(self):
512 # The renderer should be re-created by the figure, and then cached at
513 # that point.
514 state = super().__getstate__()
515 for key in ['_layoutbox', '_poslayoutbox']:
516 state[key] = None
517 # Prune the sharing & twinning info to only contain the current group.
518 for grouper_name in [
519 '_shared_x_axes', '_shared_y_axes', '_twinned_axes']:
520 grouper = getattr(self, grouper_name)
521 state[grouper_name] = (grouper.get_siblings(self)
522 if self in grouper else None)
523 return state
525 def __setstate__(self, state):
526 # Merge the grouping info back into the global groupers.
527 for grouper_name in [
528 '_shared_x_axes', '_shared_y_axes', '_twinned_axes']:
529 siblings = state.pop(grouper_name)
530 if siblings:
531 getattr(self, grouper_name).join(*siblings)
532 self.__dict__ = state
533 self._stale = True
535 def get_window_extent(self, *args, **kwargs):
536 """
537 Return the axes bounding box in display space; *args* and *kwargs*
538 are empty.
540 This bounding box does not include the spines, ticks, ticklables,
541 or other labels. For a bounding box including these elements use
542 `~matplotlib.axes.Axes.get_tightbbox`.
544 See Also
545 --------
546 matplotlib.axes.Axes.get_tightbbox
547 matplotlib.axis.Axis.get_tightbbox
548 matplotlib.spines.get_window_extent
550 """
551 return self.bbox
553 def _init_axis(self):
554 "move this out of __init__ because non-separable axes don't use it"
555 self.xaxis = maxis.XAxis(self)
556 self.spines['bottom'].register_axis(self.xaxis)
557 self.spines['top'].register_axis(self.xaxis)
558 self.yaxis = maxis.YAxis(self)
559 self.spines['left'].register_axis(self.yaxis)
560 self.spines['right'].register_axis(self.yaxis)
561 self._update_transScale()
563 def set_figure(self, fig):
564 """
565 Set the `.Figure` for this `.Axes`.
567 Parameters
568 ----------
569 fig : `.Figure`
570 """
571 martist.Artist.set_figure(self, fig)
573 self.bbox = mtransforms.TransformedBbox(self._position,
574 fig.transFigure)
575 # these will be updated later as data is added
576 self.dataLim = mtransforms.Bbox.null()
577 self._viewLim = mtransforms.Bbox.unit()
578 self.transScale = mtransforms.TransformWrapper(
579 mtransforms.IdentityTransform())
581 self._set_lim_and_transforms()
583 def _unstale_viewLim(self):
584 # We should arrange to store this information once per share-group
585 # instead of on every axis.
586 scalex = any(ax._stale_viewlim_x
587 for ax in self._shared_x_axes.get_siblings(self))
588 scaley = any(ax._stale_viewlim_y
589 for ax in self._shared_y_axes.get_siblings(self))
590 if scalex or scaley:
591 for ax in self._shared_x_axes.get_siblings(self):
592 ax._stale_viewlim_x = False
593 for ax in self._shared_y_axes.get_siblings(self):
594 ax._stale_viewlim_y = False
595 self.autoscale_view(scalex=scalex, scaley=scaley)
597 @property
598 def viewLim(self):
599 self._unstale_viewLim()
600 return self._viewLim
602 # API could be better, right now this is just to match the old calls to
603 # autoscale_view() after each plotting method.
604 def _request_autoscale_view(self, tight=None, scalex=True, scaley=True):
605 if tight is not None:
606 self._tight = tight
607 if scalex:
608 self._stale_viewlim_x = True # Else keep old state.
609 if scaley:
610 self._stale_viewlim_y = True
612 def _set_lim_and_transforms(self):
613 """
614 Set the *_xaxis_transform*, *_yaxis_transform*, *transScale*,
615 *transData*, *transLimits* and *transAxes* transformations.
617 .. note::
619 This method is primarily used by rectilinear projections of the
620 `~matplotlib.axes.Axes` class, and is meant to be overridden by
621 new kinds of projection axes that need different transformations
622 and limits. (See `~matplotlib.projections.polar.PolarAxes` for an
623 example.)
624 """
625 self.transAxes = mtransforms.BboxTransformTo(self.bbox)
627 # Transforms the x and y axis separately by a scale factor.
628 # It is assumed that this part will have non-linear components
629 # (e.g., for a log scale).
630 self.transScale = mtransforms.TransformWrapper(
631 mtransforms.IdentityTransform())
633 # An affine transformation on the data, generally to limit the
634 # range of the axes
635 self.transLimits = mtransforms.BboxTransformFrom(
636 mtransforms.TransformedBbox(self._viewLim, self.transScale))
638 # The parentheses are important for efficiency here -- they
639 # group the last two (which are usually affines) separately
640 # from the first (which, with log-scaling can be non-affine).
641 self.transData = self.transScale + (self.transLimits + self.transAxes)
643 self._xaxis_transform = mtransforms.blended_transform_factory(
644 self.transData, self.transAxes)
645 self._yaxis_transform = mtransforms.blended_transform_factory(
646 self.transAxes, self.transData)
648 def get_xaxis_transform(self, which='grid'):
649 """
650 Get the transformation used for drawing x-axis labels, ticks
651 and gridlines. The x-direction is in data coordinates and the
652 y-direction is in axis coordinates.
654 .. note::
656 This transformation is primarily used by the
657 `~matplotlib.axis.Axis` class, and is meant to be
658 overridden by new kinds of projections that may need to
659 place axis elements in different locations.
660 """
661 if which == 'grid':
662 return self._xaxis_transform
663 elif which == 'tick1':
664 # for cartesian projection, this is bottom spine
665 return self.spines['bottom'].get_spine_transform()
666 elif which == 'tick2':
667 # for cartesian projection, this is top spine
668 return self.spines['top'].get_spine_transform()
669 else:
670 raise ValueError('unknown value for which')
672 def get_xaxis_text1_transform(self, pad_points):
673 """
674 Returns
675 -------
676 transform : Transform
677 The transform used for drawing x-axis labels, which will add
678 *pad_points* of padding (in points) between the axes and the label.
679 The x-direction is in data coordinates and the y-direction is in
680 axis corrdinates
681 valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
682 The text vertical alignment.
683 halign : {'center', 'left', 'right'}
684 The text horizontal alignment.
686 Notes
687 -----
688 This transformation is primarily used by the `~matplotlib.axis.Axis`
689 class, and is meant to be overridden by new kinds of projections that
690 may need to place axis elements in different locations.
691 """
692 labels_align = rcParams["xtick.alignment"]
693 return (self.get_xaxis_transform(which='tick1') +
694 mtransforms.ScaledTranslation(0, -1 * pad_points / 72,
695 self.figure.dpi_scale_trans),
696 "top", labels_align)
698 def get_xaxis_text2_transform(self, pad_points):
699 """
700 Returns
701 -------
702 transform : Transform
703 The transform used for drawing secondary x-axis labels, which will
704 add *pad_points* of padding (in points) between the axes and the
705 label. The x-direction is in data coordinates and the y-direction
706 is in axis corrdinates
707 valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
708 The text vertical alignment.
709 halign : {'center', 'left', 'right'}
710 The text horizontal alignment.
712 Notes
713 -----
714 This transformation is primarily used by the `~matplotlib.axis.Axis`
715 class, and is meant to be overridden by new kinds of projections that
716 may need to place axis elements in different locations.
717 """
718 labels_align = rcParams["xtick.alignment"]
719 return (self.get_xaxis_transform(which='tick2') +
720 mtransforms.ScaledTranslation(0, pad_points / 72,
721 self.figure.dpi_scale_trans),
722 "bottom", labels_align)
724 def get_yaxis_transform(self, which='grid'):
725 """
726 Get the transformation used for drawing y-axis labels, ticks
727 and gridlines. The x-direction is in axis coordinates and the
728 y-direction is in data coordinates.
730 .. note::
732 This transformation is primarily used by the
733 `~matplotlib.axis.Axis` class, and is meant to be
734 overridden by new kinds of projections that may need to
735 place axis elements in different locations.
736 """
737 if which == 'grid':
738 return self._yaxis_transform
739 elif which == 'tick1':
740 # for cartesian projection, this is bottom spine
741 return self.spines['left'].get_spine_transform()
742 elif which == 'tick2':
743 # for cartesian projection, this is top spine
744 return self.spines['right'].get_spine_transform()
745 else:
746 raise ValueError('unknown value for which')
748 def get_yaxis_text1_transform(self, pad_points):
749 """
750 Returns
751 -------
752 transform : Transform
753 The transform used for drawing y-axis labels, which will add
754 *pad_points* of padding (in points) between the axes and the label.
755 The x-direction is in axis coordinates and the y-direction is in
756 data corrdinates
757 valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
758 The text vertical alignment.
759 halign : {'center', 'left', 'right'}
760 The text horizontal alignment.
762 Notes
763 -----
764 This transformation is primarily used by the `~matplotlib.axis.Axis`
765 class, and is meant to be overridden by new kinds of projections that
766 may need to place axis elements in different locations.
767 """
768 labels_align = rcParams["ytick.alignment"]
769 return (self.get_yaxis_transform(which='tick1') +
770 mtransforms.ScaledTranslation(-1 * pad_points / 72, 0,
771 self.figure.dpi_scale_trans),
772 labels_align, "right")
774 def get_yaxis_text2_transform(self, pad_points):
775 """
776 Returns
777 -------
778 transform : Transform
779 The transform used for drawing secondart y-axis labels, which will
780 add *pad_points* of padding (in points) between the axes and the
781 label. The x-direction is in axis coordinates and the y-direction
782 is in data corrdinates
783 valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
784 The text vertical alignment.
785 halign : {'center', 'left', 'right'}
786 The text horizontal alignment.
788 Notes
789 -----
790 This transformation is primarily used by the `~matplotlib.axis.Axis`
791 class, and is meant to be overridden by new kinds of projections that
792 may need to place axis elements in different locations.
793 """
794 labels_align = rcParams["ytick.alignment"]
795 return (self.get_yaxis_transform(which='tick2') +
796 mtransforms.ScaledTranslation(pad_points / 72, 0,
797 self.figure.dpi_scale_trans),
798 labels_align, "left")
800 def _update_transScale(self):
801 self.transScale.set(
802 mtransforms.blended_transform_factory(
803 self.xaxis.get_transform(), self.yaxis.get_transform()))
804 for line in getattr(self, "lines", []): # Not set during init.
805 try:
806 line._transformed_path.invalidate()
807 except AttributeError:
808 pass
810 def get_position(self, original=False):
811 """
812 Get a copy of the axes rectangle as a `.Bbox`.
814 Parameters
815 ----------
816 original : bool
817 If ``True``, return the original position. Otherwise return the
818 active position. For an explanation of the positions see
819 `.set_position`.
821 Returns
822 -------
823 pos : `.Bbox`
825 """
826 if original:
827 return self._originalPosition.frozen()
828 else:
829 locator = self.get_axes_locator()
830 if not locator:
831 self.apply_aspect()
832 return self._position.frozen()
834 def set_position(self, pos, which='both'):
835 """
836 Set the axes position.
838 Axes have two position attributes. The 'original' position is the
839 position allocated for the Axes. The 'active' position is the
840 position the Axes is actually drawn at. These positions are usually
841 the same unless a fixed aspect is set to the Axes. See `.set_aspect`
842 for details.
844 Parameters
845 ----------
846 pos : [left, bottom, width, height] or `~matplotlib.transforms.Bbox`
847 The new position of the in `.Figure` coordinates.
849 which : {'both', 'active', 'original'}, optional
850 Determines which position variables to change.
852 """
853 self._set_position(pos, which=which)
854 # because this is being called externally to the library we
855 # zero the constrained layout parts.
856 self._layoutbox = None
857 self._poslayoutbox = None
859 def _set_position(self, pos, which='both'):
860 """
861 private version of set_position. Call this internally
862 to get the same functionality of `get_position`, but not
863 to take the axis out of the constrained_layout
864 hierarchy.
865 """
866 if not isinstance(pos, mtransforms.BboxBase):
867 pos = mtransforms.Bbox.from_bounds(*pos)
868 for ax in self._twinned_axes.get_siblings(self):
869 if which in ('both', 'active'):
870 ax._position.set(pos)
871 if which in ('both', 'original'):
872 ax._originalPosition.set(pos)
873 self.stale = True
875 def reset_position(self):
876 """
877 Reset the active position to the original position.
879 This resets the a possible position change due to aspect constraints.
880 For an explanation of the positions see `.set_position`.
881 """
882 for ax in self._twinned_axes.get_siblings(self):
883 pos = ax.get_position(original=True)
884 ax.set_position(pos, which='active')
886 def set_axes_locator(self, locator):
887 """
888 Set the axes locator.
890 Parameters
891 ----------
892 locator : Callable[[Axes, Renderer], Bbox]
893 """
894 self._axes_locator = locator
895 self.stale = True
897 def get_axes_locator(self):
898 """
899 Return the axes_locator.
900 """
901 return self._axes_locator
903 def _set_artist_props(self, a):
904 """set the boilerplate props for artists added to axes"""
905 a.set_figure(self.figure)
906 if not a.is_transform_set():
907 a.set_transform(self.transData)
909 a.axes = self
910 if a.mouseover:
911 self._mouseover_set.add(a)
913 def _gen_axes_patch(self):
914 """
915 Returns
916 -------
917 Patch
918 The patch used to draw the background of the axes. It is also used
919 as the clipping path for any data elements on the axes.
921 In the standard axes, this is a rectangle, but in other projections
922 it may not be.
924 Notes
925 -----
926 Intended to be overridden by new projection types.
927 """
928 return mpatches.Rectangle((0.0, 0.0), 1.0, 1.0)
930 def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
931 """
932 Returns
933 -------
934 dict
935 Mapping of spine names to `Line2D` or `Patch` instances that are
936 used to draw axes spines.
938 In the standard axes, spines are single line segments, but in other
939 projections they may not be.
941 Notes
942 -----
943 Intended to be overridden by new projection types.
944 """
945 return OrderedDict((side, mspines.Spine.linear_spine(self, side))
946 for side in ['left', 'right', 'bottom', 'top'])
948 def cla(self):
949 """Clear the current axes."""
950 # Note: this is called by Axes.__init__()
952 # stash the current visibility state
953 if hasattr(self, 'patch'):
954 patch_visible = self.patch.get_visible()
955 else:
956 patch_visible = True
958 xaxis_visible = self.xaxis.get_visible()
959 yaxis_visible = self.yaxis.get_visible()
961 self.xaxis.cla()
962 self.yaxis.cla()
964 for name, spine in self.spines.items():
965 spine.cla()
967 self.ignore_existing_data_limits = True
968 self.callbacks = cbook.CallbackRegistry()
970 if self._sharex is not None:
971 # major and minor are axis.Ticker class instances with
972 # locator and formatter attributes
973 self.xaxis.major = self._sharex.xaxis.major
974 self.xaxis.minor = self._sharex.xaxis.minor
975 x0, x1 = self._sharex.get_xlim()
976 self.set_xlim(x0, x1, emit=False,
977 auto=self._sharex.get_autoscalex_on())
978 self.xaxis._scale = self._sharex.xaxis._scale
979 else:
980 self.xaxis._set_scale('linear')
981 try:
982 self.set_xlim(0, 1)
983 except TypeError:
984 pass
986 if self._sharey is not None:
987 self.yaxis.major = self._sharey.yaxis.major
988 self.yaxis.minor = self._sharey.yaxis.minor
989 y0, y1 = self._sharey.get_ylim()
990 self.set_ylim(y0, y1, emit=False,
991 auto=self._sharey.get_autoscaley_on())
992 self.yaxis._scale = self._sharey.yaxis._scale
993 else:
994 self.yaxis._set_scale('linear')
995 try:
996 self.set_ylim(0, 1)
997 except TypeError:
998 pass
999 # update the minor locator for x and y axis based on rcParams
1000 if rcParams['xtick.minor.visible']:
1001 self.xaxis.set_minor_locator(mticker.AutoMinorLocator())
1003 if rcParams['ytick.minor.visible']:
1004 self.yaxis.set_minor_locator(mticker.AutoMinorLocator())
1006 if self._sharex is None:
1007 self._autoscaleXon = True
1008 if self._sharey is None:
1009 self._autoscaleYon = True
1010 self._xmargin = rcParams['axes.xmargin']
1011 self._ymargin = rcParams['axes.ymargin']
1012 self._tight = None
1013 self._use_sticky_edges = True
1014 self._update_transScale() # needed?
1016 self._get_lines = _process_plot_var_args(self)
1017 self._get_patches_for_fill = _process_plot_var_args(self, 'fill')
1019 self._gridOn = rcParams['axes.grid']
1020 self.lines = []
1021 self.patches = []
1022 self.texts = []
1023 self.tables = []
1024 self.artists = []
1025 self.images = []
1026 self._mouseover_set = _OrderedSet()
1027 self.child_axes = []
1028 self._current_image = None # strictly for pyplot via _sci, _gci
1029 self.legend_ = None
1030 self.collections = [] # collection.Collection instances
1031 self.containers = []
1033 self.grid(False) # Disable grid on init to use rcParameter
1034 self.grid(self._gridOn, which=rcParams['axes.grid.which'],
1035 axis=rcParams['axes.grid.axis'])
1036 props = font_manager.FontProperties(
1037 size=rcParams['axes.titlesize'],
1038 weight=rcParams['axes.titleweight'])
1040 self.title = mtext.Text(
1041 x=0.5, y=1.0, text='',
1042 fontproperties=props,
1043 verticalalignment='baseline',
1044 horizontalalignment='center',
1045 )
1046 self._left_title = mtext.Text(
1047 x=0.0, y=1.0, text='',
1048 fontproperties=props.copy(),
1049 verticalalignment='baseline',
1050 horizontalalignment='left', )
1051 self._right_title = mtext.Text(
1052 x=1.0, y=1.0, text='',
1053 fontproperties=props.copy(),
1054 verticalalignment='baseline',
1055 horizontalalignment='right',
1056 )
1057 title_offset_points = rcParams['axes.titlepad']
1058 # refactor this out so it can be called in ax.set_title if
1059 # pad argument used...
1060 self._set_title_offset_trans(title_offset_points)
1061 # determine if the title position has been set manually:
1062 self._autotitlepos = None
1064 for _title in (self.title, self._left_title, self._right_title):
1065 self._set_artist_props(_title)
1067 # The patch draws the background of the axes. We want this to be below
1068 # the other artists. We use the frame to draw the edges so we are
1069 # setting the edgecolor to None.
1070 self.patch = self._gen_axes_patch()
1071 self.patch.set_figure(self.figure)
1072 self.patch.set_facecolor(self._facecolor)
1073 self.patch.set_edgecolor('None')
1074 self.patch.set_linewidth(0)
1075 self.patch.set_transform(self.transAxes)
1077 self.set_axis_on()
1079 self.xaxis.set_clip_path(self.patch)
1080 self.yaxis.set_clip_path(self.patch)
1082 self._shared_x_axes.clean()
1083 self._shared_y_axes.clean()
1084 if self._sharex:
1085 self.xaxis.set_visible(xaxis_visible)
1086 self.patch.set_visible(patch_visible)
1088 if self._sharey:
1089 self.yaxis.set_visible(yaxis_visible)
1090 self.patch.set_visible(patch_visible)
1092 self.stale = True
1094 def clear(self):
1095 """Clear the axes."""
1096 self.cla()
1098 def get_facecolor(self):
1099 """Get the facecolor of the Axes."""
1100 return self.patch.get_facecolor()
1101 get_fc = get_facecolor
1103 def set_facecolor(self, color):
1104 """
1105 Set the facecolor of the Axes.
1107 Parameters
1108 ----------
1109 color : color
1110 """
1111 self._facecolor = color
1112 self.stale = True
1113 return self.patch.set_facecolor(color)
1114 set_fc = set_facecolor
1116 def _set_title_offset_trans(self, title_offset_points):
1117 """
1118 Set the offset for the title either from rcParams['axes.titlepad']
1119 or from set_title kwarg ``pad``.
1120 """
1121 self.titleOffsetTrans = mtransforms.ScaledTranslation(
1122 0.0, title_offset_points / 72,
1123 self.figure.dpi_scale_trans)
1124 for _title in (self.title, self._left_title, self._right_title):
1125 _title.set_transform(self.transAxes + self.titleOffsetTrans)
1126 _title.set_clip_box(None)
1128 def set_prop_cycle(self, *args, **kwargs):
1129 """
1130 Set the property cycle of the Axes.
1132 The property cycle controls the style properties such as color,
1133 marker and linestyle of future plot commands. The style properties
1134 of data already added to the Axes are not modified.
1136 Call signatures::
1138 set_prop_cycle(cycler)
1139 set_prop_cycle(label=values[, label2=values2[, ...]])
1140 set_prop_cycle(label, values)
1142 Form 1 sets given `~cycler.Cycler` object.
1144 Form 2 creates a `~cycler.Cycler` which cycles over one or more
1145 properties simultaneously and set it as the property cycle of the
1146 axes. If multiple properties are given, their value lists must have
1147 the same length. This is just a shortcut for explicitly creating a
1148 cycler and passing it to the function, i.e. it's short for
1149 ``set_prop_cycle(cycler(label=values label2=values2, ...))``.
1151 Form 3 creates a `~cycler.Cycler` for a single property and set it
1152 as the property cycle of the axes. This form exists for compatibility
1153 with the original `cycler.cycler` interface. Its use is discouraged
1154 in favor of the kwarg form, i.e. ``set_prop_cycle(label=values)``.
1156 Parameters
1157 ----------
1158 cycler : Cycler
1159 Set the given Cycler. *None* resets to the cycle defined by the
1160 current style.
1162 label : str
1163 The property key. Must be a valid `.Artist` property.
1164 For example, 'color' or 'linestyle'. Aliases are allowed,
1165 such as 'c' for 'color' and 'lw' for 'linewidth'.
1167 values : iterable
1168 Finite-length iterable of the property values. These values
1169 are validated and will raise a ValueError if invalid.
1171 Examples
1172 --------
1173 Setting the property cycle for a single property:
1175 >>> ax.set_prop_cycle(color=['red', 'green', 'blue'])
1177 Setting the property cycle for simultaneously cycling over multiple
1178 properties (e.g. red circle, green plus, blue cross):
1180 >>> ax.set_prop_cycle(color=['red', 'green', 'blue'],
1181 ... marker=['o', '+', 'x'])
1183 See Also
1184 --------
1185 matplotlib.rcsetup.cycler
1186 Convenience function for creating validated cyclers for properties.
1187 cycler.cycler
1188 The original function for creating unvalidated cyclers.
1190 """
1191 if args and kwargs:
1192 raise TypeError("Cannot supply both positional and keyword "
1193 "arguments to this method.")
1194 # Can't do `args == (None,)` as that crashes cycler.
1195 if len(args) == 1 and args[0] is None:
1196 prop_cycle = None
1197 else:
1198 prop_cycle = cycler(*args, **kwargs)
1199 self._get_lines.set_prop_cycle(prop_cycle)
1200 self._get_patches_for_fill.set_prop_cycle(prop_cycle)
1202 def get_aspect(self):
1203 return self._aspect
1205 def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
1206 """
1207 Set the aspect of the axis scaling, i.e. the ratio of y-unit to x-unit.
1209 Parameters
1210 ----------
1211 aspect : {'auto', 'equal'} or num
1212 Possible values:
1214 ======== ================================================
1215 value description
1216 ======== ================================================
1217 'auto' automatic; fill the position rectangle with data
1218 'equal' same scaling from data to plot units for x and y
1219 num a circle will be stretched such that the height
1220 is num times the width. aspect=1 is the same as
1221 aspect='equal'.
1222 ======== ================================================
1224 adjustable : None or {'box', 'datalim'}, optional
1225 If not ``None``, this defines which parameter will be adjusted to
1226 meet the required aspect. See `.set_adjustable` for further
1227 details.
1229 anchor : None or str or 2-tuple of float, optional
1230 If not ``None``, this defines where the Axes will be drawn if there
1231 is extra space due to aspect constraints. The most common way to
1232 to specify the anchor are abbreviations of cardinal directions:
1234 ===== =====================
1235 value description
1236 ===== =====================
1237 'C' centered
1238 'SW' lower left corner
1239 'S' middle of bottom edge
1240 'SE' lower right corner
1241 etc.
1242 ===== =====================
1244 See `.set_anchor` for further details.
1246 share : bool, optional
1247 If ``True``, apply the settings to all shared Axes.
1248 Default is ``False``.
1250 See Also
1251 --------
1252 matplotlib.axes.Axes.set_adjustable
1253 defining the parameter to adjust in order to meet the required
1254 aspect.
1255 matplotlib.axes.Axes.set_anchor
1256 defining the position in case of extra space.
1257 """
1258 if not (cbook._str_equal(aspect, 'equal')
1259 or cbook._str_equal(aspect, 'auto')):
1260 aspect = float(aspect) # raise ValueError if necessary
1262 if (not cbook._str_equal(aspect, 'auto')) and self.name == '3d':
1263 raise NotImplementedError(
1264 'It is not currently possible to manually set the aspect '
1265 'on 3D axes')
1267 if share:
1268 axes = {*self._shared_x_axes.get_siblings(self),
1269 *self._shared_y_axes.get_siblings(self)}
1270 else:
1271 axes = [self]
1273 for ax in axes:
1274 ax._aspect = aspect
1276 if adjustable is None:
1277 adjustable = self._adjustable
1278 self.set_adjustable(adjustable, share=share) # Handle sharing.
1280 if anchor is not None:
1281 self.set_anchor(anchor, share=share)
1282 self.stale = True
1284 def get_adjustable(self):
1285 return self._adjustable
1287 def set_adjustable(self, adjustable, share=False):
1288 """
1289 Define which parameter the Axes will change to achieve a given aspect.
1291 Parameters
1292 ----------
1293 adjustable : {'box', 'datalim'}
1294 If 'box', change the physical dimensions of the Axes.
1295 If 'datalim', change the ``x`` or ``y`` data limits.
1297 share : bool, optional
1298 If ``True``, apply the settings to all shared Axes.
1299 Default is ``False``.
1301 See Also
1302 --------
1303 matplotlib.axes.Axes.set_aspect
1304 for a description of aspect handling.
1306 Notes
1307 -----
1308 Shared Axes (of which twinned Axes are a special case)
1309 impose restrictions on how aspect ratios can be imposed.
1310 For twinned Axes, use 'datalim'. For Axes that share both
1311 x and y, use 'box'. Otherwise, either 'datalim' or 'box'
1312 may be used. These limitations are partly a requirement
1313 to avoid over-specification, and partly a result of the
1314 particular implementation we are currently using, in
1315 which the adjustments for aspect ratios are done sequentially
1316 and independently on each Axes as it is drawn.
1317 """
1318 cbook._check_in_list(["box", "datalim"], adjustable=adjustable)
1319 if share:
1320 axs = {*self._shared_x_axes.get_siblings(self),
1321 *self._shared_y_axes.get_siblings(self)}
1322 else:
1323 axs = [self]
1324 if (adjustable == "datalim"
1325 and any(getattr(ax.get_data_ratio, "__func__", None)
1326 != _AxesBase.get_data_ratio
1327 for ax in axs)):
1328 # Limits adjustment by apply_aspect assumes that the axes' aspect
1329 # ratio can be computed from the data limits and scales.
1330 raise ValueError("Cannot set axes adjustable to 'datalim' for "
1331 "Axes which override 'get_data_ratio'")
1332 for ax in axs:
1333 ax._adjustable = adjustable
1334 self.stale = True
1336 def get_anchor(self):
1337 """
1338 Get the anchor location.
1340 See Also
1341 --------
1342 matplotlib.axes.Axes.set_anchor
1343 for a description of the anchor.
1344 matplotlib.axes.Axes.set_aspect
1345 for a description of aspect handling.
1346 """
1347 return self._anchor
1349 def set_anchor(self, anchor, share=False):
1350 """
1351 Define the anchor location.
1353 The actual drawing area (active position) of the Axes may be smaller
1354 than the Bbox (original position) when a fixed aspect is required. The
1355 anchor defines where the drawing area will be located within the
1356 available space.
1358 Parameters
1359 ----------
1360 anchor : 2-tuple of floats or {'C', 'SW', 'S', 'SE', ...}
1361 The anchor position may be either:
1363 - a sequence (*cx*, *cy*). *cx* and *cy* may range from 0
1364 to 1, where 0 is left or bottom and 1 is right or top.
1366 - a string using cardinal directions as abbreviation:
1368 - 'C' for centered
1369 - 'S' (south) for bottom-center
1370 - 'SW' (south west) for bottom-left
1371 - etc.
1373 Here is an overview of the possible positions:
1375 +------+------+------+
1376 | 'NW' | 'N' | 'NE' |
1377 +------+------+------+
1378 | 'W' | 'C' | 'E' |
1379 +------+------+------+
1380 | 'SW' | 'S' | 'SE' |
1381 +------+------+------+
1383 share : bool, optional
1384 If ``True``, apply the settings to all shared Axes.
1385 Default is ``False``.
1387 See Also
1388 --------
1389 matplotlib.axes.Axes.set_aspect
1390 for a description of aspect handling.
1391 """
1392 if not (anchor in mtransforms.Bbox.coefs or len(anchor) == 2):
1393 raise ValueError('argument must be among %s' %
1394 ', '.join(mtransforms.Bbox.coefs))
1395 if share:
1396 axes = {*self._shared_x_axes.get_siblings(self),
1397 *self._shared_y_axes.get_siblings(self)}
1398 else:
1399 axes = [self]
1400 for ax in axes:
1401 ax._anchor = anchor
1403 self.stale = True
1405 def get_data_ratio(self):
1406 """
1407 Return the aspect ratio of the scaled data.
1409 Notes
1410 -----
1411 This method is intended to be overridden by new projection types.
1412 """
1413 txmin, txmax = self.xaxis.get_transform().transform(self.get_xbound())
1414 tymin, tymax = self.yaxis.get_transform().transform(self.get_ybound())
1415 xsize = max(abs(txmax - txmin), 1e-30)
1416 ysize = max(abs(tymax - tymin), 1e-30)
1417 return ysize / xsize
1419 @cbook.deprecated("3.2")
1420 def get_data_ratio_log(self):
1421 """
1422 Return the aspect ratio of the raw data in log scale.
1424 Notes
1425 -----
1426 Will be used when both axis are in log scale.
1427 """
1428 xmin, xmax = self.get_xbound()
1429 ymin, ymax = self.get_ybound()
1431 xsize = max(abs(math.log10(xmax) - math.log10(xmin)), 1e-30)
1432 ysize = max(abs(math.log10(ymax) - math.log10(ymin)), 1e-30)
1434 return ysize / xsize
1436 def apply_aspect(self, position=None):
1437 """
1438 Adjust the Axes for a specified data aspect ratio.
1440 Depending on `.get_adjustable` this will modify either the Axes box
1441 (position) or the view limits. In the former case, `.get_anchor`
1442 will affect the position.
1444 Notes
1445 -----
1446 This is called automatically when each Axes is drawn. You may need
1447 to call it yourself if you need to update the Axes position and/or
1448 view limits before the Figure is drawn.
1450 See Also
1451 --------
1452 matplotlib.axes.Axes.set_aspect
1453 for a description of aspect ratio handling.
1454 matplotlib.axes.Axes.set_adjustable
1455 defining the parameter to adjust in order to meet the required
1456 aspect.
1457 matplotlib.axes.Axes.set_anchor
1458 defining the position in case of extra space.
1459 """
1460 if position is None:
1461 position = self.get_position(original=True)
1463 aspect = self.get_aspect()
1465 if aspect == 'auto':
1466 self._set_position(position, which='active')
1467 return
1469 if aspect == 'equal':
1470 aspect = 1
1472 fig_width, fig_height = self.get_figure().get_size_inches()
1473 fig_aspect = fig_height / fig_width
1475 if self._adjustable == 'box':
1476 if self in self._twinned_axes:
1477 raise RuntimeError("Adjustable 'box' is not allowed in a "
1478 "twinned Axes; use 'datalim' instead")
1479 box_aspect = aspect * self.get_data_ratio()
1480 pb = position.frozen()
1481 pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
1482 self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')
1483 return
1485 # self._adjustable == 'datalim'
1487 # reset active to original in case it had been changed by prior use
1488 # of 'box'
1489 self._set_position(position, which='active')
1491 x_trf = self.xaxis.get_transform()
1492 y_trf = self.yaxis.get_transform()
1493 xmin, xmax = x_trf.transform(self.get_xbound())
1494 ymin, ymax = y_trf.transform(self.get_ybound())
1495 xsize = max(abs(xmax - xmin), 1e-30)
1496 ysize = max(abs(ymax - ymin), 1e-30)
1498 l, b, w, h = position.bounds
1499 box_aspect = fig_aspect * (h / w)
1500 data_ratio = box_aspect / aspect
1502 y_expander = data_ratio * xsize / ysize - 1
1503 # If y_expander > 0, the dy/dx viewLim ratio needs to increase
1504 if abs(y_expander) < 0.005:
1505 return
1507 dL = self.dataLim
1508 x0, x1 = x_trf.transform(dL.intervalx)
1509 y0, y1 = y_trf.transform(dL.intervaly)
1510 xr = 1.05 * (x1 - x0)
1511 yr = 1.05 * (y1 - y0)
1513 xmarg = xsize - xr
1514 ymarg = ysize - yr
1515 Ysize = data_ratio * xsize
1516 Xsize = ysize / data_ratio
1517 Xmarg = Xsize - xr
1518 Ymarg = Ysize - yr
1519 # Setting these targets to, e.g., 0.05*xr does not seem to help.
1520 xm = 0
1521 ym = 0
1523 shared_x = self in self._shared_x_axes
1524 shared_y = self in self._shared_y_axes
1525 # Not sure whether we need this check:
1526 if shared_x and shared_y:
1527 raise RuntimeError("adjustable='datalim' is not allowed when both "
1528 "axes are shared")
1530 # If y is shared, then we are only allowed to change x, etc.
1531 if shared_y:
1532 adjust_y = False
1533 else:
1534 if xmarg > xm and ymarg > ym:
1535 adjy = ((Ymarg > 0 and y_expander < 0) or
1536 (Xmarg < 0 and y_expander > 0))
1537 else:
1538 adjy = y_expander > 0
1539 adjust_y = shared_x or adjy # (Ymarg > xmarg)
1541 if adjust_y:
1542 yc = 0.5 * (ymin + ymax)
1543 y0 = yc - Ysize / 2.0
1544 y1 = yc + Ysize / 2.0
1545 self.set_ybound(y_trf.inverted().transform([y0, y1]))
1546 else:
1547 xc = 0.5 * (xmin + xmax)
1548 x0 = xc - Xsize / 2.0
1549 x1 = xc + Xsize / 2.0
1550 self.set_xbound(x_trf.inverted().transform([x0, x1]))
1552 def axis(self, *args, emit=True, **kwargs):
1553 """
1554 Convenience method to get or set some axis properties.
1556 Call signatures::
1558 xmin, xmax, ymin, ymax = axis()
1559 xmin, xmax, ymin, ymax = axis([xmin, xmax, ymin, ymax])
1560 xmin, xmax, ymin, ymax = axis(option)
1561 xmin, xmax, ymin, ymax = axis(**kwargs)
1563 Parameters
1564 ----------
1565 xmin, xmax, ymin, ymax : float, optional
1566 The axis limits to be set. This can also be achieved using ::
1568 ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
1570 option : bool or str
1571 If a bool, turns axis lines and labels on or off. If a string,
1572 possible values are:
1574 ======== ==========================================================
1575 Value Description
1576 ======== ==========================================================
1577 'on' Turn on axis lines and labels. Same as ``True``.
1578 'off' Turn off axis lines and labels. Same as ``False``.
1579 'equal' Set equal scaling (i.e., make circles circular) by
1580 changing axis limits. This is the same as
1581 ``ax.set_aspect('equal', adjustable='datalim')``.
1582 Explicit data limits may not be respected in this case.
1583 'scaled' Set equal scaling (i.e., make circles circular) by
1584 changing dimensions of the plot box. This is the same as
1585 ``ax.set_aspect('equal', adjustable='box', anchor='C')``.
1586 Additionally, further autoscaling will be disabled.
1587 'tight' Set limits just large enough to show all data, then
1588 disable further autoscaling.
1589 'auto' Automatic scaling (fill plot box with data).
1590 'normal' Same as 'auto'; deprecated.
1591 'image' 'scaled' with axis limits equal to data limits.
1592 'square' Square plot; similar to 'scaled', but initially forcing
1593 ``xmax-xmin == ymax-ymin``.
1594 ======== ==========================================================
1596 emit : bool, optional, default *True*
1597 Whether observers are notified of the axis limit change.
1598 This option is passed on to `~.Axes.set_xlim` and
1599 `~.Axes.set_ylim`.
1601 Returns
1602 -------
1603 xmin, xmax, ymin, ymax : float
1604 The axis limits.
1606 See also
1607 --------
1608 matplotlib.axes.Axes.set_xlim
1609 matplotlib.axes.Axes.set_ylim
1610 """
1611 if len(args) == 1 and isinstance(args[0], (str, bool)):
1612 s = args[0]
1613 if s is True:
1614 s = 'on'
1615 if s is False:
1616 s = 'off'
1617 s = s.lower()
1618 if s == 'on':
1619 self.set_axis_on()
1620 elif s == 'off':
1621 self.set_axis_off()
1622 elif s in ('equal', 'tight', 'scaled', 'normal',
1623 'auto', 'image', 'square'):
1624 if s == 'normal':
1625 cbook.warn_deprecated(
1626 "3.1", message="Passing 'normal' to axis() is "
1627 "deprecated since %(since)s; use 'auto' instead.")
1628 self.set_autoscale_on(True)
1629 self.set_aspect('auto')
1630 self.autoscale_view(tight=False)
1631 # self.apply_aspect()
1632 if s == 'equal':
1633 self.set_aspect('equal', adjustable='datalim')
1634 elif s == 'scaled':
1635 self.set_aspect('equal', adjustable='box', anchor='C')
1636 self.set_autoscale_on(False) # Req. by Mark Bakker
1637 elif s == 'tight':
1638 self.autoscale_view(tight=True)
1639 self.set_autoscale_on(False)
1640 elif s == 'image':
1641 self.autoscale_view(tight=True)
1642 self.set_autoscale_on(False)
1643 self.set_aspect('equal', adjustable='box', anchor='C')
1644 elif s == 'square':
1645 self.set_aspect('equal', adjustable='box', anchor='C')
1646 self.set_autoscale_on(False)
1647 xlim = self.get_xlim()
1648 ylim = self.get_ylim()
1649 edge_size = max(np.diff(xlim), np.diff(ylim))[0]
1650 self.set_xlim([xlim[0], xlim[0] + edge_size],
1651 emit=emit, auto=False)
1652 self.set_ylim([ylim[0], ylim[0] + edge_size],
1653 emit=emit, auto=False)
1654 else:
1655 raise ValueError('Unrecognized string %s to axis; '
1656 'try on or off' % s)
1657 else:
1658 if len(args) >= 1:
1659 if len(args) != 1:
1660 cbook.warn_deprecated(
1661 "3.2", message="Passing more than one positional "
1662 "argument to axis() is deprecated and will raise a "
1663 "TypeError %(removal)s.")
1664 limits = args[0]
1665 try:
1666 xmin, xmax, ymin, ymax = limits
1667 except (TypeError, ValueError):
1668 raise TypeError('the first argument to axis() must be an '
1669 'interable of the form '
1670 '[xmin, xmax, ymin, ymax]')
1671 else:
1672 xmin = kwargs.pop('xmin', None)
1673 xmax = kwargs.pop('xmax', None)
1674 ymin = kwargs.pop('ymin', None)
1675 ymax = kwargs.pop('ymax', None)
1676 xauto = (None # Keep autoscale state as is.
1677 if xmin is None and xmax is None
1678 else False) # Turn off autoscale.
1679 yauto = (None
1680 if ymin is None and ymax is None
1681 else False)
1682 self.set_xlim(xmin, xmax, emit=emit, auto=xauto)
1683 self.set_ylim(ymin, ymax, emit=emit, auto=yauto)
1684 if kwargs:
1685 cbook.warn_deprecated(
1686 "3.1", message="Passing unsupported keyword arguments to "
1687 "axis() will raise a TypeError %(removal)s.")
1688 return (*self.get_xlim(), *self.get_ylim())
1690 def get_legend(self):
1691 """Return the `Legend` instance, or None if no legend is defined."""
1692 return self.legend_
1694 def get_images(self):
1695 """return a list of Axes images contained by the Axes"""
1696 return cbook.silent_list('AxesImage', self.images)
1698 def get_lines(self):
1699 """Return a list of lines contained by the Axes"""
1700 return cbook.silent_list('Line2D', self.lines)
1702 def get_xaxis(self):
1703 """Return the XAxis instance."""
1704 return self.xaxis
1706 def get_xgridlines(self):
1707 """Get the x grid lines as a list of `Line2D` instances."""
1708 return self.xaxis.get_gridlines()
1710 def get_xticklines(self):
1711 """Get the x tick lines as a list of `Line2D` instances."""
1712 return self.xaxis.get_ticklines()
1714 def get_yaxis(self):
1715 """Return the YAxis instance."""
1716 return self.yaxis
1718 def get_ygridlines(self):
1719 """Get the y grid lines as a list of `Line2D` instances."""
1720 return self.yaxis.get_gridlines()
1722 def get_yticklines(self):
1723 """Get the y tick lines as a list of `Line2D` instances."""
1724 return self.yaxis.get_ticklines()
1726 # Adding and tracking artists
1728 def _sci(self, im):
1729 """Set the current image.
1731 This image will be the target of colormap functions like
1732 `~.pyplot.viridis`, and other functions such as `~.pyplot.clim`. The
1733 current image is an attribute of the current axes.
1734 """
1735 if isinstance(im, mpl.contour.ContourSet):
1736 if im.collections[0] not in self.collections:
1737 raise ValueError("ContourSet must be in current Axes")
1738 elif im not in self.images and im not in self.collections:
1739 raise ValueError("Argument must be an image, collection, or "
1740 "ContourSet in this Axes")
1741 self._current_image = im
1743 def _gci(self):
1744 """
1745 Helper for :func:`~matplotlib.pyplot.gci`;
1746 do not use elsewhere.
1747 """
1748 return self._current_image
1750 def has_data(self):
1751 """
1752 Return *True* if any artists have been added to axes.
1754 This should not be used to determine whether the *dataLim*
1755 need to be updated, and may not actually be useful for
1756 anything.
1757 """
1758 return (
1759 len(self.collections) +
1760 len(self.images) +
1761 len(self.lines) +
1762 len(self.patches)) > 0
1764 def add_artist(self, a):
1765 """
1766 Add an `~.Artist` to the axes, and return the artist.
1768 Use `add_artist` only for artists for which there is no dedicated
1769 "add" method; and if necessary, use a method such as `update_datalim`
1770 to manually update the dataLim if the artist is to be included in
1771 autoscaling.
1773 If no ``transform`` has been specified when creating the artist (e.g.
1774 ``artist.get_transform() == None``) then the transform is set to
1775 ``ax.transData``.
1776 """
1777 a.axes = self
1778 self.artists.append(a)
1779 a._remove_method = self.artists.remove
1780 self._set_artist_props(a)
1781 a.set_clip_path(self.patch)
1782 self.stale = True
1783 return a
1785 def add_child_axes(self, ax):
1786 """
1787 Add an `~.AxesBase` to the axes' children; return the child axes.
1789 This is the lowlevel version. See `.axes.Axes.inset_axes`.
1790 """
1792 # normally axes have themselves as the axes, but these need to have
1793 # their parent...
1794 # Need to bypass the getter...
1795 ax._axes = self
1796 ax.stale_callback = martist._stale_axes_callback
1798 self.child_axes.append(ax)
1799 ax._remove_method = self.child_axes.remove
1800 self.stale = True
1801 return ax
1803 def add_collection(self, collection, autolim=True):
1804 """
1805 Add a `~.Collection` to the axes' collections; return the collection.
1806 """
1807 label = collection.get_label()
1808 if not label:
1809 collection.set_label('_collection%d' % len(self.collections))
1810 self.collections.append(collection)
1811 collection._remove_method = self.collections.remove
1812 self._set_artist_props(collection)
1814 if collection.get_clip_path() is None:
1815 collection.set_clip_path(self.patch)
1817 if autolim:
1818 # Make sure viewLim is not stale (mostly to match
1819 # pre-lazy-autoscale behavior, which is not really better).
1820 self._unstale_viewLim()
1821 self.update_datalim(collection.get_datalim(self.transData))
1823 self.stale = True
1824 return collection
1826 def add_image(self, image):
1827 """
1828 Add an `~.AxesImage` to the axes' images; return the image.
1829 """
1830 self._set_artist_props(image)
1831 if not image.get_label():
1832 image.set_label('_image%d' % len(self.images))
1833 self.images.append(image)
1834 image._remove_method = self.images.remove
1835 self.stale = True
1836 return image
1838 def _update_image_limits(self, image):
1839 xmin, xmax, ymin, ymax = image.get_extent()
1840 self.axes.update_datalim(((xmin, ymin), (xmax, ymax)))
1842 def add_line(self, line):
1843 """
1844 Add a `.Line2D` to the axes' lines; return the line.
1845 """
1846 self._set_artist_props(line)
1847 if line.get_clip_path() is None:
1848 line.set_clip_path(self.patch)
1850 self._update_line_limits(line)
1851 if not line.get_label():
1852 line.set_label('_line%d' % len(self.lines))
1853 self.lines.append(line)
1854 line._remove_method = self.lines.remove
1855 self.stale = True
1856 return line
1858 def _add_text(self, txt):
1859 """
1860 Add a `~.Text` to the axes' texts; return the text.
1861 """
1862 self._set_artist_props(txt)
1863 self.texts.append(txt)
1864 txt._remove_method = self.texts.remove
1865 self.stale = True
1866 return txt
1868 def _update_line_limits(self, line):
1869 """
1870 Figures out the data limit of the given line, updating self.dataLim.
1871 """
1872 path = line.get_path()
1873 if path.vertices.size == 0:
1874 return
1876 line_trans = line.get_transform()
1878 if line_trans == self.transData:
1879 data_path = path
1881 elif any(line_trans.contains_branch_seperately(self.transData)):
1882 # identify the transform to go from line's coordinates
1883 # to data coordinates
1884 trans_to_data = line_trans - self.transData
1886 # if transData is affine we can use the cached non-affine component
1887 # of line's path. (since the non-affine part of line_trans is
1888 # entirely encapsulated in trans_to_data).
1889 if self.transData.is_affine:
1890 line_trans_path = line._get_transformed_path()
1891 na_path, _ = line_trans_path.get_transformed_path_and_affine()
1892 data_path = trans_to_data.transform_path_affine(na_path)
1893 else:
1894 data_path = trans_to_data.transform_path(path)
1895 else:
1896 # for backwards compatibility we update the dataLim with the
1897 # coordinate range of the given path, even though the coordinate
1898 # systems are completely different. This may occur in situations
1899 # such as when ax.transAxes is passed through for absolute
1900 # positioning.
1901 data_path = path
1903 if data_path.vertices.size > 0:
1904 updatex, updatey = line_trans.contains_branch_seperately(
1905 self.transData)
1906 self.dataLim.update_from_path(data_path,
1907 self.ignore_existing_data_limits,
1908 updatex=updatex,
1909 updatey=updatey)
1910 self.ignore_existing_data_limits = False
1912 def add_patch(self, p):
1913 """
1914 Add a `~.Patch` to the axes' patches; return the patch.
1915 """
1916 self._set_artist_props(p)
1917 if p.get_clip_path() is None:
1918 p.set_clip_path(self.patch)
1919 self._update_patch_limits(p)
1920 self.patches.append(p)
1921 p._remove_method = self.patches.remove
1922 return p
1924 def _update_patch_limits(self, patch):
1925 """update the data limits for patch *p*"""
1926 # hist can add zero height Rectangles, which is useful to keep
1927 # the bins, counts and patches lined up, but it throws off log
1928 # scaling. We'll ignore rects with zero height or width in
1929 # the auto-scaling
1931 # cannot check for '==0' since unitized data may not compare to zero
1932 # issue #2150 - we update the limits if patch has non zero width
1933 # or height.
1934 if (isinstance(patch, mpatches.Rectangle) and
1935 ((not patch.get_width()) and (not patch.get_height()))):
1936 return
1937 vertices = patch.get_path().vertices
1938 if vertices.size > 0:
1939 xys = patch.get_patch_transform().transform(vertices)
1940 if patch.get_data_transform() != self.transData:
1941 patch_to_data = (patch.get_data_transform() -
1942 self.transData)
1943 xys = patch_to_data.transform(xys)
1945 updatex, updatey = patch.get_transform().\
1946 contains_branch_seperately(self.transData)
1947 self.update_datalim(xys, updatex=updatex,
1948 updatey=updatey)
1950 def add_table(self, tab):
1951 """
1952 Add a `~.Table` to the axes' tables; return the table.
1953 """
1954 self._set_artist_props(tab)
1955 self.tables.append(tab)
1956 tab.set_clip_path(self.patch)
1957 tab._remove_method = self.tables.remove
1958 return tab
1960 def add_container(self, container):
1961 """
1962 Add a `~.Container` to the axes' containers; return the container.
1963 """
1964 label = container.get_label()
1965 if not label:
1966 container.set_label('_container%d' % len(self.containers))
1967 self.containers.append(container)
1968 container._remove_method = self.containers.remove
1969 return container
1971 def _on_units_changed(self, scalex=False, scaley=False):
1972 """
1973 Callback for processing changes to axis units.
1975 Currently requests updates of data limits and view limits.
1976 """
1977 self.relim()
1978 self._request_autoscale_view(scalex=scalex, scaley=scaley)
1980 def relim(self, visible_only=False):
1981 """
1982 Recompute the data limits based on current artists.
1984 At present, `~.Collection` instances are not supported.
1986 Parameters
1987 ----------
1988 visible_only : bool
1989 Whether to exclude invisible artists. Defaults to False.
1990 """
1991 # Collections are deliberately not supported (yet); see
1992 # the TODO note in artists.py.
1993 self.dataLim.ignore(True)
1994 self.dataLim.set_points(mtransforms.Bbox.null().get_points())
1995 self.ignore_existing_data_limits = True
1997 for line in self.lines:
1998 if not visible_only or line.get_visible():
1999 self._update_line_limits(line)
2001 for p in self.patches:
2002 if not visible_only or p.get_visible():
2003 self._update_patch_limits(p)
2005 for image in self.images:
2006 if not visible_only or image.get_visible():
2007 self._update_image_limits(image)
2009 def update_datalim(self, xys, updatex=True, updatey=True):
2010 """
2011 Extend the `~.Axes.dataLim` Bbox to include the given points.
2013 If no data is set currently, the Bbox will ignore its limits and set
2014 the bound to be the bounds of the xydata (*xys*). Otherwise, it will
2015 compute the bounds of the union of its current data and the data in
2016 *xys*.
2018 Parameters
2019 ----------
2020 xys : 2D array-like
2021 The points to include in the data limits Bbox. This can be either
2022 a list of (x, y) tuples or a Nx2 array.
2024 updatex, updatey : bool, optional, default *True*
2025 Whether to update the x/y limits.
2026 """
2027 xys = np.asarray(xys)
2028 if not len(xys):
2029 return
2030 self.dataLim.update_from_data_xy(xys, self.ignore_existing_data_limits,
2031 updatex=updatex, updatey=updatey)
2032 self.ignore_existing_data_limits = False
2034 def update_datalim_bounds(self, bounds):
2035 """
2036 Extend the `~.Axes.datalim` Bbox to include the given
2037 `~matplotlib.transforms.Bbox`.
2039 Parameters
2040 ----------
2041 bounds : `~matplotlib.transforms.Bbox`
2042 """
2043 self.dataLim.set(mtransforms.Bbox.union([self.dataLim, bounds]))
2045 def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
2046 """Look for unit *kwargs* and update the axis instances as necessary"""
2048 def _process_single_axis(data, axis, unit_name, kwargs):
2049 # Return if there's no axis set
2050 if axis is None:
2051 return kwargs
2053 if data is not None:
2054 # We only need to update if there is nothing set yet.
2055 if not axis.have_units():
2056 axis.update_units(data)
2058 # Check for units in the kwargs, and if present update axis
2059 if kwargs is not None:
2060 units = kwargs.pop(unit_name, axis.units)
2061 if self.name == 'polar':
2062 polar_units = {'xunits': 'thetaunits', 'yunits': 'runits'}
2063 units = kwargs.pop(polar_units[unit_name], units)
2065 if units != axis.units:
2066 axis.set_units(units)
2067 # If the units being set imply a different converter,
2068 # we need to update.
2069 if data is not None:
2070 axis.update_units(data)
2071 return kwargs
2073 kwargs = _process_single_axis(xdata, self.xaxis, 'xunits', kwargs)
2074 kwargs = _process_single_axis(ydata, self.yaxis, 'yunits', kwargs)
2075 return kwargs
2077 def in_axes(self, mouseevent):
2078 """
2079 Return *True* if the given *mouseevent* (in display coords)
2080 is in the Axes
2081 """
2082 return self.patch.contains(mouseevent)[0]
2084 def get_autoscale_on(self):
2085 """
2086 Get whether autoscaling is applied for both axes on plot commands
2087 """
2088 return self._autoscaleXon and self._autoscaleYon
2090 def get_autoscalex_on(self):
2091 """
2092 Get whether autoscaling for the x-axis is applied on plot commands
2093 """
2094 return self._autoscaleXon
2096 def get_autoscaley_on(self):
2097 """
2098 Get whether autoscaling for the y-axis is applied on plot commands
2099 """
2100 return self._autoscaleYon
2102 def set_autoscale_on(self, b):
2103 """
2104 Set whether autoscaling is applied on plot commands
2106 Parameters
2107 ----------
2108 b : bool
2109 """
2110 self._autoscaleXon = b
2111 self._autoscaleYon = b
2113 def set_autoscalex_on(self, b):
2114 """
2115 Set whether autoscaling for the x-axis is applied on plot commands
2117 Parameters
2118 ----------
2119 b : bool
2120 """
2121 self._autoscaleXon = b
2123 def set_autoscaley_on(self, b):
2124 """
2125 Set whether autoscaling for the y-axis is applied on plot commands
2127 Parameters
2128 ----------
2129 b : bool
2130 """
2131 self._autoscaleYon = b
2133 @property
2134 def use_sticky_edges(self):
2135 """
2136 When autoscaling, whether to obey all `Artist.sticky_edges`.
2138 Default is ``True``.
2140 Setting this to ``False`` ensures that the specified margins
2141 will be applied, even if the plot includes an image, for
2142 example, which would otherwise force a view limit to coincide
2143 with its data limit.
2145 The changing this property does not change the plot until
2146 `autoscale` or `autoscale_view` is called.
2147 """
2148 return self._use_sticky_edges
2150 @use_sticky_edges.setter
2151 def use_sticky_edges(self, b):
2152 self._use_sticky_edges = bool(b)
2153 # No effect until next autoscaling, which will mark the axes as stale.
2155 def set_xmargin(self, m):
2156 """
2157 Set padding of X data limits prior to autoscaling.
2159 *m* times the data interval will be added to each
2160 end of that interval before it is used in autoscaling.
2161 For example, if your data is in the range [0, 2], a factor of
2162 ``m = 0.1`` will result in a range [-0.2, 2.2].
2164 Negative values -0.5 < m < 0 will result in clipping of the data range.
2165 I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in
2166 a range [0.2, 1.8].
2168 Parameters
2169 ----------
2170 m : float greater than -0.5
2171 """
2172 if m <= -0.5:
2173 raise ValueError("margin must be greater than -0.5")
2174 self._xmargin = m
2175 self.stale = True
2177 def set_ymargin(self, m):
2178 """
2179 Set padding of Y data limits prior to autoscaling.
2181 *m* times the data interval will be added to each
2182 end of that interval before it is used in autoscaling.
2183 For example, if your data is in the range [0, 2], a factor of
2184 ``m = 0.1`` will result in a range [-0.2, 2.2].
2186 Negative values -0.5 < m < 0 will result in clipping of the data range.
2187 I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in
2188 a range [0.2, 1.8].
2190 Parameters
2191 ----------
2192 m : float greater than -0.5
2193 """
2194 if m <= -0.5:
2195 raise ValueError("margin must be greater than -0.5")
2196 self._ymargin = m
2197 self.stale = True
2199 def margins(self, *margins, x=None, y=None, tight=True):
2200 """
2201 Set or retrieve autoscaling margins.
2203 The padding added to each limit of the axes is the *margin*
2204 times the data interval. All input parameters must be floats
2205 within the range [0, 1]. Passing both positional and keyword
2206 arguments is invalid and will raise a TypeError. If no
2207 arguments (positional or otherwise) are provided, the current
2208 margins will remain in place and simply be returned.
2210 Specifying any margin changes only the autoscaling; for example,
2211 if *xmargin* is not None, then *xmargin* times the X data
2212 interval will be added to each end of that interval before
2213 it is used in autoscaling.
2215 Parameters
2216 ----------
2217 *margins : float, optional
2218 If a single positional argument is provided, it specifies
2219 both margins of the x-axis and y-axis limits. If two
2220 positional arguments are provided, they will be interpreted
2221 as *xmargin*, *ymargin*. If setting the margin on a single
2222 axis is desired, use the keyword arguments described below.
2224 x, y : float, optional
2225 Specific margin values for the x-axis and y-axis,
2226 respectively. These cannot be used with positional
2227 arguments, but can be used individually to alter on e.g.,
2228 only the y-axis.
2230 tight : bool or None, default is True
2231 The *tight* parameter is passed to :meth:`autoscale_view`,
2232 which is executed after a margin is changed; the default
2233 here is *True*, on the assumption that when margins are
2234 specified, no additional padding to match tick marks is
2235 usually desired. Set *tight* to *None* will preserve
2236 the previous setting.
2238 Returns
2239 -------
2240 xmargin, ymargin : float
2242 Notes
2243 -----
2244 If a previously used Axes method such as :meth:`pcolor` has set
2245 :attr:`use_sticky_edges` to `True`, only the limits not set by
2246 the "sticky artists" will be modified. To force all of the
2247 margins to be set, set :attr:`use_sticky_edges` to `False`
2248 before calling :meth:`margins`.
2249 """
2251 if margins and x is not None and y is not None:
2252 raise TypeError('Cannot pass both positional and keyword '
2253 'arguments for x and/or y.')
2254 elif len(margins) == 1:
2255 x = y = margins[0]
2256 elif len(margins) == 2:
2257 x, y = margins
2258 elif margins:
2259 raise TypeError('Must pass a single positional argument for all '
2260 'margins, or one for each margin (x, y).')
2262 if x is None and y is None:
2263 if tight is not True:
2264 cbook._warn_external(f'ignoring tight={tight!r} in get mode')
2265 return self._xmargin, self._ymargin
2267 if x is not None:
2268 self.set_xmargin(x)
2269 if y is not None:
2270 self.set_ymargin(y)
2272 self._request_autoscale_view(
2273 tight=tight, scalex=(x is not None), scaley=(y is not None)
2274 )
2276 def set_rasterization_zorder(self, z):
2277 """
2278 Parameters
2279 ----------
2280 z : float or None
2281 zorder below which artists are rasterized. ``None`` means that
2282 artists do not get rasterized based on zorder.
2283 """
2284 self._rasterization_zorder = z
2285 self.stale = True
2287 def get_rasterization_zorder(self):
2288 """Return the zorder value below which artists will be rasterized."""
2289 return self._rasterization_zorder
2291 def autoscale(self, enable=True, axis='both', tight=None):
2292 """
2293 Autoscale the axis view to the data (toggle).
2295 Convenience method for simple axis view autoscaling.
2296 It turns autoscaling on or off, and then,
2297 if autoscaling for either axis is on, it performs
2298 the autoscaling on the specified axis or axes.
2300 Parameters
2301 ----------
2302 enable : bool or None, optional
2303 True (default) turns autoscaling on, False turns it off.
2304 None leaves the autoscaling state unchanged.
2306 axis : {'both', 'x', 'y'}, optional
2307 Which axis to operate on; default is 'both'.
2309 tight : bool or None, optional
2310 If True, first set the margins to zero. Then, this argument is
2311 forwarded to `autoscale_view` (regardless of its value); see the
2312 description of its behavior there.
2313 """
2314 if enable is None:
2315 scalex = True
2316 scaley = True
2317 else:
2318 scalex = False
2319 scaley = False
2320 if axis in ['x', 'both']:
2321 self._autoscaleXon = bool(enable)
2322 scalex = self._autoscaleXon
2323 if axis in ['y', 'both']:
2324 self._autoscaleYon = bool(enable)
2325 scaley = self._autoscaleYon
2326 if tight and scalex:
2327 self._xmargin = 0
2328 if tight and scaley:
2329 self._ymargin = 0
2330 self._request_autoscale_view(tight=tight, scalex=scalex, scaley=scaley)
2332 def autoscale_view(self, tight=None, scalex=True, scaley=True):
2333 """
2334 Autoscale the view limits using the data limits.
2336 Parameters
2337 ----------
2338 tight : bool or None
2339 If *True*, only expand the axis limits using the margins. Note
2340 that unlike for `autoscale`, ``tight=True`` does *not* set the
2341 margins to zero.
2343 If *False* and :rc:`axes.autolimit_mode` is 'round_numbers', then
2344 after expansion by the margins, further expand the axis limits
2345 using the axis major locator.
2347 If None (the default), reuse the value set in the previous call to
2348 `autoscale_view` (the initial value is False, but the default style
2349 sets :rc:`axes.autolimit_mode` to 'data', in which case this
2350 behaves like True).
2352 scalex : bool
2353 Whether to autoscale the x axis (default is True).
2355 scaley : bool
2356 Whether to autoscale the x axis (default is True).
2358 Notes
2359 -----
2360 The autoscaling preserves any preexisting axis direction reversal.
2362 The data limits are not updated automatically when artist data are
2363 changed after the artist has been added to an Axes instance. In that
2364 case, use :meth:`matplotlib.axes.Axes.relim` prior to calling
2365 autoscale_view.
2367 If the views of the axes are fixed, e.g. via `set_xlim`, they will
2368 not be changed by autoscale_view().
2369 See :meth:`matplotlib.axes.Axes.autoscale` for an alternative.
2370 """
2371 if tight is not None:
2372 self._tight = bool(tight)
2374 x_stickies = y_stickies = np.array([])
2375 if self.use_sticky_edges:
2376 # Only iterate over axes and artists if needed. The check for
2377 # ``hasattr(ax, "lines")`` is necessary because this can be called
2378 # very early in the axes init process (e.g., for twin axes) when
2379 # these attributes don't even exist yet, in which case
2380 # `get_children` would raise an AttributeError.
2381 if self._xmargin and scalex and self._autoscaleXon:
2382 x_stickies = np.sort(np.concatenate([
2383 artist.sticky_edges.x
2384 for ax in self._shared_x_axes.get_siblings(self)
2385 if hasattr(ax, "lines")
2386 for artist in ax.get_children()]))
2387 if self._ymargin and scaley and self._autoscaleYon:
2388 y_stickies = np.sort(np.concatenate([
2389 artist.sticky_edges.y
2390 for ax in self._shared_y_axes.get_siblings(self)
2391 if hasattr(ax, "lines")
2392 for artist in ax.get_children()]))
2393 if self.get_xscale().lower() == 'log':
2394 x_stickies = x_stickies[x_stickies > 0]
2395 if self.get_yscale().lower() == 'log':
2396 y_stickies = y_stickies[y_stickies > 0]
2398 def handle_single_axis(scale, autoscaleon, shared_axes, interval,
2399 minpos, axis, margin, stickies, set_bound):
2401 if not (scale and autoscaleon):
2402 return # nothing to do...
2404 shared = shared_axes.get_siblings(self)
2405 dl = [ax.dataLim for ax in shared]
2406 # ignore non-finite data limits if good limits exist
2407 finite_dl = [d for d in dl if np.isfinite(d).all()]
2408 if len(finite_dl):
2409 # if finite limits exist for atleast one axis (and the
2410 # other is infinite), restore the finite limits
2411 x_finite = [d for d in dl
2412 if (np.isfinite(d.intervalx).all() and
2413 (d not in finite_dl))]
2414 y_finite = [d for d in dl
2415 if (np.isfinite(d.intervaly).all() and
2416 (d not in finite_dl))]
2418 dl = finite_dl
2419 dl.extend(x_finite)
2420 dl.extend(y_finite)
2422 bb = mtransforms.BboxBase.union(dl)
2423 x0, x1 = getattr(bb, interval)
2424 # If x0 and x1 are non finite, use the locator to figure out
2425 # default limits.
2426 locator = axis.get_major_locator()
2427 x0, x1 = locator.nonsingular(x0, x1)
2429 # Prevent margin addition from crossing a sticky value. Small
2430 # tolerances (whose values come from isclose()) must be used due to
2431 # floating point issues with streamplot.
2432 def tol(x): return 1e-5 * abs(x) + 1e-8
2433 # Index of largest element < x0 + tol, if any.
2434 i0 = stickies.searchsorted(x0 + tol(x0)) - 1
2435 x0bound = stickies[i0] if i0 != -1 else None
2436 # Index of smallest element > x1 - tol, if any.
2437 i1 = stickies.searchsorted(x1 - tol(x1))
2438 x1bound = stickies[i1] if i1 != len(stickies) else None
2440 # Add the margin in figure space and then transform back, to handle
2441 # non-linear scales.
2442 minpos = getattr(bb, minpos)
2443 transform = axis.get_transform()
2444 inverse_trans = transform.inverted()
2445 x0, x1 = axis._scale.limit_range_for_scale(x0, x1, minpos)
2446 x0t, x1t = transform.transform([x0, x1])
2447 delta = (x1t - x0t) * margin
2448 if not np.isfinite(delta):
2449 delta = 0 # If a bound isn't finite, set margin to zero.
2450 x0, x1 = inverse_trans.transform([x0t - delta, x1t + delta])
2452 # Apply sticky bounds.
2453 if x0bound is not None:
2454 x0 = max(x0, x0bound)
2455 if x1bound is not None:
2456 x1 = min(x1, x1bound)
2458 if not self._tight:
2459 x0, x1 = locator.view_limits(x0, x1)
2460 set_bound(x0, x1)
2461 # End of definition of internal function 'handle_single_axis'.
2463 handle_single_axis(
2464 scalex, self._autoscaleXon, self._shared_x_axes, 'intervalx',
2465 'minposx', self.xaxis, self._xmargin, x_stickies, self.set_xbound)
2466 handle_single_axis(
2467 scaley, self._autoscaleYon, self._shared_y_axes, 'intervaly',
2468 'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound)
2470 def _get_axis_list(self):
2471 return (self.xaxis, self.yaxis)
2473 def _get_axis_map(self):
2474 """
2475 Return a mapping of `Axis` "names" to `Axis` instances.
2477 The `Axis` name is derived from the attribute under which the instance
2478 is stored, so e.g. for polar axes, the theta-axis is still named "x"
2479 and the r-axis is still named "y" (for back-compatibility).
2481 In practice, this means that the entries are typically "x" and "y", and
2482 additionally "z" for 3D axes.
2483 """
2484 d = {}
2485 axis_list = self._get_axis_list()
2486 for k, v in vars(self).items():
2487 if k.endswith("axis") and v in axis_list:
2488 d[k[:-len("axis")]] = v
2489 return d
2491 def _update_title_position(self, renderer):
2492 """
2493 Update the title position based on the bounding box enclosing
2494 all the ticklabels and x-axis spine and xlabel...
2495 """
2497 if self._autotitlepos is not None and not self._autotitlepos:
2498 _log.debug('title position was updated manually, not adjusting')
2499 return
2501 titles = (self.title, self._left_title, self._right_title)
2503 if self._autotitlepos is None:
2504 for title in titles:
2505 x, y = title.get_position()
2506 if not np.isclose(y, 1.0):
2507 self._autotitlepos = False
2508 _log.debug('not adjusting title pos because a title was'
2509 ' already placed manually: %f', y)
2510 return
2511 self._autotitlepos = True
2513 for title in titles:
2514 x, _ = title.get_position()
2515 # need to start again in case of window resizing
2516 title.set_position((x, 1.0))
2517 # need to check all our twins too...
2518 axs = self._twinned_axes.get_siblings(self)
2519 # and all the children
2520 for ax in self.child_axes:
2521 if ax is not None:
2522 locator = ax.get_axes_locator()
2523 if locator:
2524 pos = locator(self, renderer)
2525 ax.apply_aspect(pos)
2526 else:
2527 ax.apply_aspect()
2528 axs = axs + [ax]
2529 top = 0
2530 for ax in axs:
2531 if (ax.xaxis.get_ticks_position() in ['top', 'unknown']
2532 or ax.xaxis.get_label_position() == 'top'):
2533 bb = ax.xaxis.get_tightbbox(renderer)
2534 else:
2535 bb = ax.get_window_extent(renderer)
2536 if bb is not None:
2537 top = max(top, bb.ymax)
2538 if title.get_window_extent(renderer).ymin < top:
2539 _, y = self.transAxes.inverted().transform((0, top))
2540 title.set_position((x, y))
2541 # empirically, this doesn't always get the min to top,
2542 # so we need to adjust again.
2543 if title.get_window_extent(renderer).ymin < top:
2544 _, y = self.transAxes.inverted().transform(
2545 (0., 2 * top - title.get_window_extent(renderer).ymin))
2546 title.set_position((x, y))
2548 ymax = max(title.get_position()[1] for title in titles)
2549 for title in titles:
2550 # now line up all the titles at the highest baseline.
2551 x, _ = title.get_position()
2552 title.set_position((x, ymax))
2554 # Drawing
2555 @martist.allow_rasterization
2556 def draw(self, renderer=None, inframe=False):
2557 """Draw everything (plot lines, axes, labels)"""
2558 if renderer is None:
2559 renderer = self.figure._cachedRenderer
2560 if renderer is None:
2561 raise RuntimeError('No renderer defined')
2562 if not self.get_visible():
2563 return
2564 self._unstale_viewLim()
2566 renderer.open_group('axes', gid=self.get_gid())
2568 # prevent triggering call backs during the draw process
2569 self._stale = True
2571 # loop over self and child axes...
2572 locator = self.get_axes_locator()
2573 if locator:
2574 pos = locator(self, renderer)
2575 self.apply_aspect(pos)
2576 else:
2577 self.apply_aspect()
2579 artists = self.get_children()
2580 artists.remove(self.patch)
2582 # the frame draws the edges around the axes patch -- we
2583 # decouple these so the patch can be in the background and the
2584 # frame in the foreground. Do this before drawing the axis
2585 # objects so that the spine has the opportunity to update them.
2586 if not (self.axison and self._frameon):
2587 for spine in self.spines.values():
2588 artists.remove(spine)
2590 self._update_title_position(renderer)
2592 if not self.axison or inframe:
2593 for _axis in self._get_axis_list():
2594 artists.remove(_axis)
2596 if inframe:
2597 artists.remove(self.title)
2598 artists.remove(self._left_title)
2599 artists.remove(self._right_title)
2601 if not self.figure.canvas.is_saving():
2602 artists = [a for a in artists
2603 if not a.get_animated() or a in self.images]
2604 artists = sorted(artists, key=attrgetter('zorder'))
2606 # rasterize artists with negative zorder
2607 # if the minimum zorder is negative, start rasterization
2608 rasterization_zorder = self._rasterization_zorder
2610 if (rasterization_zorder is not None and
2611 artists and artists[0].zorder < rasterization_zorder):
2612 renderer.start_rasterizing()
2613 artists_rasterized = [a for a in artists
2614 if a.zorder < rasterization_zorder]
2615 artists = [a for a in artists
2616 if a.zorder >= rasterization_zorder]
2617 else:
2618 artists_rasterized = []
2620 # the patch draws the background rectangle -- the frame below
2621 # will draw the edges
2622 if self.axison and self._frameon:
2623 self.patch.draw(renderer)
2625 if artists_rasterized:
2626 for a in artists_rasterized:
2627 a.draw(renderer)
2628 renderer.stop_rasterizing()
2630 mimage._draw_list_compositing_images(renderer, self, artists)
2632 renderer.close_group('axes')
2633 self.stale = False
2635 def draw_artist(self, a):
2636 """
2637 This method can only be used after an initial draw which
2638 caches the renderer. It is used to efficiently update Axes
2639 data (axis ticks, labels, etc are not updated)
2640 """
2641 if self.figure._cachedRenderer is None:
2642 raise AttributeError("draw_artist can only be used after an "
2643 "initial draw which caches the renderer")
2644 a.draw(self.figure._cachedRenderer)
2646 def redraw_in_frame(self):
2647 """
2648 This method can only be used after an initial draw which
2649 caches the renderer. It is used to efficiently update Axes
2650 data (axis ticks, labels, etc are not updated)
2651 """
2652 if self.figure._cachedRenderer is None:
2653 raise AttributeError("redraw_in_frame can only be used after an "
2654 "initial draw which caches the renderer")
2655 self.draw(self.figure._cachedRenderer, inframe=True)
2657 def get_renderer_cache(self):
2658 return self.figure._cachedRenderer
2660 # Axes rectangle characteristics
2662 def get_frame_on(self):
2663 """Get whether the axes rectangle patch is drawn."""
2664 return self._frameon
2666 def set_frame_on(self, b):
2667 """
2668 Set whether the axes rectangle patch is drawn.
2670 Parameters
2671 ----------
2672 b : bool
2673 """
2674 self._frameon = b
2675 self.stale = True
2677 def get_axisbelow(self):
2678 """
2679 Get whether axis ticks and gridlines are above or below most artists.
2681 Returns
2682 -------
2683 axisbelow : bool or 'line'
2685 See Also
2686 --------
2687 set_axisbelow
2688 """
2689 return self._axisbelow
2691 def set_axisbelow(self, b):
2692 """
2693 Set whether axis ticks and gridlines are above or below most artists.
2695 This controls the zorder of the ticks and gridlines. For more
2696 information on the zorder see :doc:`/gallery/misc/zorder_demo`.
2698 Parameters
2699 ----------
2700 b : bool or 'line'
2701 Possible values:
2703 - *True* (zorder = 0.5): Ticks and gridlines are below all Artists.
2704 - 'line' (zorder = 1.5): Ticks and gridlines are above patches
2705 (e.g. rectangles, with default zorder = 1) but still below lines
2706 and markers (with their default zorder = 2).
2707 - *False* (zorder = 2.5): Ticks and gridlines are above patches
2708 and lines / markers.
2710 See Also
2711 --------
2712 get_axisbelow
2713 """
2714 self._axisbelow = axisbelow = validate_axisbelow(b)
2715 if axisbelow is True:
2716 zorder = 0.5
2717 elif axisbelow is False:
2718 zorder = 2.5
2719 elif axisbelow == "line":
2720 zorder = 1.5
2721 else:
2722 raise ValueError("Unexpected axisbelow value")
2723 for axis in self._get_axis_list():
2724 axis.set_zorder(zorder)
2725 self.stale = True
2727 @docstring.dedent_interpd
2728 def grid(self, b=None, which='major', axis='both', **kwargs):
2729 """
2730 Configure the grid lines.
2732 Parameters
2733 ----------
2734 b : bool or None, optional
2735 Whether to show the grid lines. If any *kwargs* are supplied,
2736 it is assumed you want the grid on and *b* will be set to True.
2738 If *b* is *None* and there are no *kwargs*, this toggles the
2739 visibility of the lines.
2741 which : {'major', 'minor', 'both'}, optional
2742 The grid lines to apply the changes on.
2744 axis : {'both', 'x', 'y'}, optional
2745 The axis to apply the changes on.
2747 **kwargs : `.Line2D` properties
2748 Define the line properties of the grid, e.g.::
2750 grid(color='r', linestyle='-', linewidth=2)
2752 Valid keyword arguments are:
2754 %(_Line2D_docstr)s
2756 Notes
2757 -----
2758 The axis is drawn as a unit, so the effective zorder for drawing the
2759 grid is determined by the zorder of each axis, not by the zorder of the
2760 `.Line2D` objects comprising the grid. Therefore, to set grid zorder,
2761 use `.set_axisbelow` or, for more control, call the
2762 `~matplotlib.axis.Axis.set_zorder` method of each axis.
2763 """
2764 if len(kwargs):
2765 b = True
2766 cbook._check_in_list(['x', 'y', 'both'], axis=axis)
2767 if axis in ['x', 'both']:
2768 self.xaxis.grid(b, which=which, **kwargs)
2769 if axis in ['y', 'both']:
2770 self.yaxis.grid(b, which=which, **kwargs)
2772 def ticklabel_format(self, *, axis='both', style='', scilimits=None,
2773 useOffset=None, useLocale=None, useMathText=None):
2774 r"""
2775 Change the `~matplotlib.ticker.ScalarFormatter` used by
2776 default for linear axes.
2778 Optional keyword arguments:
2780 ============== =========================================
2781 Keyword Description
2782 ============== =========================================
2783 *axis* {'x', 'y', 'both'}
2784 *style* {'sci' (or 'scientific'), 'plain'}
2785 plain turns off scientific notation
2786 *scilimits* (m, n), pair of integers; if *style*
2787 is 'sci', scientific notation will
2788 be used for numbers outside the range
2789 10\ :sup:`m` to 10\ :sup:`n`.
2790 Use (0, 0) to include all numbers.
2791 Use (m, m) where m != 0 to fix the order
2792 of magnitude to 10\ :sup:`m`.
2793 *useOffset* bool or float
2794 If True, the offset will be calculated as
2795 needed; if False, no offset will be used;
2796 if a numeric offset is specified, it will
2797 be used.
2798 *useLocale* If True, format the number according to
2799 the current locale. This affects things
2800 such as the character used for the
2801 decimal separator. If False, use
2802 C-style (English) formatting. The
2803 default setting is controlled by the
2804 axes.formatter.use_locale rcparam.
2805 *useMathText* If True, render the offset and scientific
2806 notation in mathtext
2807 ============== =========================================
2809 Only the major ticks are affected.
2810 If the method is called when the `~matplotlib.ticker.ScalarFormatter`
2811 is not the `~matplotlib.ticker.Formatter` being used, an
2812 `AttributeError` will be raised.
2813 """
2814 style = style.lower()
2815 axis = axis.lower()
2816 if scilimits is not None:
2817 try:
2818 m, n = scilimits
2819 m + n + 1 # check that both are numbers
2820 except (ValueError, TypeError):
2821 raise ValueError("scilimits must be a sequence of 2 integers")
2822 STYLES = {'sci': True, 'scientific': True, 'plain': False, '': None}
2823 is_sci_style = cbook._check_getitem(STYLES, style=style)
2824 axis_map = {**{k: [v] for k, v in self._get_axis_map().items()},
2825 'both': self._get_axis_list()}
2826 axises = cbook._check_getitem(axis_map, axis=axis)
2827 try:
2828 for axis in axises:
2829 if is_sci_style is not None:
2830 axis.major.formatter.set_scientific(is_sci_style)
2831 if scilimits is not None:
2832 axis.major.formatter.set_powerlimits(scilimits)
2833 if useOffset is not None:
2834 axis.major.formatter.set_useOffset(useOffset)
2835 if useLocale is not None:
2836 axis.major.formatter.set_useLocale(useLocale)
2837 if useMathText is not None:
2838 axis.major.formatter.set_useMathText(useMathText)
2839 except AttributeError:
2840 raise AttributeError(
2841 "This method only works with the ScalarFormatter")
2843 def locator_params(self, axis='both', tight=None, **kwargs):
2844 """
2845 Control behavior of major tick locators.
2847 Because the locator is involved in autoscaling, `~.Axes.autoscale_view`
2848 is called automatically after the parameters are changed.
2850 Parameters
2851 ----------
2852 axis : {'both', 'x', 'y'}, optional
2853 The axis on which to operate.
2855 tight : bool or None, optional
2856 Parameter passed to `~.Axes.autoscale_view`.
2857 Default is None, for no change.
2859 Other Parameters
2860 ----------------
2861 **kwargs
2862 Remaining keyword arguments are passed to directly to the
2863 ``set_params()`` method of the locator. Supported keywords depend
2864 on the type of the locator. See for example
2865 `~.ticker.MaxNLocator.set_params` for the `.ticker.MaxNLocator`
2866 used by default for linear axes.
2868 Examples
2869 --------
2870 When plotting small subplots, one might want to reduce the maximum
2871 number of ticks and use tight bounds, for example::
2873 ax.locator_params(tight=True, nbins=4)
2875 """
2876 _x = axis in ['x', 'both']
2877 _y = axis in ['y', 'both']
2878 if _x:
2879 self.xaxis.get_major_locator().set_params(**kwargs)
2880 if _y:
2881 self.yaxis.get_major_locator().set_params(**kwargs)
2882 self._request_autoscale_view(tight=tight, scalex=_x, scaley=_y)
2884 def tick_params(self, axis='both', **kwargs):
2885 """Change the appearance of ticks, tick labels, and gridlines.
2887 Parameters
2888 ----------
2889 axis : {'x', 'y', 'both'}, optional
2890 Which axis to apply the parameters to.
2892 Other Parameters
2893 ----------------
2894 axis : {'x', 'y', 'both'}
2895 Axis on which to operate; default is 'both'.
2896 reset : bool, default: False
2897 If *True*, set all parameters to defaults before processing other
2898 keyword arguments.
2899 which : {'major', 'minor', 'both'}
2900 Default is 'major'; apply arguments to *which* ticks.
2901 direction : {'in', 'out', 'inout'}
2902 Puts ticks inside the axes, outside the axes, or both.
2903 length : float
2904 Tick length in points.
2905 width : float
2906 Tick width in points.
2907 color : color
2908 Tick color.
2909 pad : float
2910 Distance in points between tick and label.
2911 labelsize : float or str
2912 Tick label font size in points or as a string (e.g., 'large').
2913 labelcolor : color
2914 Tick label color.
2915 colors : color
2916 Tick color and label color.
2917 zorder : float
2918 Tick and label zorder.
2919 bottom, top, left, right : bool
2920 Whether to draw the respective ticks.
2921 labelbottom, labeltop, labelleft, labelright : bool
2922 Whether to draw the respective tick labels.
2923 labelrotation : float
2924 Tick label rotation
2925 grid_color : color
2926 Gridline color.
2927 grid_alpha : float
2928 Transparency of gridlines: 0 (transparent) to 1 (opaque).
2929 grid_linewidth : float
2930 Width of gridlines in points.
2931 grid_linestyle : str
2932 Any valid `.Line2D` line style spec.
2934 Examples
2935 --------
2936 Usage ::
2938 ax.tick_params(direction='out', length=6, width=2, colors='r',
2939 grid_color='r', grid_alpha=0.5)
2941 This will make all major ticks be red, pointing out of the box,
2942 and with dimensions 6 points by 2 points. Tick labels will
2943 also be red. Gridlines will be red and translucent.
2945 """
2946 cbook._check_in_list(['x', 'y', 'both'], axis=axis)
2947 if axis in ['x', 'both']:
2948 xkw = dict(kwargs)
2949 xkw.pop('left', None)
2950 xkw.pop('right', None)
2951 xkw.pop('labelleft', None)
2952 xkw.pop('labelright', None)
2953 self.xaxis.set_tick_params(**xkw)
2954 if axis in ['y', 'both']:
2955 ykw = dict(kwargs)
2956 ykw.pop('top', None)
2957 ykw.pop('bottom', None)
2958 ykw.pop('labeltop', None)
2959 ykw.pop('labelbottom', None)
2960 self.yaxis.set_tick_params(**ykw)
2962 def set_axis_off(self):
2963 """
2964 Turn the x- and y-axis off.
2966 This affects the axis lines, ticks, ticklabels, grid and axis labels.
2967 """
2968 self.axison = False
2969 self.stale = True
2971 def set_axis_on(self):
2972 """
2973 Turn the x- and y-axis on.
2975 This affects the axis lines, ticks, ticklabels, grid and axis labels.
2976 """
2977 self.axison = True
2978 self.stale = True
2980 # data limits, ticks, tick labels, and formatting
2982 def invert_xaxis(self):
2983 """
2984 Invert the x-axis.
2986 See Also
2987 --------
2988 xaxis_inverted
2989 get_xlim, set_xlim
2990 get_xbound, set_xbound
2991 """
2992 self.xaxis.set_inverted(not self.xaxis.get_inverted())
2994 def xaxis_inverted(self):
2995 """
2996 Return whether the x-axis is inverted.
2998 The axis is inverted if the left value is larger than the right value.
3000 See Also
3001 --------
3002 invert_xaxis
3003 get_xlim, set_xlim
3004 get_xbound, set_xbound
3005 """
3006 return self.xaxis.get_inverted()
3008 def get_xbound(self):
3009 """
3010 Return the lower and upper x-axis bounds, in increasing order.
3012 See Also
3013 --------
3014 set_xbound
3015 get_xlim, set_xlim
3016 invert_xaxis, xaxis_inverted
3017 """
3018 left, right = self.get_xlim()
3019 if left < right:
3020 return left, right
3021 else:
3022 return right, left
3024 def set_xbound(self, lower=None, upper=None):
3025 """
3026 Set the lower and upper numerical bounds of the x-axis.
3028 This method will honor axes inversion regardless of parameter order.
3029 It will not change the autoscaling setting (``Axes._autoscaleXon``).
3031 Parameters
3032 ----------
3033 lower, upper : float or None
3034 The lower and upper bounds. If *None*, the respective axis bound
3035 is not modified.
3037 See Also
3038 --------
3039 get_xbound
3040 get_xlim, set_xlim
3041 invert_xaxis, xaxis_inverted
3042 """
3043 if upper is None and np.iterable(lower):
3044 lower, upper = lower
3046 old_lower, old_upper = self.get_xbound()
3048 if lower is None:
3049 lower = old_lower
3050 if upper is None:
3051 upper = old_upper
3053 if self.xaxis_inverted():
3054 if lower < upper:
3055 self.set_xlim(upper, lower, auto=None)
3056 else:
3057 self.set_xlim(lower, upper, auto=None)
3058 else:
3059 if lower < upper:
3060 self.set_xlim(lower, upper, auto=None)
3061 else:
3062 self.set_xlim(upper, lower, auto=None)
3064 def get_xlim(self):
3065 """
3066 Return the x-axis view limits.
3068 Returns
3069 -------
3070 left, right : (float, float)
3071 The current x-axis limits in data coordinates.
3073 See Also
3074 --------
3075 set_xlim
3076 set_xbound, get_xbound
3077 invert_xaxis, xaxis_inverted
3079 Notes
3080 -----
3081 The x-axis may be inverted, in which case the *left* value will
3082 be greater than the *right* value.
3084 """
3085 return tuple(self.viewLim.intervalx)
3087 def _validate_converted_limits(self, limit, convert):
3088 """
3089 Raise ValueError if converted limits are non-finite.
3091 Note that this function also accepts None as a limit argument.
3093 Returns
3094 -------
3095 The limit value after call to convert(), or None if limit is None.
3096 """
3097 if limit is not None:
3098 converted_limit = convert(limit)
3099 if (isinstance(converted_limit, Real)
3100 and not np.isfinite(converted_limit)):
3101 raise ValueError("Axis limits cannot be NaN or Inf")
3102 return converted_limit
3104 def set_xlim(self, left=None, right=None, emit=True, auto=False,
3105 *, xmin=None, xmax=None):
3106 """
3107 Set the x-axis view limits.
3109 Parameters
3110 ----------
3111 left : float, optional
3112 The left xlim in data coordinates. Passing *None* leaves the
3113 limit unchanged.
3115 The left and right xlims may also be passed as the tuple
3116 (*left*, *right*) as the first positional argument (or as
3117 the *left* keyword argument).
3119 .. ACCEPTS: (bottom: float, top: float)
3121 right : float, optional
3122 The right xlim in data coordinates. Passing *None* leaves the
3123 limit unchanged.
3125 emit : bool, optional
3126 Whether to notify observers of limit change (default: True).
3128 auto : bool or None, optional
3129 Whether to turn on autoscaling of the x-axis. True turns on,
3130 False turns off (default action), None leaves unchanged.
3132 xmin, xmax : scalar, optional
3133 They are equivalent to left and right respectively,
3134 and it is an error to pass both *xmin* and *left* or
3135 *xmax* and *right*.
3137 Returns
3138 -------
3139 left, right : (float, float)
3140 The new x-axis limits in data coordinates.
3142 See Also
3143 --------
3144 get_xlim
3145 set_xbound, get_xbound
3146 invert_xaxis, xaxis_inverted
3148 Notes
3149 -----
3150 The *left* value may be greater than the *right* value, in which
3151 case the x-axis values will decrease from left to right.
3153 Examples
3154 --------
3155 >>> set_xlim(left, right)
3156 >>> set_xlim((left, right))
3157 >>> left, right = set_xlim(left, right)
3159 One limit may be left unchanged.
3161 >>> set_xlim(right=right_lim)
3163 Limits may be passed in reverse order to flip the direction of
3164 the x-axis. For example, suppose *x* represents the number of
3165 years before present. The x-axis limits might be set like the
3166 following so 5000 years ago is on the left of the plot and the
3167 present is on the right.
3169 >>> set_xlim(5000, 0)
3171 """
3172 if right is None and np.iterable(left):
3173 left, right = left
3174 if xmin is not None:
3175 if left is not None:
3176 raise TypeError('Cannot pass both `xmin` and `left`')
3177 left = xmin
3178 if xmax is not None:
3179 if right is not None:
3180 raise TypeError('Cannot pass both `xmax` and `right`')
3181 right = xmax
3183 self._process_unit_info(xdata=(left, right))
3184 left = self._validate_converted_limits(left, self.convert_xunits)
3185 right = self._validate_converted_limits(right, self.convert_xunits)
3187 if left is None or right is None:
3188 # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
3189 # so only grab the limits if we really need them.
3190 old_left, old_right = self.get_xlim()
3191 if left is None:
3192 left = old_left
3193 if right is None:
3194 right = old_right
3196 if self.get_xscale() == 'log' and (left <= 0 or right <= 0):
3197 # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
3198 # so only grab the limits if we really need them.
3199 old_left, old_right = self.get_xlim()
3200 if left <= 0:
3201 cbook._warn_external(
3202 'Attempted to set non-positive left xlim on a '
3203 'log-scaled axis.\n'
3204 'Invalid limit will be ignored.')
3205 left = old_left
3206 if right <= 0:
3207 cbook._warn_external(
3208 'Attempted to set non-positive right xlim on a '
3209 'log-scaled axis.\n'
3210 'Invalid limit will be ignored.')
3211 right = old_right
3212 if left == right:
3213 cbook._warn_external(
3214 f"Attempting to set identical left == right == {left} results "
3215 f"in singular transformations; automatically expanding.")
3216 reverse = left > right
3217 left, right = self.xaxis.get_major_locator().nonsingular(left, right)
3218 left, right = self.xaxis.limit_range_for_scale(left, right)
3219 # cast to bool to avoid bad interaction between python 3.8 and np.bool_
3220 left, right = sorted([left, right], reverse=bool(reverse))
3222 self._viewLim.intervalx = (left, right)
3223 # Mark viewlims as no longer stale without triggering an autoscale.
3224 for ax in self._shared_x_axes.get_siblings(self):
3225 ax._stale_viewlim_x = False
3226 if auto is not None:
3227 self._autoscaleXon = bool(auto)
3229 if emit:
3230 self.callbacks.process('xlim_changed', self)
3231 # Call all of the other x-axes that are shared with this one
3232 for other in self._shared_x_axes.get_siblings(self):
3233 if other is not self:
3234 other.set_xlim(self.viewLim.intervalx,
3235 emit=False, auto=auto)
3236 if other.figure != self.figure:
3237 other.figure.canvas.draw_idle()
3238 self.stale = True
3239 return left, right
3241 def get_xscale(self):
3242 """
3243 Return the x-axis scale as string.
3245 See Also
3246 --------
3247 set_xscale
3248 """
3249 return self.xaxis.get_scale()
3251 def set_xscale(self, value, **kwargs):
3252 """
3253 Set the x-axis scale.
3255 Parameters
3256 ----------
3257 value : {"linear", "log", "symlog", "logit", ...}
3258 The axis scale type to apply.
3260 **kwargs
3261 Different keyword arguments are accepted, depending on the scale.
3262 See the respective class keyword arguments:
3264 - `matplotlib.scale.LinearScale`
3265 - `matplotlib.scale.LogScale`
3266 - `matplotlib.scale.SymmetricalLogScale`
3267 - `matplotlib.scale.LogitScale`
3269 Notes
3270 -----
3271 By default, Matplotlib supports the above mentioned scales.
3272 Additionally, custom scales may be registered using
3273 `matplotlib.scale.register_scale`. These scales can then also
3274 be used here.
3275 """
3276 old_default_lims = (self.xaxis.get_major_locator()
3277 .nonsingular(-np.inf, np.inf))
3278 g = self.get_shared_x_axes()
3279 for ax in g.get_siblings(self):
3280 ax.xaxis._set_scale(value, **kwargs)
3281 ax._update_transScale()
3282 ax.stale = True
3283 new_default_lims = (self.xaxis.get_major_locator()
3284 .nonsingular(-np.inf, np.inf))
3285 if old_default_lims != new_default_lims:
3286 # Force autoscaling now, to take advantage of the scale locator's
3287 # nonsingular() before it possibly gets swapped out by the user.
3288 self.autoscale_view(scaley=False)
3290 @cbook._make_keyword_only("3.2", "minor")
3291 def get_xticks(self, minor=False):
3292 """Return the x ticks as a list of locations"""
3293 return self.xaxis.get_ticklocs(minor=minor)
3295 @cbook._make_keyword_only("3.2", "minor")
3296 def set_xticks(self, ticks, minor=False):
3297 """
3298 Set the x ticks with list of *ticks*
3300 Parameters
3301 ----------
3302 ticks : list
3303 List of x-axis tick locations.
3305 minor : bool, optional
3306 If ``False`` sets major ticks, if ``True`` sets minor ticks.
3307 Default is ``False``.
3308 """
3309 ret = self.xaxis.set_ticks(ticks, minor=minor)
3310 self.stale = True
3311 return ret
3313 def get_xmajorticklabels(self):
3314 """
3315 Get the major x tick labels.
3317 Returns
3318 -------
3319 labels : list
3320 List of `~matplotlib.text.Text` instances
3321 """
3322 return self.xaxis.get_majorticklabels()
3324 def get_xminorticklabels(self):
3325 """
3326 Get the minor x tick labels.
3328 Returns
3329 -------
3330 labels : list
3331 List of `~matplotlib.text.Text` instances
3332 """
3333 return self.xaxis.get_minorticklabels()
3335 def get_xticklabels(self, minor=False, which=None):
3336 """
3337 Get the x tick labels as a list of `~matplotlib.text.Text` instances.
3339 Parameters
3340 ----------
3341 minor : bool, optional
3342 If True return the minor ticklabels,
3343 else return the major ticklabels.
3345 which : None, ('minor', 'major', 'both')
3346 Overrides *minor*.
3348 Selects which ticklabels to return
3350 Returns
3351 -------
3352 ret : list
3353 List of `~matplotlib.text.Text` instances.
3354 """
3355 return self.xaxis.get_ticklabels(minor=minor, which=which)
3357 def set_xticklabels(self, labels, fontdict=None, minor=False, **kwargs):
3358 """
3359 Set the x-tick labels with list of string labels.
3361 Parameters
3362 ----------
3363 labels : List[str]
3364 List of string labels.
3366 fontdict : dict, optional
3367 A dictionary controlling the appearance of the ticklabels.
3368 The default *fontdict* is::
3370 {'fontsize': rcParams['axes.titlesize'],
3371 'fontweight': rcParams['axes.titleweight'],
3372 'verticalalignment': 'baseline',
3373 'horizontalalignment': loc}
3375 minor : bool, optional
3376 Whether to set the minor ticklabels rather than the major ones.
3378 Returns
3379 -------
3380 A list of `~.text.Text` instances.
3382 Other Parameters
3383 -----------------
3384 **kwargs : `~.text.Text` properties.
3385 """
3386 if fontdict is not None:
3387 kwargs.update(fontdict)
3388 ret = self.xaxis.set_ticklabels(labels,
3389 minor=minor, **kwargs)
3390 self.stale = True
3391 return ret
3393 def invert_yaxis(self):
3394 """
3395 Invert the y-axis.
3397 See Also
3398 --------
3399 yaxis_inverted
3400 get_ylim, set_ylim
3401 get_ybound, set_ybound
3402 """
3403 self.yaxis.set_inverted(not self.yaxis.get_inverted())
3405 def yaxis_inverted(self):
3406 """
3407 Return whether the y-axis is inverted.
3409 The axis is inverted if the bottom value is larger than the top value.
3411 See Also
3412 --------
3413 invert_yaxis
3414 get_ylim, set_ylim
3415 get_ybound, set_ybound
3416 """
3417 return self.yaxis.get_inverted()
3419 def get_ybound(self):
3420 """
3421 Return the lower and upper y-axis bounds, in increasing order.
3423 See Also
3424 --------
3425 set_ybound
3426 get_ylim, set_ylim
3427 invert_yaxis, yaxis_inverted
3428 """
3429 bottom, top = self.get_ylim()
3430 if bottom < top:
3431 return bottom, top
3432 else:
3433 return top, bottom
3435 def set_ybound(self, lower=None, upper=None):
3436 """
3437 Set the lower and upper numerical bounds of the y-axis.
3439 This method will honor axes inversion regardless of parameter order.
3440 It will not change the autoscaling setting (``Axes._autoscaleYon``).
3442 Parameters
3443 ----------
3444 lower, upper : float or None
3445 The lower and upper bounds. If *None*, the respective axis bound
3446 is not modified.
3448 See Also
3449 --------
3450 get_ybound
3451 get_ylim, set_ylim
3452 invert_yaxis, yaxis_inverted
3453 """
3454 if upper is None and np.iterable(lower):
3455 lower, upper = lower
3457 old_lower, old_upper = self.get_ybound()
3459 if lower is None:
3460 lower = old_lower
3461 if upper is None:
3462 upper = old_upper
3464 if self.yaxis_inverted():
3465 if lower < upper:
3466 self.set_ylim(upper, lower, auto=None)
3467 else:
3468 self.set_ylim(lower, upper, auto=None)
3469 else:
3470 if lower < upper:
3471 self.set_ylim(lower, upper, auto=None)
3472 else:
3473 self.set_ylim(upper, lower, auto=None)
3475 def get_ylim(self):
3476 """
3477 Return the y-axis view limits.
3479 Returns
3480 -------
3481 bottom, top : (float, float)
3482 The current y-axis limits in data coordinates.
3484 See Also
3485 --------
3486 set_ylim
3487 set_ybound, get_ybound
3488 invert_yaxis, yaxis_inverted
3490 Notes
3491 -----
3492 The y-axis may be inverted, in which case the *bottom* value
3493 will be greater than the *top* value.
3495 """
3496 return tuple(self.viewLim.intervaly)
3498 def set_ylim(self, bottom=None, top=None, emit=True, auto=False,
3499 *, ymin=None, ymax=None):
3500 """
3501 Set the y-axis view limits.
3503 Parameters
3504 ----------
3505 bottom : float, optional
3506 The bottom ylim in data coordinates. Passing *None* leaves the
3507 limit unchanged.
3509 The bottom and top ylims may also be passed as the tuple
3510 (*bottom*, *top*) as the first positional argument (or as
3511 the *bottom* keyword argument).
3513 .. ACCEPTS: (bottom: float, top: float)
3515 top : float, optional
3516 The top ylim in data coordinates. Passing *None* leaves the
3517 limit unchanged.
3519 emit : bool, optional
3520 Whether to notify observers of limit change (default: ``True``).
3522 auto : bool or None, optional
3523 Whether to turn on autoscaling of the y-axis. *True* turns on,
3524 *False* turns off (default action), *None* leaves unchanged.
3526 ymin, ymax : scalar, optional
3527 They are equivalent to bottom and top respectively,
3528 and it is an error to pass both *ymin* and *bottom* or
3529 *ymax* and *top*.
3531 Returns
3532 -------
3533 bottom, top : (float, float)
3534 The new y-axis limits in data coordinates.
3536 See Also
3537 --------
3538 get_ylim
3539 set_ybound, get_ybound
3540 invert_yaxis, yaxis_inverted
3542 Notes
3543 -----
3544 The *bottom* value may be greater than the *top* value, in which
3545 case the y-axis values will decrease from *bottom* to *top*.
3547 Examples
3548 --------
3549 >>> set_ylim(bottom, top)
3550 >>> set_ylim((bottom, top))
3551 >>> bottom, top = set_ylim(bottom, top)
3553 One limit may be left unchanged.
3555 >>> set_ylim(top=top_lim)
3557 Limits may be passed in reverse order to flip the direction of
3558 the y-axis. For example, suppose ``y`` represents depth of the
3559 ocean in m. The y-axis limits might be set like the following
3560 so 5000 m depth is at the bottom of the plot and the surface,
3561 0 m, is at the top.
3563 >>> set_ylim(5000, 0)
3564 """
3565 if top is None and np.iterable(bottom):
3566 bottom, top = bottom
3567 if ymin is not None:
3568 if bottom is not None:
3569 raise TypeError('Cannot pass both `ymin` and `bottom`')
3570 bottom = ymin
3571 if ymax is not None:
3572 if top is not None:
3573 raise TypeError('Cannot pass both `ymax` and `top`')
3574 top = ymax
3576 self._process_unit_info(ydata=(bottom, top))
3577 bottom = self._validate_converted_limits(bottom, self.convert_yunits)
3578 top = self._validate_converted_limits(top, self.convert_yunits)
3580 if bottom is None or top is None:
3581 # Axes init calls set_ylim(0, 1) before get_ylim() can be called,
3582 # so only grab the limits if we really need them.
3583 old_bottom, old_top = self.get_ylim()
3584 if bottom is None:
3585 bottom = old_bottom
3586 if top is None:
3587 top = old_top
3589 if self.get_yscale() == 'log' and (bottom <= 0 or top <= 0):
3590 # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
3591 # so only grab the limits if we really need them.
3592 old_bottom, old_top = self.get_ylim()
3593 if bottom <= 0:
3594 cbook._warn_external(
3595 'Attempted to set non-positive bottom ylim on a '
3596 'log-scaled axis.\n'
3597 'Invalid limit will be ignored.')
3598 bottom = old_bottom
3599 if top <= 0:
3600 cbook._warn_external(
3601 'Attempted to set non-positive top ylim on a '
3602 'log-scaled axis.\n'
3603 'Invalid limit will be ignored.')
3604 top = old_top
3605 if bottom == top:
3606 cbook._warn_external(
3607 f"Attempting to set identical bottom == top == {bottom} "
3608 f"results in singular transformations; automatically "
3609 f"expanding.")
3610 reverse = bottom > top
3611 bottom, top = self.yaxis.get_major_locator().nonsingular(bottom, top)
3612 bottom, top = self.yaxis.limit_range_for_scale(bottom, top)
3613 # cast to bool to avoid bad interaction between python 3.8 and np.bool_
3614 bottom, top = sorted([bottom, top], reverse=bool(reverse))
3616 self._viewLim.intervaly = (bottom, top)
3617 # Mark viewlims as no longer stale without triggering an autoscale.
3618 for ax in self._shared_y_axes.get_siblings(self):
3619 ax._stale_viewlim_y = False
3620 if auto is not None:
3621 self._autoscaleYon = bool(auto)
3623 if emit:
3624 self.callbacks.process('ylim_changed', self)
3625 # Call all of the other y-axes that are shared with this one
3626 for other in self._shared_y_axes.get_siblings(self):
3627 if other is not self:
3628 other.set_ylim(self.viewLim.intervaly,
3629 emit=False, auto=auto)
3630 if other.figure != self.figure:
3631 other.figure.canvas.draw_idle()
3632 self.stale = True
3633 return bottom, top
3635 def get_yscale(self):
3636 """
3637 Return the y-axis scale as string.
3639 See Also
3640 --------
3641 set_yscale
3642 """
3643 return self.yaxis.get_scale()
3645 def set_yscale(self, value, **kwargs):
3646 """
3647 Set the y-axis scale.
3649 Parameters
3650 ----------
3651 value : {"linear", "log", "symlog", "logit", ...}
3652 The axis scale type to apply.
3654 **kwargs
3655 Different keyword arguments are accepted, depending on the scale.
3656 See the respective class keyword arguments:
3658 - `matplotlib.scale.LinearScale`
3659 - `matplotlib.scale.LogScale`
3660 - `matplotlib.scale.SymmetricalLogScale`
3661 - `matplotlib.scale.LogitScale`
3663 Notes
3664 -----
3665 By default, Matplotlib supports the above mentioned scales.
3666 Additionally, custom scales may be registered using
3667 `matplotlib.scale.register_scale`. These scales can then also
3668 be used here.
3669 """
3670 old_default_lims = (self.yaxis.get_major_locator()
3671 .nonsingular(-np.inf, np.inf))
3672 g = self.get_shared_y_axes()
3673 for ax in g.get_siblings(self):
3674 ax.yaxis._set_scale(value, **kwargs)
3675 ax._update_transScale()
3676 ax.stale = True
3677 new_default_lims = (self.yaxis.get_major_locator()
3678 .nonsingular(-np.inf, np.inf))
3679 if old_default_lims != new_default_lims:
3680 # Force autoscaling now, to take advantage of the scale locator's
3681 # nonsingular() before it possibly gets swapped out by the user.
3682 self.autoscale_view(scalex=False)
3684 @cbook._make_keyword_only("3.2", "minor")
3685 def get_yticks(self, minor=False):
3686 """Return the y ticks as a list of locations"""
3687 return self.yaxis.get_ticklocs(minor=minor)
3689 @cbook._make_keyword_only("3.2", "minor")
3690 def set_yticks(self, ticks, minor=False):
3691 """
3692 Set the y ticks with list of *ticks*
3694 Parameters
3695 ----------
3696 ticks : list
3697 List of y-axis tick locations
3699 minor : bool, optional
3700 If ``False`` sets major ticks, if ``True`` sets minor ticks.
3701 Default is ``False``.
3702 """
3703 ret = self.yaxis.set_ticks(ticks, minor=minor)
3704 return ret
3706 def get_ymajorticklabels(self):
3707 """
3708 Get the major y tick labels.
3710 Returns
3711 -------
3712 labels : list
3713 List of `~matplotlib.text.Text` instances
3714 """
3715 return self.yaxis.get_majorticklabels()
3717 def get_yminorticklabels(self):
3718 """
3719 Get the minor y tick labels.
3721 Returns
3722 -------
3723 labels : list
3724 List of `~matplotlib.text.Text` instances
3725 """
3726 return self.yaxis.get_minorticklabels()
3728 def get_yticklabels(self, minor=False, which=None):
3729 """
3730 Get the y tick labels as a list of `~matplotlib.text.Text` instances.
3732 Parameters
3733 ----------
3734 minor : bool
3735 If True return the minor ticklabels,
3736 else return the major ticklabels
3738 which : None, ('minor', 'major', 'both')
3739 Overrides *minor*.
3741 Selects which ticklabels to return
3743 Returns
3744 -------
3745 ret : list
3746 List of `~matplotlib.text.Text` instances.
3747 """
3748 return self.yaxis.get_ticklabels(minor=minor, which=which)
3750 def set_yticklabels(self, labels, fontdict=None, minor=False, **kwargs):
3751 """
3752 Set the y-tick labels with list of strings labels.
3754 Parameters
3755 ----------
3756 labels : List[str]
3757 list of string labels
3759 fontdict : dict, optional
3760 A dictionary controlling the appearance of the ticklabels.
3761 The default *fontdict* is::
3763 {'fontsize': rcParams['axes.titlesize'],
3764 'fontweight': rcParams['axes.titleweight'],
3765 'verticalalignment': 'baseline',
3766 'horizontalalignment': loc}
3768 minor : bool, optional
3769 Whether to set the minor ticklabels rather than the major ones.
3771 Returns
3772 -------
3773 A list of `~.text.Text` instances.
3775 Other Parameters
3776 ----------------
3777 **kwargs : `~.text.Text` properties.
3778 """
3779 if fontdict is not None:
3780 kwargs.update(fontdict)
3781 return self.yaxis.set_ticklabels(labels,
3782 minor=minor, **kwargs)
3784 def xaxis_date(self, tz=None):
3785 """
3786 Sets up x-axis ticks and labels that treat the x data as dates.
3788 Parameters
3789 ----------
3790 tz : str or `tzinfo` instance, optional
3791 Timezone. Defaults to :rc:`timezone`.
3792 """
3793 # should be enough to inform the unit conversion interface
3794 # dates are coming in
3795 self.xaxis.axis_date(tz)
3797 def yaxis_date(self, tz=None):
3798 """
3799 Sets up y-axis ticks and labels that treat the y data as dates.
3801 Parameters
3802 ----------
3803 tz : str or `tzinfo` instance, optional
3804 Timezone. Defaults to :rc:`timezone`.
3805 """
3806 self.yaxis.axis_date(tz)
3808 def format_xdata(self, x):
3809 """
3810 Return *x* formatted as an x-value.
3812 This function will use the `.fmt_xdata` attribute if it is not None,
3813 else will fall back on the xaxis major formatter.
3814 """
3815 return (self.fmt_xdata if self.fmt_xdata is not None
3816 else self.xaxis.get_major_formatter().format_data_short)(x)
3818 def format_ydata(self, y):
3819 """
3820 Return *y* formatted as an y-value.
3822 This function will use the `.fmt_ydata` attribute if it is not None,
3823 else will fall back on the yaxis major formatter.
3824 """
3825 return (self.fmt_ydata if self.fmt_ydata is not None
3826 else self.yaxis.get_major_formatter().format_data_short)(y)
3828 def format_coord(self, x, y):
3829 """Return a format string formatting the *x*, *y* coordinates."""
3830 if x is None:
3831 xs = '???'
3832 else:
3833 xs = self.format_xdata(x)
3834 if y is None:
3835 ys = '???'
3836 else:
3837 ys = self.format_ydata(y)
3838 return 'x=%s y=%s' % (xs, ys)
3840 def minorticks_on(self):
3841 """
3842 Display minor ticks on the axes.
3844 Displaying minor ticks may reduce performance; you may turn them off
3845 using `minorticks_off()` if drawing speed is a problem.
3846 """
3847 for ax in (self.xaxis, self.yaxis):
3848 scale = ax.get_scale()
3849 if scale == 'log':
3850 s = ax._scale
3851 ax.set_minor_locator(mticker.LogLocator(s.base, s.subs))
3852 elif scale == 'symlog':
3853 s = ax._scale
3854 ax.set_minor_locator(
3855 mticker.SymmetricalLogLocator(s._transform, s.subs))
3856 else:
3857 ax.set_minor_locator(mticker.AutoMinorLocator())
3859 def minorticks_off(self):
3860 """Remove minor ticks from the axes."""
3861 self.xaxis.set_minor_locator(mticker.NullLocator())
3862 self.yaxis.set_minor_locator(mticker.NullLocator())
3864 # Interactive manipulation
3866 def can_zoom(self):
3867 """
3868 Return *True* if this axes supports the zoom box button functionality.
3869 """
3870 return True
3872 def can_pan(self):
3873 """
3874 Return *True* if this axes supports any pan/zoom button functionality.
3875 """
3876 return True
3878 def get_navigate(self):
3879 """
3880 Get whether the axes responds to navigation commands
3881 """
3882 return self._navigate
3884 def set_navigate(self, b):
3885 """
3886 Set whether the axes responds to navigation toolbar commands
3888 Parameters
3889 ----------
3890 b : bool
3891 """
3892 self._navigate = b
3894 def get_navigate_mode(self):
3895 """
3896 Get the navigation toolbar button status: 'PAN', 'ZOOM', or None
3897 """
3898 return self._navigate_mode
3900 def set_navigate_mode(self, b):
3901 """
3902 Set the navigation toolbar button status;
3904 .. warning::
3905 this is not a user-API function.
3907 """
3908 self._navigate_mode = b
3910 def _get_view(self):
3911 """
3912 Save information required to reproduce the current view.
3914 Called before a view is changed, such as during a pan or zoom
3915 initiated by the user. You may return any information you deem
3916 necessary to describe the view.
3918 .. note::
3920 Intended to be overridden by new projection types, but if not, the
3921 default implementation saves the view limits. You *must* implement
3922 :meth:`_set_view` if you implement this method.
3923 """
3924 xmin, xmax = self.get_xlim()
3925 ymin, ymax = self.get_ylim()
3926 return (xmin, xmax, ymin, ymax)
3928 def _set_view(self, view):
3929 """
3930 Apply a previously saved view.
3932 Called when restoring a view, such as with the navigation buttons.
3934 .. note::
3936 Intended to be overridden by new projection types, but if not, the
3937 default implementation restores the view limits. You *must*
3938 implement :meth:`_get_view` if you implement this method.
3939 """
3940 xmin, xmax, ymin, ymax = view
3941 self.set_xlim((xmin, xmax))
3942 self.set_ylim((ymin, ymax))
3944 def _set_view_from_bbox(self, bbox, direction='in',
3945 mode=None, twinx=False, twiny=False):
3946 """
3947 Update view from a selection bbox.
3949 .. note::
3951 Intended to be overridden by new projection types, but if not, the
3952 default implementation sets the view limits to the bbox directly.
3954 Parameters
3955 ----------
3956 bbox : 4-tuple or 3 tuple
3957 * If bbox is a 4 tuple, it is the selected bounding box limits,
3958 in *display* coordinates.
3959 * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where
3960 (xp, yp) is the center of zooming and scl the scale factor to
3961 zoom by.
3963 direction : str
3964 The direction to apply the bounding box.
3965 * `'in'` - The bounding box describes the view directly, i.e.,
3966 it zooms in.
3967 * `'out'` - The bounding box describes the size to make the
3968 existing view, i.e., it zooms out.
3970 mode : str or None
3971 The selection mode, whether to apply the bounding box in only the
3972 `'x'` direction, `'y'` direction or both (`None`).
3974 twinx : bool
3975 Whether this axis is twinned in the *x*-direction.
3977 twiny : bool
3978 Whether this axis is twinned in the *y*-direction.
3979 """
3980 Xmin, Xmax = self.get_xlim()
3981 Ymin, Ymax = self.get_ylim()
3983 if len(bbox) == 3:
3984 # Zooming code
3985 xp, yp, scl = bbox
3987 # Should not happen
3988 if scl == 0:
3989 scl = 1.
3991 # direction = 'in'
3992 if scl > 1:
3993 direction = 'in'
3994 else:
3995 direction = 'out'
3996 scl = 1/scl
3998 # get the limits of the axes
3999 tranD2C = self.transData.transform
4000 xmin, ymin = tranD2C((Xmin, Ymin))
4001 xmax, ymax = tranD2C((Xmax, Ymax))
4003 # set the range
4004 xwidth = xmax - xmin
4005 ywidth = ymax - ymin
4006 xcen = (xmax + xmin)*.5
4007 ycen = (ymax + ymin)*.5
4008 xzc = (xp*(scl - 1) + xcen)/scl
4009 yzc = (yp*(scl - 1) + ycen)/scl
4011 bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl,
4012 xzc + xwidth/2./scl, yzc + ywidth/2./scl]
4013 elif len(bbox) != 4:
4014 # should be len 3 or 4 but nothing else
4015 cbook._warn_external(
4016 "Warning in _set_view_from_bbox: bounding box is not a tuple "
4017 "of length 3 or 4. Ignoring the view change.")
4018 return
4020 # Just grab bounding box
4021 lastx, lasty, x, y = bbox
4023 # zoom to rect
4024 inverse = self.transData.inverted()
4025 (lastx, lasty), (x, y) = inverse.transform([(lastx, lasty), (x, y)])
4027 if twinx:
4028 x0, x1 = Xmin, Xmax
4029 else:
4030 if Xmin < Xmax:
4031 if x < lastx:
4032 x0, x1 = x, lastx
4033 else:
4034 x0, x1 = lastx, x
4035 if x0 < Xmin:
4036 x0 = Xmin
4037 if x1 > Xmax:
4038 x1 = Xmax
4039 else:
4040 if x > lastx:
4041 x0, x1 = x, lastx
4042 else:
4043 x0, x1 = lastx, x
4044 if x0 > Xmin:
4045 x0 = Xmin
4046 if x1 < Xmax:
4047 x1 = Xmax
4049 if twiny:
4050 y0, y1 = Ymin, Ymax
4051 else:
4052 if Ymin < Ymax:
4053 if y < lasty:
4054 y0, y1 = y, lasty
4055 else:
4056 y0, y1 = lasty, y
4057 if y0 < Ymin:
4058 y0 = Ymin
4059 if y1 > Ymax:
4060 y1 = Ymax
4061 else:
4062 if y > lasty:
4063 y0, y1 = y, lasty
4064 else:
4065 y0, y1 = lasty, y
4066 if y0 > Ymin:
4067 y0 = Ymin
4068 if y1 < Ymax:
4069 y1 = Ymax
4071 if direction == 'in':
4072 if mode == 'x':
4073 self.set_xlim((x0, x1))
4074 elif mode == 'y':
4075 self.set_ylim((y0, y1))
4076 else:
4077 self.set_xlim((x0, x1))
4078 self.set_ylim((y0, y1))
4079 elif direction == 'out':
4080 if self.get_xscale() == 'log':
4081 alpha = np.log(Xmax / Xmin) / np.log(x1 / x0)
4082 rx1 = pow(Xmin / x0, alpha) * Xmin
4083 rx2 = pow(Xmax / x0, alpha) * Xmin
4084 else:
4085 alpha = (Xmax - Xmin) / (x1 - x0)
4086 rx1 = alpha * (Xmin - x0) + Xmin
4087 rx2 = alpha * (Xmax - x0) + Xmin
4088 if self.get_yscale() == 'log':
4089 alpha = np.log(Ymax / Ymin) / np.log(y1 / y0)
4090 ry1 = pow(Ymin / y0, alpha) * Ymin
4091 ry2 = pow(Ymax / y0, alpha) * Ymin
4092 else:
4093 alpha = (Ymax - Ymin) / (y1 - y0)
4094 ry1 = alpha * (Ymin - y0) + Ymin
4095 ry2 = alpha * (Ymax - y0) + Ymin
4097 if mode == 'x':
4098 self.set_xlim((rx1, rx2))
4099 elif mode == 'y':
4100 self.set_ylim((ry1, ry2))
4101 else:
4102 self.set_xlim((rx1, rx2))
4103 self.set_ylim((ry1, ry2))
4105 def start_pan(self, x, y, button):
4106 """
4107 Called when a pan operation has started.
4109 *x*, *y* are the mouse coordinates in display coords.
4110 button is the mouse button number:
4112 * 1: LEFT
4113 * 2: MIDDLE
4114 * 3: RIGHT
4116 .. note::
4118 Intended to be overridden by new projection types.
4120 """
4121 self._pan_start = types.SimpleNamespace(
4122 lim=self.viewLim.frozen(),
4123 trans=self.transData.frozen(),
4124 trans_inverse=self.transData.inverted().frozen(),
4125 bbox=self.bbox.frozen(),
4126 x=x,
4127 y=y)
4129 def end_pan(self):
4130 """
4131 Called when a pan operation completes (when the mouse button
4132 is up.)
4134 .. note::
4136 Intended to be overridden by new projection types.
4138 """
4139 del self._pan_start
4141 def drag_pan(self, button, key, x, y):
4142 """
4143 Called when the mouse moves during a pan operation.
4145 *button* is the mouse button number:
4147 * 1: LEFT
4148 * 2: MIDDLE
4149 * 3: RIGHT
4151 *key* is a "shift" key
4153 *x*, *y* are the mouse coordinates in display coords.
4155 .. note::
4157 Intended to be overridden by new projection types.
4159 """
4160 def format_deltas(key, dx, dy):
4161 if key == 'control':
4162 if abs(dx) > abs(dy):
4163 dy = dx
4164 else:
4165 dx = dy
4166 elif key == 'x':
4167 dy = 0
4168 elif key == 'y':
4169 dx = 0
4170 elif key == 'shift':
4171 if 2 * abs(dx) < abs(dy):
4172 dx = 0
4173 elif 2 * abs(dy) < abs(dx):
4174 dy = 0
4175 elif abs(dx) > abs(dy):
4176 dy = dy / abs(dy) * abs(dx)
4177 else:
4178 dx = dx / abs(dx) * abs(dy)
4179 return dx, dy
4181 p = self._pan_start
4182 dx = x - p.x
4183 dy = y - p.y
4184 if dx == dy == 0:
4185 return
4186 if button == 1:
4187 dx, dy = format_deltas(key, dx, dy)
4188 result = p.bbox.translated(-dx, -dy).transformed(p.trans_inverse)
4189 elif button == 3:
4190 try:
4191 dx = -dx / self.bbox.width
4192 dy = -dy / self.bbox.height
4193 dx, dy = format_deltas(key, dx, dy)
4194 if self.get_aspect() != 'auto':
4195 dx = dy = 0.5 * (dx + dy)
4196 alpha = np.power(10.0, (dx, dy))
4197 start = np.array([p.x, p.y])
4198 oldpoints = p.lim.transformed(p.trans)
4199 newpoints = start + alpha * (oldpoints - start)
4200 result = (mtransforms.Bbox(newpoints)
4201 .transformed(p.trans_inverse))
4202 except OverflowError:
4203 cbook._warn_external('Overflow while panning')
4204 return
4205 else:
4206 return
4208 valid = np.isfinite(result.transformed(p.trans))
4209 points = result.get_points().astype(object)
4210 # Just ignore invalid limits (typically, underflow in log-scale).
4211 points[~valid] = None
4212 self.set_xlim(points[:, 0])
4213 self.set_ylim(points[:, 1])
4215 def get_children(self):
4216 # docstring inherited.
4217 return [
4218 *self.collections,
4219 *self.patches,
4220 *self.lines,
4221 *self.texts,
4222 *self.artists,
4223 *self.spines.values(),
4224 *self._get_axis_list(),
4225 self.title, self._left_title, self._right_title,
4226 *self.tables,
4227 *self.images,
4228 *self.child_axes,
4229 *([self.legend_] if self.legend_ is not None else []),
4230 self.patch,
4231 ]
4233 def contains(self, mouseevent):
4234 # docstring inherited.
4235 inside, info = self._default_contains(mouseevent)
4236 if inside is not None:
4237 return inside, info
4238 return self.patch.contains(mouseevent)
4240 def contains_point(self, point):
4241 """
4242 Return whether *point* (pair of pixel coordinates) is inside the axes
4243 patch.
4244 """
4245 return self.patch.contains_point(point, radius=1.0)
4247 def get_default_bbox_extra_artists(self):
4248 """
4249 Return a default list of artists that are used for the bounding box
4250 calculation.
4252 Artists are excluded either by not being visible or
4253 ``artist.set_in_layout(False)``.
4254 """
4256 artists = self.get_children()
4258 if not (self.axison and self._frameon):
4259 # don't do bbox on spines if frame not on.
4260 for spine in self.spines.values():
4261 artists.remove(spine)
4263 if not self.axison:
4264 for _axis in self._get_axis_list():
4265 artists.remove(_axis)
4267 return [artist for artist in artists
4268 if (artist.get_visible() and artist.get_in_layout())]
4270 def get_tightbbox(self, renderer, call_axes_locator=True,
4271 bbox_extra_artists=None):
4272 """
4273 Return the tight bounding box of the axes, including axis and their
4274 decorators (xlabel, title, etc).
4276 Artists that have ``artist.set_in_layout(False)`` are not included
4277 in the bbox.
4279 Parameters
4280 ----------
4281 renderer : `.RendererBase` instance
4282 renderer that will be used to draw the figures (i.e.
4283 ``fig.canvas.get_renderer()``)
4285 bbox_extra_artists : list of `.Artist` or ``None``
4286 List of artists to include in the tight bounding box. If
4287 ``None`` (default), then all artist children of the axes are
4288 included in the tight bounding box.
4290 call_axes_locator : boolean (default ``True``)
4291 If *call_axes_locator* is ``False``, it does not call the
4292 ``_axes_locator`` attribute, which is necessary to get the correct
4293 bounding box. ``call_axes_locator=False`` can be used if the
4294 caller is only interested in the relative size of the tightbbox
4295 compared to the axes bbox.
4297 Returns
4298 -------
4299 bbox : `.BboxBase`
4300 bounding box in figure pixel coordinates.
4302 See Also
4303 --------
4304 matplotlib.axes.Axes.get_window_extent
4305 matplotlib.axis.Axis.get_tightbbox
4306 matplotlib.spines.Spine.get_window_extent
4308 """
4310 bb = []
4312 if not self.get_visible():
4313 return None
4315 locator = self.get_axes_locator()
4316 if locator and call_axes_locator:
4317 pos = locator(self, renderer)
4318 self.apply_aspect(pos)
4319 else:
4320 self.apply_aspect()
4322 if self.axison:
4323 bb_xaxis = self.xaxis.get_tightbbox(renderer)
4324 if bb_xaxis:
4325 bb.append(bb_xaxis)
4327 bb_yaxis = self.yaxis.get_tightbbox(renderer)
4328 if bb_yaxis:
4329 bb.append(bb_yaxis)
4331 self._update_title_position(renderer)
4332 axbbox = self.get_window_extent(renderer)
4333 bb.append(axbbox)
4335 self._update_title_position(renderer)
4336 if self.title.get_visible():
4337 bb.append(self.title.get_window_extent(renderer))
4338 if self._left_title.get_visible():
4339 bb.append(self._left_title.get_window_extent(renderer))
4340 if self._right_title.get_visible():
4341 bb.append(self._right_title.get_window_extent(renderer))
4343 bb.append(self.get_window_extent(renderer))
4345 bbox_artists = bbox_extra_artists
4346 if bbox_artists is None:
4347 bbox_artists = self.get_default_bbox_extra_artists()
4349 for a in bbox_artists:
4350 # Extra check here to quickly see if clipping is on and
4351 # contained in the axes. If it is, don't get the tightbbox for
4352 # this artist because this can be expensive:
4353 clip_extent = a._get_clipping_extent_bbox()
4354 if clip_extent is not None:
4355 clip_extent = mtransforms.Bbox.intersection(clip_extent,
4356 axbbox)
4357 if np.all(clip_extent.extents == axbbox.extents):
4358 # clip extent is inside the axes bbox so don't check
4359 # this artist
4360 continue
4361 bbox = a.get_tightbbox(renderer)
4362 if (bbox is not None
4363 and 0 < bbox.width < np.inf
4364 and 0 < bbox.height < np.inf):
4365 bb.append(bbox)
4366 _bbox = mtransforms.Bbox.union(
4367 [b for b in bb if b.width != 0 or b.height != 0])
4369 return _bbox
4371 def _make_twin_axes(self, *args, **kwargs):
4372 """Make a twinx axes of self. This is used for twinx and twiny."""
4373 # Typically, SubplotBase._make_twin_axes is called instead of this.
4374 if 'sharex' in kwargs and 'sharey' in kwargs:
4375 raise ValueError("Twinned Axes may share only one axis")
4376 ax2 = self.figure.add_axes(self.get_position(True), *args, **kwargs)
4377 self.set_adjustable('datalim')
4378 ax2.set_adjustable('datalim')
4379 self._twinned_axes.join(self, ax2)
4380 return ax2
4382 def twinx(self):
4383 """
4384 Create a twin Axes sharing the xaxis.
4386 Create a new Axes with an invisible x-axis and an independent
4387 y-axis positioned opposite to the original one (i.e. at right). The
4388 x-axis autoscale setting will be inherited from the original
4389 Axes. To ensure that the tick marks of both y-axes align, see
4390 `~matplotlib.ticker.LinearLocator`.
4392 Returns
4393 -------
4394 ax_twin : Axes
4395 The newly created Axes instance
4397 Notes
4398 -----
4399 For those who are 'picking' artists while using twinx, pick
4400 events are only called for the artists in the top-most axes.
4401 """
4402 ax2 = self._make_twin_axes(sharex=self)
4403 ax2.yaxis.tick_right()
4404 ax2.yaxis.set_label_position('right')
4405 ax2.yaxis.set_offset_position('right')
4406 ax2.set_autoscalex_on(self.get_autoscalex_on())
4407 self.yaxis.tick_left()
4408 ax2.xaxis.set_visible(False)
4409 ax2.patch.set_visible(False)
4410 return ax2
4412 def twiny(self):
4413 """
4414 Create a twin Axes sharing the yaxis.
4416 Create a new Axes with an invisible y-axis and an independent
4417 x-axis positioned opposite to the original one (i.e. at top). The
4418 y-axis autoscale setting will be inherited from the original Axes.
4419 To ensure that the tick marks of both x-axes align, see
4420 `~matplotlib.ticker.LinearLocator`.
4422 Returns
4423 -------
4424 ax_twin : Axes
4425 The newly created Axes instance
4427 Notes
4428 -----
4429 For those who are 'picking' artists while using twiny, pick
4430 events are only called for the artists in the top-most axes.
4431 """
4432 ax2 = self._make_twin_axes(sharey=self)
4433 ax2.xaxis.tick_top()
4434 ax2.xaxis.set_label_position('top')
4435 ax2.set_autoscaley_on(self.get_autoscaley_on())
4436 self.xaxis.tick_bottom()
4437 ax2.yaxis.set_visible(False)
4438 ax2.patch.set_visible(False)
4439 return ax2
4441 def get_shared_x_axes(self):
4442 """Return a reference to the shared axes Grouper object for x axes."""
4443 return self._shared_x_axes
4445 def get_shared_y_axes(self):
4446 """Return a reference to the shared axes Grouper object for y axes."""
4447 return self._shared_y_axes