diff --git a/docs/matplotlibrc b/docs/matplotlibrc new file mode 100644 index 000000000..c212bcbee --- /dev/null +++ b/docs/matplotlibrc @@ -0,0 +1,3 @@ +backend : Agg +savefig.dpi : 80 # figure dots per inch +docstring.hardcopy : True # set this when you want to generate hardcopy docstring diff --git a/proplot/axes.py b/proplot/axes.py index 9d372429b..89ff3dbfc 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -2,8 +2,11 @@ """ The axes classes used for all ProPlot figures. """ +import re import numpy as np +import inspect import functools +from matplotlib import docstring from numbers import Integral, Number import matplotlib.projections as mproj import matplotlib.axes as maxes @@ -15,17 +18,16 @@ import matplotlib.gridspec as mgridspec import matplotlib.transforms as mtransforms import matplotlib.collections as mcollections -from . import projs, axistools +import matplotlib.patheffects as mpatheffects +from . import projs, axistools, styletools from .utils import _warn_proplot, _notNone, units, arange, edges from .rctools import rc, _rc_nodots from .wrappers import ( - _get_transform, _norecurse, _redirect, - _add_errorbars, _bar_wrapper, _barh_wrapper, _boxplot_wrapper, + _norecurse, _redirect, + _add_errorbars, _default_crs, _default_latlon, _default_transform, _cmap_changer, - _cycle_changer, _fill_between_wrapper, _fill_betweenx_wrapper, - _hist_wrapper, _plot_wrapper, _scatter_wrapper, + _cycle_changer, _standardize_1d, _standardize_2d, - _text_wrapper, _violinplot_wrapper, colorbar_wrapper, legend_wrapper, ) try: @@ -82,6 +84,76 @@ } +def _get_transform(self, transform): + """Translates user input transform. Also used in an axes method.""" + if isinstance(transform, mtransforms.Transform): + return transform + elif transform == 'figure': + return self.figure.transFigure + elif transform == 'axes': + return self.transAxes + elif transform == 'data': + return self.transData + else: + raise ValueError(f'Unknown transform {transform!r}.') + +# Concatenates docstrings and obfuscates call signature +# NOTE: Originally had idea to use numpydoc.docscrape.NumpyDocString to +# interpolate docstrings but *enormous* number of assupmtions would go into +# this. And simple is better than complex. + + +def _concatenate_docstrings(func): + """Concatenates docstrings from a matplotlib axes method with a ProPlot + axes method. Requires that proplot documentation has no other parameters, + notes, or examples sections.""" + # Get matplotlib axes func + # If current func has no docstring just blindly copy matplotlib one + name = func.__name__ + orig = getattr(maxes.Axes, name) + odoc = inspect.getdoc(orig) + if not odoc: # should never happen + return func + + # Prepend summary and potentially bail + # TODO: Does this break anything on sphinx website? + fdoc = inspect.getdoc(func) or '' # also dedents + summary = odoc[:re.search(r'\.( | *\n|\Z)', odoc).start() + 1] + fdoc = f'{summary}\n\n{fdoc}' + if rc.get('docstring.hardcopy'): # True when running sphinx + func.__doc__ = fdoc + return func + + # Obfuscate signature by converting to *args **kwargs. Note this does + # not change behavior of function! Copy parameters from a dummy function + # because I'm too lazy to figure out inspect.Parameters API + # See: https://stackoverflow.com/a/33112180/4970632 + def _dummy(*args, **kwargs): + pass + dsig = inspect.signature(_dummy) + fsig = inspect.signature(func) + func.__signature__ = ( + fsig.replace(parameters=tuple(dsig.parameters.values()))) + + # Concatenate docstrings and copy summary + # Make sure different sections are very visible + doc = f""" +==========================={"="*len(name)} +proplot.Axes.{name} documentation +==========================={"="*len(name)} +{fdoc} + +==================================={"="*len(name)} +matplotlib.axes.Axes.{name} documentation +==================================={"="*len(name)} +{odoc} +""" + func.__doc__ = doc + + # Return + return func + + def _abc(i): """Function for a-b-c labeling, returns a...z...aa...zz...aaa...zzz.""" if i < 26: @@ -662,8 +734,7 @@ def format( positioned inside the axes. This can help them stand out on top of artists plotted inside the axes. Defaults are :rc:`abc.border` and :rc:`title.border` - ltitle, rtitle, ultitle, uctitle, urtitle, lltitle, lctitle, lrtitle \ -: str, optional + ltitle, rtitle, ultitle, uctitle, urtitle, lltitle, lctitle, lrtitle : str, optional Axes titles in particular positions. This lets you specify multiple "titles" for each subplots. See the `abcloc` keyword. top : bool, optional @@ -675,8 +746,7 @@ def format( llabels, tlabels, rlabels, blabels : list of str, optional Aliases for `leftlabels`, `toplabels`, `rightlabels`, `bottomlabels`. - leftlabels, toplabels, rightlabels, bottomlabels : list of str, \ -optional + leftlabels, toplabels, rightlabels, bottomlabels : list of str, optional The subplot row and column labels. If list, length must match the number of subplots on the left, top, right, or bottom edges of the figure. @@ -701,7 +771,7 @@ def format( :py:obj:`XYAxes.format`, :py:obj:`ProjAxes.format`, :py:obj:`PolarAxes.format`, - """ + """ # noqa # Figure patch (for some reason needs to be re-asserted even if # declared before figure is drawn) kw = rc.fill({'facecolor': 'figure.facecolor'}, context=True) @@ -856,10 +926,208 @@ def areax(self, *args, **kwargs): """Alias for `~matplotlib.axes.Axes.fill_betweenx`.""" return self.fill_betweenx(*args, **kwargs) + @_concatenate_docstrings + @docstring.dedent_interpd + def bar(self, x=None, height=None, width=0.8, bottom=None, *, left=None, + vert=None, orientation='vertical', stacked=False, + lw=None, linewidth=0.7, edgecolor='k', + **kwargs): + """ + Permits bar stacking and bar grouping. + + Parameters + ---------- + %(standardize_1d_args)s + x, height, width, bottom : float or list of float, optional + The dimensions of the bars. If the *x* coordinates are not + provided, they are set to ``np.arange(0, len(height))``. + orientation : {'vertical', 'horizontal'}, optional + The orientation of the bars. + vert : bool, optional + Alternative to the `orientation` keyword arg. If ``False``, + horizontal bars are drawn. This is for consistency with + `~matplotlib.axes.Axes.boxplot` and + `~matplotlib.axes.Axes.violinplot`. + stacked : bool, optional + Whether to stack columns of input data, or plot the bars + side-by-side. + edgecolor : color-spec, optional + The edge color for the bar patches. + lw, linewidth : float, optional + The edge width for the bar patches. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s + %(add_errorbars_kwargs)s + + Other parameters + ---------------- + **kwargs + Passed to `~matplotlib.axes.Axes.bar`. + """ + # Barh converts y-->bottom, left-->x, width-->height, height-->width. + # Convert back to (x, bottom, width, height) so we can pass stuff + # through cycle_changer. + # NOTE: You *must* do juggling of barh keyword order --> bar keyword + # order --> barh keyword order, because horizontal hist passes + # arguments to bar directly and will not use a 'barh' method with + # overridden argument order! + if vert is not None: + orientation = ('vertical' if vert else 'horizontal') + if orientation == 'horizontal': + x, bottom = bottom, x + width, height = height, width + + # Parse args + # TODO: Stacked feature is implemented in `cycle_changer`, but makes + # more sense do document here; figure out way to move it here? + if left is not None: + _warn_proplot( + 'The "left" keyword with bar() is deprecated. ' + 'Use "x" instead.') + x = left + if x is None and height is None: + raise ValueError( + f'bar() requires at least 1 positional argument, got 0.') + elif height is None: + x, height = None, x + + # Call func + # TODO: This *must* also be wrapped by cycle_changer, which ultimately + # permutes back the x/bottom args for horizontal bars! Need to clean + # this up. + lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) + bar = _standardize_1d(_add_errorbars(_cycle_changer( + maxes.Axes.bar + ))) + return bar(self, x, height, width=width, bottom=bottom, + linewidth=lw, edgecolor=edgecolor, + stacked=stacked, orientation=orientation, + **kwargs) + + @_concatenate_docstrings + @docstring.dedent_interpd + def barh(self, y=None, width=None, height=0.8, left=None, **kwargs): + """Usage is same as `bar`.""" + kwargs.setdefault('orientation', 'horizontal') + if y is None and width is None: + raise ValueError( + f'barh() requires at least 1 positional argument, got 0.') + return self.bar(x=left, height=height, width=width, bottom=y, **kwargs) + def boxes(self, *args, **kwargs): """Alias for `~matplotlib.axes.Axes.boxplot`.""" return self.boxplot(*args, **kwargs) + @_concatenate_docstrings + @docstring.dedent_interpd + def boxplot( + self, *args, + color='k', fill=True, fillcolor=None, fillalpha=0.7, + lw=None, linewidth=0.7, orientation=None, + marker=None, markersize=None, + boxcolor=None, boxlw=None, + capcolor=None, caplw=None, + meancolor=None, meanlw=None, + mediancolor=None, medianlw=None, + whiskercolor=None, whiskerlw=None, + fliercolor=None, flierlw=None, + **kwargs + ): + """ + Adds convenient keyword args. Fills the objects with a cycle color + by default. + + Parameters + ---------- + %(standardize_1d_args)s + color : color-spec, optional + The color of all objects. + fill : bool, optional + Whether to fill the box with a color. + fillcolor : color-spec, optional + The fill color for the boxes. Default is the next color + cycler color. + fillalpha : float, optional + The opacity of the boxes. Default is ``1``. + lw, linewidth : float, optional + The linewidth of all objects. + orientation : {None, 'horizontal', 'vertical'}, optional + Alternative to the native `vert` keyword arg. Controls orientation. + marker : marker-spec, optional + Marker style for the 'fliers', i.e. outliers. + markersize : float, optional + Marker size for the 'fliers', i.e. outliers. + boxcolor, capcolor, meancolor, mediancolor, whiskercolor : color-spec, optional + The color of various boxplot components. These are + shorthands so you don't have to pass e.g. a ``boxprops`` + dictionary. + boxlw, caplw, meanlw, medianlw, whiskerlw : float, optional + The line width of various boxplot components. These are shorthands + so you don't have to pass e.g. a ``boxprops`` dictionary. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s + + Other parameters + ---------------- + **kwargs + Passed to `~matplotlib.axes.Axes.boxplot`. + """ # noqa + # Call function + if len(args) > 2: + raise ValueError(f'Expected 1-2 positional args, got {len(args)}.') + if orientation is not None: + if orientation == 'horizontal': + kwargs['vert'] = False + elif orientation != 'vertical': + raise ValueError( + 'Orientation must be "horizontal" or "vertical", ' + 'got {orientation!r}.') + boxplot = _standardize_1d(_cycle_changer( + maxes.Axes.boxplot + )) + obj = boxplot(self, *args, **kwargs) + if not args: + return obj + + # Modify results + # TODO: Pass props keyword args instead? Maybe does not matter. + lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) + if fillcolor is None: + cycler = next(self._get_lines.prop_cycler) + fillcolor = cycler.get('color', None) + for key, icolor, ilw in ( + ('boxes', boxcolor, boxlw), + ('caps', capcolor, caplw), + ('whiskers', whiskercolor, whiskerlw), + ('means', meancolor, meanlw), + ('medians', mediancolor, medianlw), + ('fliers', fliercolor, flierlw), + ): + if key not in obj: # possible if not rendered + continue + artists = obj[key] + ilw = _notNone(ilw, lw) + icolor = _notNone(icolor, color) + for artist in artists: + if icolor is not None: + artist.set_color(icolor) + artist.set_markeredgecolor(icolor) + if ilw is not None: + artist.set_linewidth(ilw) + artist.set_markeredgewidth(ilw) + if key == 'boxes' and fill: + patch = mpatches.PathPatch( + artist.get_path(), + color=fillcolor, alpha=fillalpha, linewidth=0) + self.add_artist(patch) + if key == 'fliers': + if marker is not None: + artist.set_marker(marker) + if markersize is not None: + artist.set_markersize(markersize) + return obj + + @docstring.dedent_interpd def colorbar( self, *args, loc=None, pad=None, length=None, width=None, space=None, frame=None, frameon=None, @@ -867,11 +1135,11 @@ def colorbar( **kwargs ): """ - Add an *inset* colorbar or *outer* colorbar along the outside edge of - the axes. See `~proplot.wrappers.colorbar_wrapper` for details. + Adds colorbar as an *inset* or along the outside edge of the axes. Parameters ---------- + %(colorbar_args)s loc : str, optional The colorbar location. Default is :rc:`colorbar.loc`. The following location keys are valid. @@ -919,8 +1187,7 @@ def colorbar( :rc:`colorbar.framealpha`, :rc:`axes.linewidth`, :rc:`axes.edgecolor`, and :rc:`axes.facecolor`, respectively. - **kwargs - Passed to `~proplot.wrappers.colorbar_wrapper`. + %(colorbar_kwargs)s """ # TODO: add option to pad inset away from axes edge! kwargs.update({'edgecolor': edgecolor, 'linewidth': linewidth}) @@ -1111,97 +1378,100 @@ def colorbar( # Generate colorbar return colorbar_wrapper(ax, *args, **kwargs) - def legend(self, *args, loc=None, width=None, space=None, **kwargs): + def draw(self, renderer=None, *args, **kwargs): + """Adds post-processing steps before axes is drawn.""" + self._reassign_title() + super().draw(renderer, *args, **kwargs) + + def _fill_between_apply(self, *args, betweenx=False, + negcolor='blue', poscolor='red', negpos=False, + **kwargs): + """Parse args and call function.""" + # Allow common keyword usage + x = 'y' if betweenx else 'x' + y = 'x' if betweenx else 'y' + if x in kwargs: + args = (kwargs.pop(x), *args) + for y in (y + '1', y + '2'): + if y in kwargs: + args = (*args, kwargs.pop(y)) + if len(args) == 1: + args = (np.arange(len(args[0])), *args) + if len(args) == 2: + if kwargs.get('stacked', False): + args = (*args, 0) + else: + args = (args[0], 0, args[1]) # default behavior + if len(args) != 3: + raise ValueError(f'Expected 2-3 positional args, got {len(args)}.') + fill_between = _standardize_1d(_cycle_changer( + maxes.Axes.fill_between + )) + if not negpos: + obj = fill_between(self, *args, **kwargs) + return obj + + # Get zero points + objs = [] + kwargs.setdefault('interpolate', True) + y1, y2 = np.atleast_1d( + args[-2]).squeeze(), np.atleast_1d(args[-1]).squeeze() + if y1.ndim > 1 or y2.ndim > 1: + raise ValueError( + f'When "negpos" is True, y must be 1-dimensional.') + if kwargs.get('where', None) is not None: + raise ValueError( + 'When "negpos" is True, you cannot set the "where" keyword.') + for i in range(2): + kw = {**kwargs} + kw.setdefault('color', negcolor if i == 0 else poscolor) + where = (y2 < y1) if i == 0 else (y2 >= y1) + obj = fill_between(self, *args, where=where, **kw) + objs.append(obj) + return (*objs,) + + @_concatenate_docstrings + @docstring.dedent_interpd + def fill_between(self, *args, **kwargs): """ - Add an *inset* legend or *outer* legend along the edge of the axes. - See `~proplot.wrappers.legend_wrapper` for details. + Also accessible via the `~proplot.axes.Axes.area` alias. Parameters ---------- - loc : int or str, optional - The legend location or panel location. The following location keys - are valid. Note that if a panel does not exist, it will be - generated on-the-fly. - - ================== ======================================= - Location Valid keys - ================== ======================================= - left panel ``'left'``, ``'l'`` - right panel ``'right'``, ``'r'`` - bottom panel ``'bottom'``, ``'b'`` - top panel ``'top'``, ``'t'`` - "best" inset ``'best'``, ``'inset'``, ``'i'``, ``0`` - upper right inset ``'upper right'``, ``'ur'``, ``1`` - upper left inset ``'upper left'``, ``'ul'``, ``2`` - lower left inset ``'lower left'``, ``'ll'``, ``3`` - lower right inset ``'lower right'``, ``'lr'``, ``4`` - center left inset ``'center left'``, ``'cl'``, ``5`` - center right inset ``'center right'``, ``'cr'``, ``6`` - lower center inset ``'lower center'``, ``'lc'``, ``7`` - upper center inset ``'upper center'``, ``'uc'``, ``8`` - center inset ``'center'``, ``'c'``, ``9`` - ================== ======================================= - - width : float or str, optional - For outer legends only. The space allocated for the legend box. - This does nothing if :rcraw:`tight` is ``True``. Units are - interpreted by `~proplot.utils.units`. - space : float or str, optional - For outer legends only. The space between the axes and the legend - box. Units are interpreted by `~proplot.utils.units`. - When :rcraw:`tight` is ``True``, this is adjusted automatically. - Otherwise, the default is :rc:`subplots.panelpad`. + %(standardize_1d_args)s + If `y1` and `y2` are provided, their shapes must be identical, and + we fill between respective columns of these arrays. + stacked : bool, optional + If `y2` is ``None``, this indicates whether to "stack" successive + columns of the `y1` array. + negpos : bool, optional + Whether to shade where `y2` is greater than `y1` with the color + `poscolor`, and where `y1` is greater than `y2` with the color + `negcolor`. For example, to shade positive values red and negtive + blue, use ``ax.fill_between(x, 0, y)``. + negcolor, poscolor : color-spec, optional + Colors to use for the negative and positive values. Ignored if + `negpos` is ``False``. + where : ndarray, optional + Boolean ndarray mask for points you want to shade. See + `this matplotlib example \ +`__. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s Other parameters ---------------- - *args, **kwargs - Passed to `~proplot.wrappers.legend_wrapper`. - """ - if loc != '_fill': - loc = self._loc_translate(loc, rc['legend.loc']) - if isinstance(loc, np.ndarray): - loc = loc.tolist() - - # Generate panel - if loc in ('left', 'right', 'top', 'bottom'): - ax = self.panel_axes(loc, width=width, space=space, filled=True) - return ax.legend(*args, loc='_fill', **kwargs) - - # Fill - if loc == '_fill': - # Hide content - for s in self.spines.values(): - s.set_visible(False) - self.xaxis.set_visible(False) - self.yaxis.set_visible(False) - self.patch.set_alpha(0) - self._panel_filled = True - # Try to make handles and stuff flush against the axes edge - kwargs.setdefault('borderaxespad', 0) - frameon = _notNone(kwargs.get('frame', None), kwargs.get( - 'frameon', None), rc['legend.frameon']) - if not frameon: - kwargs.setdefault('borderpad', 0) - # Apply legend location - side = self._panel_side - if side == 'bottom': - loc = 'upper center' - elif side == 'right': - loc = 'center left' - elif side == 'left': - loc = 'center right' - elif side == 'top': - loc = 'lower center' - else: - raise ValueError(f'Invalid panel side {side!r}.') + **kwargs + Passed to `~matplotlib.axes.Axes.fill_between`. + """ # noqa + return self._fill_between_apply(*args, betweenx=False, **kwargs) - # Draw legend - return legend_wrapper(self, *args, loc=loc, **kwargs) - - def draw(self, renderer=None, *args, **kwargs): - """Perform post-processing steps then draw the axes.""" - self._reassign_title() - super().draw(renderer, *args, **kwargs) + @_concatenate_docstrings + @docstring.dedent_interpd + def fill_betweenx(self, *args, **kwargs): + """Usage is same as `fill_between_wrapper`. Also accessible via the + `~proplot.axes.Axes.areax` alias.""" + return self._fill_between_apply(*args, betweenx=True, **kwargs) def get_size_inches(self): """Return the width and the height of the axes in inches. Similar @@ -1238,8 +1508,26 @@ def heatmap(self, *args, **kwargs): ) return obj + @_concatenate_docstrings + @docstring.dedent_interpd + def hist(self, x, bins=None, **kwargs): + """ + Enforces that all arguments after `bins` are + keyword-only and sets the default patch linewidth to ``0``. + + Parameters + ---------- + %(cycle_changer_kwargs)s + %(add_errorbars_kwargs)s + """ + kwargs.setdefault('linewidth', 0) + hist = _standardize_1d(_cycle_changer( + maxes.Axes.hist + )) + return hist(self, x, bins=bins, **kwargs) + def inset_axes( - self, bounds, *, transform=None, zorder=4, + self, bounds, *, transform=None, zorder=5, zoom=True, zoom_kw=None, **kwargs ): """ @@ -1250,8 +1538,7 @@ def inset_axes( ---------- bounds : list of float The bounds for the inset axes, listed as ``(x, y, width, height)``. - transform : {'data', 'axes', 'figure'} or \ -`~matplotlib.transforms.Transform`, optional + transform : {'data', 'axes', 'figure'} or `~matplotlib.transforms.Transform`, optional The transform used to interpret `bounds`. Can be a `~matplotlib.transforms.Transform` object or a string representing the `~matplotlib.axes.Axes.transData`, @@ -1275,7 +1562,7 @@ def inset_axes( ---------------- **kwargs Passed to `XYAxes`. - """ + """ # noqa # Carbon copy with my custom axes if not transform: transform = self.transAxes @@ -1365,6 +1652,97 @@ def indicate_inset_zoom( self._inset_zoom_data = (rectpatch, connects) return (rectpatch, connects) + @docstring.dedent_interpd + def legend(self, *args, loc=None, width=None, space=None, **kwargs): + """ + Adds an *inset* legend or *outer* legend along the edge of the axes. + See `~proplot.wrappers.legend_wrapper` for details. + + Parameters + ---------- + %(legend_args)s + loc : int or str, optional + The legend location or panel location. The following location keys + are valid. Note that if a panel does not exist, it will be + generated on-the-fly. + + ================== ======================================= + Location Valid keys + ================== ======================================= + left panel ``'left'``, ``'l'`` + right panel ``'right'``, ``'r'`` + bottom panel ``'bottom'``, ``'b'`` + top panel ``'top'``, ``'t'`` + "best" inset ``'best'``, ``'inset'``, ``'i'``, ``0`` + upper right inset ``'upper right'``, ``'ur'``, ``1`` + upper left inset ``'upper left'``, ``'ul'``, ``2`` + lower left inset ``'lower left'``, ``'ll'``, ``3`` + lower right inset ``'lower right'``, ``'lr'``, ``4`` + center left inset ``'center left'``, ``'cl'``, ``5`` + center right inset ``'center right'``, ``'cr'``, ``6`` + lower center inset ``'lower center'``, ``'lc'``, ``7`` + upper center inset ``'upper center'``, ``'uc'``, ``8`` + center inset ``'center'``, ``'c'``, ``9`` + ================== ======================================= + + width : float or str, optional + The space allocated for outer legends. This does nothing + if :rcraw:`tight` is ``True``. Units are interpreted by + `~proplot.utils.units`. + space : float or str, optional + The space between the axes and the legend for outer legends. + Units are interpreted by `~proplot.utils.units`. + When :rcraw:`tight` is ``True``, this is adjusted automatically. + Otherwise, defaut is :rc:`subplots.panelspace`. + %(legend_kwargs)s + + Other parameters + ---------------- + *args, **kwargs + Passed to `~proplot.wrappers.legend_wrapper`. + """ + loc = self._loc_translate(loc, width=width, space=space) + if isinstance(loc, np.ndarray): + loc = loc.tolist() + + # Generate panel + if loc in ('left', 'right', 'top', 'bottom'): + ax = self.panel_axes(loc, width=width, space=space, filled=True) + return ax.legend(*args, loc='_fill', **kwargs) + + # Fill + if loc == '_fill': + # Hide content + for s in self.spines.values(): + s.set_visible(False) + self.xaxis.set_visible(False) + self.yaxis.set_visible(False) + self.patch.set_alpha(0) + self._panel_filled = True + # Try to make handles and stuff flush against the axes edge + kwargs.setdefault('borderaxespad', 0) + frameon = _notNone( + kwargs.get( + 'frame', None), kwargs.get( + 'frameon', None), rc['legend.frameon']) + if not frameon: + kwargs.setdefault('borderpad', 0) + # Apply legend location + side = self._panel_side + if side == 'bottom': + loc = 'upper center' + elif side == 'right': + loc = 'center left' + elif side == 'left': + loc = 'center right' + elif side == 'top': + loc = 'lower center' + else: + raise ValueError(f'Invalid panel side {side!r}.') + + # Draw legend + return legend_wrapper(self, *args, loc=loc, **kwargs) + def panel_axes(self, side, **kwargs): """ Return a panel axes drawn along the edge of this axes. @@ -1393,6 +1771,314 @@ def panel_axes(self, side, **kwargs): """ return self.figure._add_axes_panel(self, side, **kwargs) + @_concatenate_docstrings + @docstring.dedent_interpd + def plot(self, *args, cmap=None, values=None, **kwargs): + """ + Draws a "colormap line" if the ``cmap`` argument was passed. Colormap + lines change color as a function of the parametric coordinate + ``values``. + + Parameters + ---------- + %(standardize_1d_args)s + cmap, values : optional + Passed to `~proplot.axes.Axes.cmapline`. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s + %(add_errorbars_kwargs)s + + Other parameters + ---------------- + **kwargs + `~matplotlib.lines.Line2D` properties. + """ + if len(args) > 3: # e.g. with fmt string + raise ValueError(f'Expected 1-3 positional args, got {len(args)}.') + if cmap is not None: + return self.cmapline(*args, cmap=cmap, values=values, **kwargs) + # Apply various wrappers + plot = _standardize_1d(_add_errorbars(_cycle_changer( + _redirect(maxes.Axes.plot) + ))) + return plot(self, *args, values=values, **kwargs) + + @_concatenate_docstrings + @docstring.dedent_interpd + def scatter( + self, *args, + s=None, size=None, markersize=None, + c=None, color=None, markercolor=None, + smin=None, smax=None, + cmap=None, cmap_kw=None, + vmin=None, vmax=None, norm=None, norm_kw=None, + lw=None, linewidth=None, linewidths=None, + markeredgewidth=None, markeredgewidths=None, + edgecolor=None, edgecolors=None, + markeredgecolor=None, markeredgecolors=None, + **kwargs): + """ + Adds optional keyword args more consistent with the + `~matplotlib.axes.Axes.plot` keywords. + + Parameters + ---------- + %(standardize_1d_args)s + s, size, markersize : float or list of float, optional + Aliases for the marker size. + smin, smax : float, optional + Used to scale the `s` array. These are the minimum and maximum + marker sizes. Defaults are the minimum and maximum of the `s` + array. + c, color, markercolor : color-spec or list thereof, or array, optional + Aliases for the marker fill color. If just an array of values, the + colors will be generated by passing the values through the `norm` + normalizer and drawing from the `cmap` colormap. + cmap : colormap-spec, optional + The colormap specifer, passed to the `~proplot.styletools.Colormap` + constructor. + cmap_kw : dict-like, optional + Passed to `~proplot.styletools.Colormap`. + vmin, vmax : float, optional + Used to generate a `norm` for scaling the `c` array. These are the + values corresponding to the leftmost and rightmost colors in the + colormap. Defaults are the minimum and maximum values of the + `c` array. + norm : normalizer spec, optional + The colormap normalizer, passed to the `~proplot.styletools.Norm` + constructor. + norm_kw : dict, optional + Passed to `~proplot.styletools.Norm`. + lw, linewidth, linewidths, markeredgewidth, markeredgewidths : float or list thereof, optional + Aliases for the marker edge width. + edgecolors, markeredgecolor, markeredgecolors : color-spec or list thereof, optional + Aliases for the marker edge color. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s + %(add_errorbars_kwargs)s + + Other parameters + ---------------- + **kwargs + Passed to `~matplotlib.axes.Axes.scatter`. + """ # noqa + # Manage input arguments + # NOTE: Parse 1D must come before this + nargs = len(args) + if len(args) > 4: + raise ValueError(f'Expected 1-4 positional args, got {nargs}.') + args = list(args) + if len(args) == 4: + c = args.pop(1) + if len(args) == 3: + s = args.pop(0) + + # Format cmap and norm + cmap_kw = cmap_kw or {} + norm_kw = norm_kw or {} + if cmap is not None: + cmap = styletools.Colormap(cmap, N=None, **cmap_kw) + if norm is not None: + norm = styletools.Norm(norm, N=None, **norm_kw) + + # Apply some aliases for keyword arguments + c = _notNone( + c, color, markercolor, None, names=( + 'c', 'color', 'markercolor')) + s = _notNone( + s, size, markersize, None, names=( + 's', 'size', 'markersize')) + lw = _notNone( + lw, linewidth, linewidths, markeredgewidth, markeredgewidths, None, + names=('lw', 'linewidth', 'linewidths', + 'markeredgewidth', 'markeredgewidths')) + ec = _notNone( + edgecolor, edgecolors, markeredgecolor, markeredgecolors, None, + names=('edgecolor', 'edgecolors', 'markeredgecolor', + 'markeredgecolors')) + + # Scale s array + if np.iterable(s): + smin_true, smax_true = min(s), max(s) + if smin is None: + smin = smin_true + if smax is None: + smax = smax_true + s = smin + (smax - smin) * ( + np.array(s) - smin_true) / (smax_true - smin_true) + scatter = _standardize_1d(_add_errorbars(_cycle_changer( + _redirect(maxes.Axes.scatter) + ))) + return scatter(self, *args, c=c, s=s, + cmap=cmap, vmin=vmin, vmax=vmax, + norm=norm, linewidths=lw, edgecolors=ec, + **kwargs) + + @_concatenate_docstrings + def text(self, + x=0, y=0, text='', transform='data', + fontfamily=None, fontname=None, fontsize=None, size=None, + border=False, bordercolor='w', invert=False, lw=None, linewidth=2, + **kwargs): + """ + Enables specifying `tranform` with a string name and + adds feature for drawing borders around text. + + Parameters + ---------- + x, y : float + The *x* and *y* coordinates for the text. + text : str + The text string. + transform : {'data', 'axes', 'figure'} or `~matplotlib.transforms.Transform`, optional + The transform used to interpret `x` and `y`. Can be a + `~matplotlib.transforms.Transform` object or a string representing + the `~matplotlib.axes.Axes.transData`, + `~matplotlib.axes.Axes.transAxes`, or + `~matplotlib.figure.Figure.transFigure` transforms. Default is + ``'data'``, i.e. the text is positioned in data coordinates. + size, fontsize : float or str, optional + The font size. If float, units are inches. If string, units are + interpreted by `~proplot.utils.units`. + fontname, fontfamily : str, optional + Aliases for the ``fontfamily`` `~matplotlib.text.Text` property. + border : bool, optional + Whether to draw border around text. + bordercolor : color-spec, optional + The color of the border. Default is ``'w'``. + invert : bool, optional + If ``False``, ``'color'`` is used for the text and ``bordercolor`` + for the border. If ``True``, this is inverted. + lw, linewidth : float, optional + Ignored if `border` is ``False``. The width of the text border. + + Other parameters + ---------------- + **kwargs + Passed to `~matplotlib.text.Text`. + """ # noqa + # Default transform by string name + if not transform: + transform = self.transData + else: + transform = _get_transform(self, transform) + + # More flexible keyword args and more helpful warning if invalid font + fontname = _notNone( + fontfamily, fontname, None, names=( + 'fontfamily', 'fontname')) + if fontname is not None: + if not isinstance(fontname, str) and np.iterable( + fontname) and len(fontname) == 1: + fontname = fontname[0] + if fontname in styletools.fonts: + kwargs['fontfamily'] = fontname + else: + _warn_proplot( + f'Font {fontname!r} unavailable. Available fonts are: ' + ', '.join(map(repr, styletools.fonts)) + '.') + size = _notNone(fontsize, size, None, names=('fontsize', 'size')) + if size is not None: + kwargs['fontsize'] = units(size, 'pt') + # text.color is ignored sometimes unless we apply this + kwargs.setdefault('color', rc.get('text.color')) + + # Draw text and optionally add border + obj = super().text(x, y, text, transform=transform, **kwargs) + if border: + linewidth = lw or linewidth + facecolor, bgcolor = kwargs['color'], bordercolor + if invert: + facecolor, bgcolor = bgcolor, facecolor + kwargs = { + 'linewidth': linewidth, + 'foreground': bgcolor, + 'joinstyle': 'miter'} + obj.update({ + 'color': facecolor, 'zorder': 100, + 'path_effects': [ + mpatheffects.Stroke(**kwargs), mpatheffects.Normal()] + }) + return obj + + @_concatenate_docstrings + @docstring.dedent_interpd + def violinplot( + self, *args, + lw=None, linewidth=0.7, fillcolor=None, + edgecolor='k', fillalpha=0.7, orientation=None, + **kwargs): + """ + Adds convenient keyword args. + Makes the style shown in right plot of `this matplotlib example \ +`__ + the default. It is also no longer possible to show minima and maxima + with whiskers, because this is redundant. + + Parameters + ---------- + %(standardize_1d_args)s + lw, linewidth : float, optional + The linewidth of the line objects. Default is ``1``. + edgecolor : color-spec, optional + The edge color for the violin patches. Default is ``'k'``. + fillcolor : color-spec, optional + The violin plot fill color. Default is the next color cycler color. + fillalpha : float, optional + The opacity of the violins. Default is ``1``. + orientation : {None, 'horizontal', 'vertical'}, optional + Alternative to the native `vert` keyword arg. Controls orientation. + boxrange, barrange : (float, float), optional + Percentile ranges for the thick and thin central bars. The defaults + are ``(25, 75)`` and ``(5, 95)``, respectively. + %(standardize_1d_kwargs)s + %(cycle_changer_kwargs)s + %(add_errorbars_kwargs)s + + Other parameters + ---------------- + **kwargs + Passed to `~matplotlib.axes.Axes.violinplot`. + """ # noqa + # Orientation and checks + if len(args) > 2: + raise ValueError(f'Expected 1-2 positional args, got {len(args)}.') + if orientation is not None: + if orientation == 'horizontal': + kwargs['vert'] = False + elif orientation != 'vertical': + raise ValueError( + 'Orientation must be "horizontal" or "vertical", ' + 'got {orientation!r}.') + + # Sanitize input + lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) + if kwargs.pop('showextrema', None): + _warn_proplot(f'Ignoring showextrema=True.') + if 'showmeans' in kwargs: + kwargs.setdefault('means', kwargs.pop('showmeans')) + if 'showmedians' in kwargs: + kwargs.setdefault('medians', kwargs.pop('showmedians')) + kwargs.setdefault('capsize', 0) + violinplot = _standardize_1d(_cycle_changer( + maxes.Axes.violinplot + )) + obj = violinplot(self, *args, + showmeans=False, showmedians=False, showextrema=False, + edgecolor=edgecolor, lw=lw, **kwargs) + if not args: + return obj + + # Modify body settings + for artist in obj['bodies']: + artist.set_alpha(fillalpha) + artist.set_edgecolor(edgecolor) + artist.set_linewidths(lw) + if fillcolor is not None: + artist.set_facecolor(fillcolor) + return obj + + @docstring.dedent_interpd @_standardize_1d @_cmap_changer def parametric( @@ -1540,39 +2226,6 @@ def number(self, num): raise ValueError(f'Invalid number {num!r}. Must be integer >=1.') self._number = num - # Wrapped by special functions - # Also support redirecting to Basemap methods - text = _text_wrapper( - maxes.Axes.text - ) - plot = _plot_wrapper(_standardize_1d(_add_errorbars(_cycle_changer( - maxes.Axes.plot - )))) - scatter = _scatter_wrapper(_standardize_1d(_add_errorbars(_cycle_changer( - maxes.Axes.scatter - )))) - bar = _bar_wrapper(_standardize_1d(_add_errorbars(_cycle_changer( - maxes.Axes.bar - )))) - barh = _barh_wrapper( - maxes.Axes.barh - ) # calls self.bar - hist = _hist_wrapper(_standardize_1d(_cycle_changer( - maxes.Axes.hist - ))) - boxplot = _boxplot_wrapper(_standardize_1d(_cycle_changer( - maxes.Axes.boxplot - ))) - violinplot = _violinplot_wrapper(_standardize_1d(_add_errorbars( - _cycle_changer(maxes.Axes.violinplot) - ))) - fill_between = _fill_between_wrapper(_standardize_1d(_cycle_changer( - maxes.Axes.fill_between - ))) - fill_betweenx = _fill_betweenx_wrapper(_standardize_1d(_cycle_changer( - maxes.Axes.fill_betweenx - ))) - # Wrapped by cycle wrapper and standardized pie = _standardize_1d(_cycle_changer( maxes.Axes.pie @@ -3577,21 +4230,18 @@ def projection(self, map_projection): # Wrapped methods # TODO: Remove this duplication! if GeoAxes is not object: - text = _text_wrapper( - GeoAxes.text - ) - plot = _default_transform(_plot_wrapper(_standardize_1d( + plot = _default_transform(_standardize_1d( _add_errorbars(_cycle_changer(GeoAxes.plot)) - ))) - scatter = _default_transform(_scatter_wrapper(_standardize_1d( + )) + scatter = _default_transform(_standardize_1d( _add_errorbars(_cycle_changer(GeoAxes.scatter)) - ))) - fill_between = _fill_between_wrapper(_standardize_1d(_cycle_changer( + )) + fill_between = _standardize_1d(_cycle_changer( GeoAxes.fill_between - ))) - fill_betweenx = _fill_betweenx_wrapper(_standardize_1d(_cycle_changer( + )) + fill_betweenx = _standardize_1d(_cycle_changer( GeoAxes.fill_betweenx - ))) + )) contour = _default_transform(_standardize_2d(_cmap_changer( GeoAxes.contour ))) @@ -3843,12 +4493,12 @@ def projection(self, map_projection): self._map_projection = map_projection # Wrapped methods - plot = _norecurse(_default_latlon(_plot_wrapper(_standardize_1d( + plot = _norecurse(_default_latlon(_standardize_1d( _add_errorbars(_cycle_changer(_redirect(maxes.Axes.plot))) - )))) - scatter = _norecurse(_default_latlon(_scatter_wrapper(_standardize_1d( + ))) + scatter = _norecurse(_default_latlon(_standardize_1d( _add_errorbars(_cycle_changer(_redirect(maxes.Axes.scatter))) - )))) + ))) contour = _norecurse(_default_latlon(_standardize_2d(_cmap_changer( _redirect(maxes.Axes.contour) )))) diff --git a/proplot/styletools.py b/proplot/styletools.py index ec54f52b4..2f475115b 100644 --- a/proplot/styletools.py +++ b/proplot/styletools.py @@ -724,6 +724,7 @@ class LinearSegmentedColormap(mcolors.LinearSegmentedColormap, _Colormap): r""" New base class for all `~matplotlib.colors.LinearSegmentedColormap`\ s. """ + def __str__(self): return type(self).__name__ + f'(name={self.name!r})' @@ -1269,6 +1270,7 @@ class ListedColormap(mcolors.ListedColormap, _Colormap): r""" New base class for all `~matplotlib.colors.ListedColormap`\ s. """ + def __str__(self): return f'ListedColormap(name={self.name!r})' @@ -2666,6 +2668,7 @@ class LinearSegmentedNorm(mcolors.Normalize): Can be used by passing ``norm='segmented'`` or ``norm='segments'`` to any command accepting ``cmap``. The default midpoint is zero. """ + def __init__(self, levels, vmin=None, vmax=None, **kwargs): """ Parameters diff --git a/proplot/subplots.py b/proplot/subplots.py index 3eb55484f..b75049e7d 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -13,6 +13,7 @@ import matplotlib.figure as mfigure import matplotlib.transforms as mtransforms import matplotlib.gridspec as mgridspec +from matplotlib import docstring from numbers import Integral from .rctools import rc from .utils import _warn_proplot, _notNone, _counter, _setstate, units # noqa @@ -53,6 +54,28 @@ 'pnas2': '11.4cm', 'pnas3': '17.8cm', } +# Table that goes in the subplots docstring +# Must be indented one space as this is embedded in parameter description +journal_doc = """ + =========== ==================== ========================================================================================================================================================== + Key Size description Organization + =========== ==================== ========================================================================================================================================================== + ``'pnas1'`` 1-column `Proceedings of the National Academy of Sciences `__ + ``'pnas2'`` 2-column ” + ``'pnas3'`` landscape page ” + ``'ams1'`` 1-column `American Meteorological Society `__ + ``'ams2'`` small 2-column ” + ``'ams3'`` medium 2-column ” + ``'ams4'`` full 2-column ” + ``'agu1'`` 1-column `American Geophysical Union `__ + ``'agu2'`` 2-column ” + ``'agu3'`` full height 1-column ” + ``'agu4'`` full height 2-column ” + ``'aaas1'`` 1-column `American Association for the Advancement of Science `__ + ``'aaas2'`` 2-column ” + =========== ==================== ========================================================================================================================================================== +""" # noqa +docstring.interpd.update(journal_doc=journal_doc) def close(*args, **kwargs): @@ -2035,6 +2058,7 @@ def _axes_dict(naxs, value, kw=False, default=None): return kwargs +@docstring.dedent_interpd def subplots( array=None, ncols=1, nrows=1, ref=1, order='C', @@ -2081,8 +2105,8 @@ def subplots( `~proplot.utils.units`. journal : str, optional String name corresponding to an academic journal standard that is used - to control the figure width (and height, if specified). See below - table. + to control the figure width (and height, if specified). Valid names + are as follows. =========== ==================== ========================================================================================================================================================== Key Size description Organization diff --git a/proplot/wrappers.py b/proplot/wrappers.py index 14f46cc5c..3e9349fc8 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -8,13 +8,13 @@ import numpy as np import numpy.ma as ma import functools -from . import styletools, axistools +from . import axistools, styletools from .utils import _warn_proplot, _notNone, edges, edges2d, units +from matplotlib import docstring import matplotlib.axes as maxes import matplotlib.contour as mcontour import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms -import matplotlib.patheffects as mpatheffects import matplotlib.patches as mpatches import matplotlib.colors as mcolors import matplotlib.artist as martist @@ -30,17 +30,7 @@ except ModuleNotFoundError: PlateCarree = object -__all__ = [ - 'add_errorbars', 'bar_wrapper', 'barh_wrapper', 'boxplot_wrapper', - 'default_crs', 'default_latlon', 'default_transform', - 'cmap_changer', - 'cycle_changer', - 'colorbar_wrapper', - 'fill_between_wrapper', 'fill_betweenx_wrapper', 'hist_wrapper', - 'legend_wrapper', 'plot_wrapper', 'scatter_wrapper', - 'standardize_1d', 'standardize_2d', 'text_wrapper', - 'violinplot_wrapper', -] +__all__ = [] # just hidden helper methods here! def _load_objects(): @@ -209,22 +199,6 @@ def _auto_label(data, axis=None, units=True): def standardize_1d(self, func, *args, **kwargs): - """ - Wraps %(methods)s, standardizes acceptable positional args and optionally - modifies the x axis label, y axis label, title, and axis ticks - if a `~xarray.DataArray`, `~pandas.DataFrame`, or `~pandas.Series` is - passed. - - Positional args are standardized as follows. - - * If a 2D array is passed, the corresponding plot command is called for - each column of data (except for ``boxplot`` and ``violinplot``, in which - case each column is interpreted as a distribution). - * If *x* and *y* or *latitude* and *longitude* coordinates were not - provided, and a `~pandas.DataFrame` or `~xarray.DataArray`, we - try to infer them from the metadata. Otherwise, - ``np.arange(0, data.shape[0])`` is used. - """ # Sanitize input # TODO: Add exceptions for methods other than 'hist'? name = func.__name__ @@ -321,6 +295,28 @@ def standardize_1d(self, func, *args, **kwargs): return func(self, x, *ys, *args, **kwargs) +# TODO: Do we need to strip leading/trailing newlines? +standardize_1d_args = """ +*args : (x,) or (x,y) + Positional args are standardized as follows. + + * If a 2D array is passed, the corresponding plot command is called for + each column of data. + * If *x* and *y* or *latitude* and *longitude* coordinates were not + provided, and a `~pandas.DataFrame` or `~xarray.DataArray`, we + try to infer them from the metadata. Otherwise, + ``np.arange(0, data.shape[0])`` is used. +""" +standardize_1d_kwargs = """ +autoformat : bool, optional + Whether to modify the x axis label, y axis label, title, and axis ticks + if ``x`` or ``y`` is a `~xarray.DataArray`, `~pandas.DataFrame`, or + `~pandas.Series`. Default is the figure-wide autoformat setting. +""" +docstring.interpd.update(standardize_1d_args=standardize_1d_args) +docstring.interpd.update(standardize_1d_kwargs=standardize_1d_kwargs) + + def _interp_poles(y, Z): """Adds data points on the poles as the average of highest latitude data.""" @@ -389,35 +385,6 @@ def _enforce_bounds(x, y, xmin, xmax): def standardize_2d(self, func, *args, order='C', globe=False, **kwargs): - """ - Wraps %(methods)s, standardizes acceptable positional args and optionally - modifies the x axis label, y axis label, title, and axis ticks if the - a `~xarray.DataArray`, `~pandas.DataFrame`, or `~pandas.Series` is passed. - - Positional args are standardized as follows. - - * If *x* and *y* or *latitude* and *longitude* coordinates were not - provided, and a `~pandas.DataFrame` or `~xarray.DataArray` is passed, we - try to infer them from the metadata. Otherwise, - ``np.arange(0, data.shape[0])`` and ``np.arange(0, data.shape[1])`` - are used. - * For ``pcolor`` and ``pcolormesh``, coordinate *edges* are calculated - if *centers* were provided. For all other methods, coordinate *centers* - are calculated if *edges* were provided. - - For `~proplot.axes.GeoAxes` and `~proplot.axes.BasemapAxes`, the - `globe` keyword arg is added, suitable for plotting datasets with global - coverage. Passing ``globe=True`` does the following. - - 1. "Interpolates" input data to the North and South poles. - 2. Makes meridional coverage "circular", i.e. the last longitude coordinate - equals the first longitude coordinate plus 360\N{DEGREE SIGN}. - - For `~proplot.axes.BasemapAxes`, 1D longitude vectors are also cycled to - fit within the map edges. For example, if the projection central longitude - is 90\N{DEGREE SIGN}, the data is shifted so that it spans - -90\N{DEGREE SIGN} to 270\N{DEGREE SIGN}. - """ # Sanitize input name = func.__name__ _load_objects() @@ -666,6 +633,47 @@ def standardize_2d(self, func, *args, order='C', globe=False, **kwargs): return func(self, x, y, *Zs, colorbar_kw=colorbar_kw, **kwargs) +standardize_2d_args = """ +*args : (Z1,...) or (x,y,Z1,...) + Positional args are standardized as follows. + + * If *x* and *y* or coordinates were not + provided, and a `~pandas.DataFrame` or `~xarray.DataArray` is passed, we + try to infer them from the metadata. Otherwise, + ``np.arange(0, data.shape[0])`` and ``np.arange(0, data.shape[1])`` + are used. + * For ``pcolor`` and ``pcolormesh``, coordinate *edges* are calculated + if *centers* were provided. For all other methods, coordinate *centers* + are calculated if *edges* were provided. +""" +standardize_2d_kwargs = """ +globe : bool, optional + Valid for `~proplot.axes.CartopyAxes` and `~proplot.axes.BasemapAxes`. + When ``globe=True``, "global data coverage" is enforced as follows. + + 1. Input data is interpolated to the North and South poles. That is, 1D + vectors containing the mean values from the highest and lowest latitude + band in the dataset (e.g. -89\N{DEGREE SIGN} and 89\N{DEGREE SIGN}) are + appended to the dataset and given the coordinates -90\N{DEGREE SIGN} + and 90\N{DEGREE SIGN}. This prevents "holes" over your poles. + 2. Meridional coverage is made "circular". That is, a 1D vector is appended + to the end of the dataset with longitude coordinate equal to the initial + longitude coordinate plus 360\N{DEGREE SIGN}. This prevents empty + sectors on the edge of your projection. + + For `~proplot.axes.BasemapAxes`, 1D longitude vectors are also cycled to + fit within the map edges. For example, if the projection central longitude + is 90\N{DEGREE SIGN}, the data is shifted so that it spans + -90\N{DEGREE SIGN} to 270\N{DEGREE SIGN}. +autoformat : bool, optional + Whether to modify the x axis label, y axis label, title, and axis ticks + if ``x`` or ``y`` is a `~xarray.DataArray`, `~pandas.DataFrame`, or + `~pandas.Series`. Default is the figure-wide autoformat setting. +""" +docstring.interpd.update(standardize_2d_args=standardize_2d_args) +docstring.interpd.update(standardize_2d_kwargs=standardize_2d_kwargs) + + def _errorbar_values(data, idata, bardata=None, barrange=None, barstd=False): """Returns values that can be passed to the `~matplotlib.axes.Axes.errorbar` `xerr` and `yerr` keyword args.""" @@ -701,66 +709,6 @@ def add_errorbars( boxzorder=3, barzorder=3, **kwargs ): - """ - Wraps %(methods)s, adds support for drawing error bars. Includes - options for interpreting columns of data as ranges, representing the mean - or median of each column with lines, points, or bars, and drawing error - bars representing percentile ranges or standard deviation multiples for - the data in each column. - - Parameters - ---------- - *args - The input data. - bars : bool, optional - Toggles *thin* error bars with optional "whiskers" (i.e. caps). Default - is ``True`` when `means` is ``True``, `medians` is ``True``, or - `bardata` is not ``None``. - boxes : bool, optional - Toggles *thick* boxplot-like error bars with a marker inside - representing the mean or median. Default is ``True`` when `means` is - ``True``, `medians` is ``True``, or `boxdata` is not ``None``. - means : bool, optional - Whether to plot the means of each column in the input data. - medians : bool, optional - Whether to plot the medians of each column in the input data. - bardata, boxdata : 2xN ndarray, optional - Arrays that manually specify the thin and thick error bar coordinates. - The first row contains lower bounds, and the second row contains - upper bounds. Columns correspond to points in the dataset. - barstd, boxstd : bool, optional - Whether `barrange` and `boxrange` refer to multiples of the standard - deviation, or percentile ranges. Default is ``False``. - barrange : (float, float), optional - Percentile ranges or standard deviation multiples for drawing thin - error bars. The defaults are ``(-3,3)`` (i.e. +/-3 standard deviations) - when `barstd` is ``True``, and ``(0,100)`` (i.e. the full data range) - when `barstd` is ``False``. - boxrange : (float, float), optional - Percentile ranges or standard deviation multiples for drawing thick - error bars. The defaults are ``(-1,1)`` (i.e. +/-1 standard deviation) - when `boxstd` is ``True``, and ``(25,75)`` (i.e. the middle 50th - percentile) when `boxstd` is ``False``. - barcolor, boxcolor : color-spec, optional - Colors for the thick and thin error bars. Default is ``'k'``. - barlw, boxlw : float, optional - Line widths for the thin and thick error bars, in points. Default - `barlw` is ``0.7`` and default `boxlw` is ``4*barlw``. - boxmarker : bool, optional - Whether to draw a small marker in the middle of the box denoting - the mean or median position. Ignored if `boxes` is ``False``. - Default is ``True``. - boxmarkercolor : color-spec, optional - Color for the `boxmarker` marker. Default is ``'w'``. - capsize : float, optional - The cap size for thin error bars, in points. - barzorder, boxzorder : float, optional - The "zorder" for the thin and thick error bars. - lw, linewidth : float, optional - If passed, this is used for the default `barlw`. - edgecolor : float, optional - If passed, this is used for the default `barcolor` and `boxcolor`. - """ name = func.__name__ x, y, *args = args # Sensible defaults @@ -842,645 +790,90 @@ def add_errorbars( return obj -def plot_wrapper(self, func, *args, cmap=None, values=None, **kwargs): - """ - Wraps %(methods)s, draws a "colormap line" if the `cmap` argument was - passed. "Colormap lines" change color as a function of the parametric - coordinate `values` using the input colormap `cmap`. - - Parameters - ---------- - *args : (y,), (x,y), or (x,y,fmt) - Passed to `~matplotlib.axes.Axes.plot`. - cmap, values : optional - Passed to `~proplot.axes.Axes.parametric`. - **kwargs - `~matplotlib.lines.Line2D` properties. - """ - if len(args) > 3: # e.g. with fmt string - raise ValueError(f'Expected 1-3 positional args, got {len(args)}.') - if cmap is None: - lines = func(self, *args, values=values, **kwargs) - else: - lines = self.parametric(*args, cmap=cmap, values=values, **kwargs) - return lines - - -def scatter_wrapper( - self, func, *args, - s=None, size=None, markersize=None, - c=None, color=None, markercolor=None, - smin=None, smax=None, - cmap=None, cmap_kw=None, vmin=None, vmax=None, norm=None, norm_kw=None, - lw=None, linewidth=None, linewidths=None, - markeredgewidth=None, markeredgewidths=None, - edgecolor=None, edgecolors=None, - markeredgecolor=None, markeredgecolors=None, - **kwargs -): - """ - Wraps `~matplotlib.axes.Axes.scatter`, adds optional keyword args - more consistent with the `~matplotlib.axes.Axes.plot` keywords. - - Parameters - ---------- - s, size, markersize : float or list of float, optional - Aliases for the marker size. - smin, smax : float, optional - Used to scale the `s` array. These are the minimum and maximum marker - sizes. Defaults are the minimum and maximum of the `s` array. - c, color, markercolor : color-spec or list thereof, or array, optional - Aliases for the marker fill color. If just an array of values, the - colors will be generated by passing the values through the `norm` - normalizer and drawing from the `cmap` colormap. - cmap : colormap-spec, optional - The colormap specifer, passed to the `~proplot.styletools.Colormap` - constructor. - cmap_kw : dict-like, optional - Passed to `~proplot.styletools.Colormap`. - vmin, vmax : float, optional - Used to generate a `norm` for scaling the `c` array. These are the - values corresponding to the leftmost and rightmost colors in the - colormap. Defaults are the minimum and maximum values of the `c` array. - norm : normalizer spec, optional - The colormap normalizer, passed to the `~proplot.styletools.Norm` - constructor. - norm_kw : dict, optional - Passed to `~proplot.styletools.Norm`. - lw, linewidth, linewidths, markeredgewidth, markeredgewidths : \ -float or list thereof, optional - Aliases for the marker edge width. - edgecolors, markeredgecolor, markeredgecolors : \ -color-spec or list thereof, optional - Aliases for the marker edge color. - **kwargs - Passed to `~matplotlib.axes.Axes.scatter`. - """ - # Manage input arguments - # NOTE: Parse 1D must come before this - nargs = len(args) - if len(args) > 4: - raise ValueError(f'Expected 1-4 positional args, got {nargs}.') - args = list(args) - if len(args) == 4: - c = args.pop(1) - if len(args) == 3: - s = args.pop(0) - - # Format cmap and norm - cmap_kw = cmap_kw or {} - norm_kw = norm_kw or {} - if cmap is not None: - cmap = styletools.Colormap(cmap, **cmap_kw) - if norm is not None: - norm = styletools.Norm(norm, **norm_kw) - - # Apply some aliases for keyword arguments - c = _notNone(c, color, markercolor, None, - names=('c', 'color', 'markercolor')) - s = _notNone(s, size, markersize, None, - names=('s', 'size', 'markersize')) - lw = _notNone( - lw, linewidth, linewidths, markeredgewidth, markeredgewidths, None, - names=( - 'lw', 'linewidth', 'linewidths', - 'markeredgewidth', 'markeredgewidths' - )) - ec = _notNone( - edgecolor, edgecolors, markeredgecolor, markeredgecolors, None, - names=( - 'edgecolor', 'edgecolors', 'markeredgecolor', 'markeredgecolors' - )) - - # Scale s array - if np.iterable(s): - smin_true, smax_true = min(s), max(s) - if smin is None: - smin = smin_true - if smax is None: - smax = smax_true - s = smin + (smax - smin) * (np.array(s) - smin_true) / \ - (smax_true - smin_true) - return func(self, *args, c=c, s=s, - cmap=cmap, vmin=vmin, vmax=vmax, - norm=norm, linewidths=lw, edgecolors=ec, - **kwargs) - - -def _fill_between_apply( - self, func, *args, - negcolor='blue', poscolor='red', negpos=False, - **kwargs -): - """Parse args and call function.""" - # Allow common keyword usage - x = 'y' if 'x' in func.__name__ else 'y' - y = 'x' if x == 'y' else 'y' - if x in kwargs: - args = (kwargs.pop(x), *args) - for y in (y + '1', y + '2'): - if y in kwargs: - args = (*args, kwargs.pop(y)) - if len(args) == 1: - args = (np.arange(len(args[0])), *args) - if len(args) == 2: - if kwargs.get('stacked', False): - args = (*args, 0) - else: - args = (args[0], 0, args[1]) # default behavior - if len(args) != 3: - raise ValueError(f'Expected 2-3 positional args, got {len(args)}.') - if not negpos: - obj = func(self, *args, **kwargs) - return obj - - # Get zero points - objs = [] - kwargs.setdefault('interpolate', True) - y1, y2 = np.atleast_1d( - args[-2]).squeeze(), np.atleast_1d(args[-1]).squeeze() - if y1.ndim > 1 or y2.ndim > 1: - raise ValueError(f'When "negpos" is True, y must be 1-dimensional.') - if kwargs.get('where', None) is not None: - raise ValueError( - 'When "negpos" is True, you cannot set the "where" keyword.' - ) - for i in range(2): - kw = {**kwargs} - kw.setdefault('color', negcolor if i == 0 else poscolor) - where = (y2 < y1) if i == 0 else (y2 >= y1) - obj = func(self, *args, where=where, **kw) - objs.append(obj) - return (*objs,) - - -def fill_between_wrapper(self, func, *args, **kwargs): - """ - Wraps `~matplotlib.axes.Axes.fill_between`, also accessible via the - `~proplot.axes.Axes.area` alias. - - Parameters - ---------- - *args : (y1,), (x,y1), or (x,y1,y2) - The *x* and *y* coordinates. If `x` is not provided, it will be - inferred from `y1`. If `y1` and `y2` are provided, their shapes - must be identical, and we fill between respective columns of these - arrays. - stacked : bool, optional - If `y2` is ``None``, this indicates whether to "stack" successive - columns of the `y1` array. - negpos : bool, optional - Whether to shade where `y2` is greater than `y1` with the color - `poscolor`, and where `y1` is greater than `y2` with the color - `negcolor`. For example, to shade positive values red and negtive blue, - use ``ax.fill_between(x, 0, y)``. - negcolor, poscolor : color-spec, optional - Colors to use for the negative and positive values. Ignored if `negpos` - is ``False``. - where : ndarray, optional - Boolean ndarray mask for points you want to shade. See `this example \ -`__. - **kwargs - Passed to `~matplotlib.axes.Axes.fill_between`. - """ # noqa - return _fill_between_apply(self, func, *args, **kwargs) - - -def fill_betweenx_wrapper(self, func, *args, **kwargs): - """Wraps %(methods)s, also accessible via the `~proplot.axes.Axes.areax` - alias. Usage is same as `fill_between_wrapper`.""" - return _fill_between_apply(self, func, *args, **kwargs) - - -def hist_wrapper(self, func, x, bins=None, **kwargs): - """Wraps %(methods)s, enforces that all arguments after `bins` are - keyword-only and sets the default patch linewidth to ``0``.""" - kwargs.setdefault('linewidth', 0) - return func(self, x, bins=bins, **kwargs) - - -def barh_wrapper( - self, func, y=None, width=None, height=0.8, left=None, **kwargs -): - """Wraps %(methods)s, usage is same as `bar_wrapper`.""" - kwargs.setdefault('orientation', 'horizontal') - if y is None and width is None: - raise ValueError( - f'barh() requires at least 1 positional argument, got 0.' - ) - return self.bar(x=left, height=height, width=width, bottom=y, **kwargs) - - -def bar_wrapper( - self, func, x=None, height=None, width=0.8, bottom=None, *, left=None, - vert=None, orientation='vertical', stacked=False, - lw=None, linewidth=0.7, edgecolor='k', - **kwargs -): - """ - Wraps %(methods)s, permits bar stacking and bar grouping. - - Parameters - ---------- - x, height, width, bottom : float or list of float, optional - The dimensions of the bars. If the *x* coordinates are not provided, - they are set to ``np.arange(0, len(height))``. - orientation : {'vertical', 'horizontal'}, optional - The orientation of the bars. - vert : bool, optional - Alternative to the `orientation` keyword arg. If ``False``, horizontal - bars are drawn. This is for consistency with - `~matplotlib.axes.Axes.boxplot` and `~matplotlib.axes.Axes.violinplot`. - stacked : bool, optional - Whether to stack columns of input data, or plot the bars side-by-side. - edgecolor : color-spec, optional - The edge color for the bar patches. - lw, linewidth : float, optional - The edge width for the bar patches. - """ - # Barh converts y-->bottom, left-->x, width-->height, height-->width. - # Convert back to (x, bottom, width, height) so we can pass stuff through - # cycle_changer. - # NOTE: You *must* do juggling of barh keyword order --> bar keyword order - # --> barh keyword order, because horizontal hist passes arguments to bar - # directly and will not use a 'barh' method with overridden argument order! - if vert is not None: - orientation = ('vertical' if vert else 'horizontal') - if orientation == 'horizontal': - x, bottom = bottom, x - width, height = height, width - - # Parse args - # TODO: Stacked feature is implemented in `cycle_changer`, but makes more - # sense do document here; figure out way to move it here? - if left is not None: - _warn_proplot( - f'The "left" keyword with bar() is deprecated. Use "x" instead.' - ) - x = left - if x is None and height is None: - raise ValueError( - f'bar() requires at least 1 positional argument, got 0.' - ) - elif height is None: - x, height = None, x - - # Call func - # TODO: This *must* also be wrapped by cycle_changer, which ultimately - # permutes back the x/bottom args for horizontal bars! Need to clean up. - lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) - return func(self, x, height, width=width, bottom=bottom, - linewidth=lw, edgecolor=edgecolor, - stacked=stacked, orientation=orientation, - **kwargs) - - -def boxplot_wrapper( - self, func, *args, - color='k', fill=True, fillcolor=None, fillalpha=0.7, - lw=None, linewidth=0.7, orientation=None, - marker=None, markersize=None, - boxcolor=None, boxlw=None, - capcolor=None, caplw=None, - meancolor=None, meanlw=None, - mediancolor=None, medianlw=None, - whiskercolor=None, whiskerlw=None, - fliercolor=None, flierlw=None, - **kwargs -): - """ - Wraps %(methods)s, adds convenient keyword args. - Fills the objects with a cycle color by default. - - Parameters - ---------- - *args : 1D or 2D ndarray - The data array. - color : color-spec, optional - The color of all objects. - fill : bool, optional - Whether to fill the box with a color. - fillcolor : color-spec, optional - The fill color for the boxes. Default is the next color cycler color. - fillalpha : float, optional - The opacity of the boxes. Default is ``1``. - lw, linewidth : float, optional - The linewidth of all objects. - orientation : {None, 'horizontal', 'vertical'}, optional - Alternative to the native `vert` keyword arg. Controls orientation. - marker : marker-spec, optional - Marker style for the 'fliers', i.e. outliers. - markersize : float, optional - Marker size for the 'fliers', i.e. outliers. - boxcolor, capcolor, meancolor, mediancolor, whiskercolor : \ -color-spec, optional - The color of various boxplot components. These are shorthands so you - don't have to pass e.g. a ``boxprops`` dictionary. - boxlw, caplw, meanlw, medianlw, whiskerlw : float, optional - The line width of various boxplot components. These are shorthands so - you don't have to pass e.g. a ``boxprops`` dictionary. - """ - # Call function - if len(args) > 2: - raise ValueError(f'Expected 1-2 positional args, got {len(args)}.') - if orientation is not None: - if orientation == 'horizontal': - kwargs['vert'] = False - elif orientation != 'vertical': - raise ValueError( - 'Orientation must be "horizontal" or "vertical", ' - f'got {orientation!r}.' - ) - obj = func(self, *args, **kwargs) - if not args: - return obj - - # Modify results - # TODO: Pass props keyword args instead? Maybe does not matter. - lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) - if fillcolor is None: - cycler = next(self._get_lines.prop_cycler) - fillcolor = cycler.get('color', None) - for key, icolor, ilw in ( - ('boxes', boxcolor, boxlw), - ('caps', capcolor, caplw), - ('whiskers', whiskercolor, whiskerlw), - ('means', meancolor, meanlw), - ('medians', mediancolor, medianlw), - ('fliers', fliercolor, flierlw), - ): - if key not in obj: # possible if not rendered - continue - artists = obj[key] - ilw = _notNone(ilw, lw) - icolor = _notNone(icolor, color) - for artist in artists: - if icolor is not None: - artist.set_color(icolor) - artist.set_markeredgecolor(icolor) - if ilw is not None: - artist.set_linewidth(ilw) - artist.set_markeredgewidth(ilw) - if key == 'boxes' and fill: - patch = mpatches.PathPatch( - artist.get_path(), color=fillcolor, - alpha=fillalpha, linewidth=0) - self.add_artist(patch) - if key == 'fliers': - if marker is not None: - artist.set_marker(marker) - if markersize is not None: - artist.set_markersize(markersize) - return obj - - -def violinplot_wrapper( - self, func, *args, - lw=None, linewidth=0.7, fillcolor=None, edgecolor='k', - fillalpha=0.7, orientation=None, - **kwargs -): - """ - Wraps %(methods)s, adds convenient keyword args. - Makes the style shown in right plot of `this matplotlib example \ -`__ - the default. It is also no longer possible to show minima and maxima with - whiskers, because this is redundant. - - Parameters - ---------- - *args : 1D or 2D ndarray - The data array. - lw, linewidth : float, optional - The linewidth of the line objects. Default is ``1``. - edgecolor : color-spec, optional - The edge color for the violin patches. Default is ``'k'``. - fillcolor : color-spec, optional - The violin plot fill color. Default is the next color cycler color. - fillalpha : float, optional - The opacity of the violins. Default is ``1``. - orientation : {None, 'horizontal', 'vertical'}, optional - Alternative to the native `vert` keyword arg. Controls orientation. - boxrange, barrange : (float, float), optional - Percentile ranges for the thick and thin central bars. The defaults - are ``(25, 75)`` and ``(5, 95)``, respectively. - """ - # Orientation and checks - if len(args) > 2: - raise ValueError(f'Expected 1-2 positional args, got {len(args)}.') - if orientation is not None: - if orientation == 'horizontal': - kwargs['vert'] = False - elif orientation != 'vertical': - raise ValueError( - 'Orientation must be "horizontal" or "vertical", ' - f'got {orientation!r}.' - ) - - # Sanitize input - lw = _notNone(lw, linewidth, None, names=('lw', 'linewidth')) - if kwargs.pop('showextrema', None): - _warn_proplot(f'Ignoring showextrema=True.') - if 'showmeans' in kwargs: - kwargs.setdefault('means', kwargs.pop('showmeans')) - if 'showmedians' in kwargs: - kwargs.setdefault('medians', kwargs.pop('showmedians')) - kwargs.setdefault('capsize', 0) - obj = func(self, *args, - showmeans=False, showmedians=False, showextrema=False, - edgecolor=edgecolor, lw=lw, **kwargs) - if not args: - return obj - - # Modify body settings - for artist in obj['bodies']: - artist.set_alpha(fillalpha) - artist.set_edgecolor(edgecolor) - artist.set_linewidths(lw) - if fillcolor is not None: - artist.set_facecolor(fillcolor) - return obj - - -def _get_transform(self, transform): - """Translates user input transform. Also used in an axes method.""" - try: - from cartopy.crs import CRS - except ModuleNotFoundError: - CRS = None - cartopy = (getattr(self, 'name', '') == 'geo') - if (isinstance(transform, mtransforms.Transform) - or CRS and isinstance(transform, CRS)): - return transform - elif transform == 'figure': - return self.figure.transFigure - elif transform == 'axes': - return self.transAxes - elif transform == 'data': - return PlateCarree() if cartopy else self.transData - elif cartopy and transform == 'map': - return self.transData - else: - raise ValueError(f'Unknown transform {transform!r}.') - - -def text_wrapper( - self, func, - x=0, y=0, text='', transform='data', - fontfamily=None, fontname=None, fontsize=None, size=None, - border=False, bordercolor='w', invert=False, lw=None, linewidth=2, - **kwargs -): - """ - Wraps %(methods)s, and enables specifying `tranform` with a string name and - adds feature for drawing borders around text. - - Parameters - ---------- - x, y : float - The *x* and *y* coordinates for the text. - text : str - The text string. - transform : {'data', 'axes', 'figure'} or \ -`~matplotlib.transforms.Transform`, optional - The transform used to interpret `x` and `y`. Can be a - `~matplotlib.transforms.Transform` object or a string representing the - `~matplotlib.axes.Axes.transData`, `~matplotlib.axes.Axes.transAxes`, - or `~matplotlib.figure.Figure.transFigure` transforms. Default is - ``'data'``, i.e. the text is positioned in data coordinates. - size, fontsize : float or str, optional - The font size. If float, units are inches. If string, units are - interpreted by `~proplot.utils.units`. - fontname, fontfamily : str, optional - Aliases for the ``fontfamily`` `~matplotlib.text.Text` property. - border : bool, optional - Whether to draw border around text. - bordercolor : color-spec, optional - The color of the border. Default is ``'w'``. - invert : bool, optional - If ``False``, ``'color'`` is used for the text and ``bordercolor`` - for the border. If ``True``, this is inverted. - lw, linewidth : float, optional - Ignored if `border` is ``False``. The width of the text border. - - Other parameters - ---------------- - **kwargs - Passed to `~matplotlib.text.Text` instantiator. - """ - # Default transform by string name - if not transform: - transform = self.transData - else: - transform = _get_transform(self, transform) - - # More flexible keyword args and more helpful warning if invalid font - # is specified - fontname = _notNone(fontfamily, fontname, None, - names=('fontfamily', 'fontname')) - if fontname is not None: - if not isinstance(fontname, str) and np.iterable( - fontname) and len(fontname) == 1: - fontname = fontname[0] - if fontname.lower() in list(map(str.lower, styletools.fonts)): - kwargs['fontfamily'] = fontname - else: - _warn_proplot( - f'Font {fontname!r} unavailable. Available fonts are ' - + ', '.join(map(repr, styletools.fonts)) + '.' - ) - size = _notNone(fontsize, size, None, names=('fontsize', 'size')) - if size is not None: - kwargs['fontsize'] = units(size, 'pt') - # text.color is ignored sometimes unless we apply this - kwargs.setdefault('color', rc['text.color']) - obj = func(self, x, y, text, transform=transform, **kwargs) - - # Optionally draw border around text - if border: - linewidth = lw or linewidth - facecolor, bgcolor = kwargs['color'], bordercolor - if invert: - facecolor, bgcolor = bgcolor, facecolor - kwargs = {'linewidth': linewidth, - 'foreground': bgcolor, 'joinstyle': 'miter'} - obj.update({ - 'color': facecolor, - 'zorder': 100, - 'path_effects': - [mpatheffects.Stroke(**kwargs), mpatheffects.Normal()] - }) - return obj - - -def cycle_changer( - self, func, *args, - cycle=None, cycle_kw=None, - markers=None, linestyles=None, - label=None, labels=None, values=None, - legend=None, legend_kw=None, - colorbar=None, colorbar_kw=None, - **kwargs -): - """ - Wraps methods that use the property cycler (%(methods)s), - adds features for controlling colors in the property cycler and drawing - legends or colorbars in one go. - - This wrapper also *standardizes acceptable input* -- these methods now all - accept 2D arrays holding columns of data, and *x*-coordinates are always - optional. Note this alters the behavior of `~matplotlib.axes.Axes.boxplot` - and `~matplotlib.axes.Axes.violinplot`, which now compile statistics on - *columns* of data instead of *rows*. +# TODO: mix standardize_1d args with errorbars args +add_errorbars_args = """ +*args : (x,) or (x,y) + Positional args are standardized as follows. - Parameters - ---------- - cycle : cycle-spec, optional - The cycle specifer, passed to the `~proplot.styletools.Cycle` - constructor. If the returned list of colors is unchanged from the - current axes color cycler, the axes cycle will **not** be reset to the - first position. - cycle_kw : dict-like, optional - Passed to `~proplot.styletools.Cycle`. - label : float or str, optional - The legend label to be used for this plotted element. - labels, values : list of float or list of str, optional - Used with 2D input arrays. The legend labels or colorbar coordinates - for each column in the array. Can be numeric or string, and must match - the number of columns in the 2D array. - legend : bool, int, or str, optional - If not ``None``, this is a location specifying where to draw an *inset* - or *panel* legend from the resulting handle(s). If ``True``, the - default location is used. Valid locations are described in - `~proplot.axes.Axes.legend`. - legend_kw : dict-like, optional - Ignored if `legend` is ``None``. Extra keyword args for our call - to `~proplot.axes.Axes.legend`. - colorbar : bool, int, or str, optional - If not ``None``, this is a location specifying where to draw an *inset* - or *panel* colorbar from the resulting handle(s). If ``True``, the - default location is used. Valid locations are described in - `~proplot.axes.Axes.colorbar`. - colorbar_kw : dict-like, optional - Ignored if `colorbar` is ``None``. Extra keyword args for our call - to `~proplot.axes.Axes.colorbar`. + * If a 2D array is passed and `means` and `medians` are both ``False``, + the corresponding plot command is called for each column of data . + * If *x* and *y* coordinates were not + provided, and a `~pandas.DataFrame` or `~xarray.DataArray`, we + try to infer them from the metadata. Otherwise, + ``np.arange(0, data.shape[0])`` is used. - Other parameters - ---------------- - *args, **kwargs - Passed to the matplotlib plotting method. - - See also - -------- - `~proplot.styletools.Cycle`, `~proplot.styletools.colors` - - Notes - ----- - See the `matplotlib source \ -`_. - The `set_prop_cycle` command modifies underlying - `_get_lines` and `_get_patches_for_fill`. - """ + Wraps %(methods)s, adds support for drawing error bars. Includes + options for interpreting columns of data as ranges, representing the mean + or median of each column with lines, points, or bars, and drawing error + bars representing percentile ranges or standard deviation multiples for + the data in each column. +""" +add_errorbars_kwargs = """ +bars : bool, optional + Toggles *thin* error bars with optional "whiskers" (i.e. caps). Default + is ``True`` when `means` is ``True``, `medians` is ``True``, or + `bardata` is not ``None``. +boxes : bool, optional + Toggles *thick* boxplot-like error bars with a marker inside + representing the mean or median. Default is ``True`` when `means` is + ``True``, `medians` is ``True``, or `boxdata` is not ``None``. +means : bool, optional + Whether to plot the means of each column in the input data. +medians : bool, optional + Whether to plot the medians of each column in the input data. +bardata, boxdata : 2xN ndarray, optional + Arrays that manually specify the thin and thick error bar coordinates. + The first row contains lower bounds, and the second row contains + upper bounds. Columns correspond to points in the dataset. +barstd, boxstd : bool, optional + Whether `barrange` and `boxrange` refer to multiples of the standard + deviation, or percentile ranges. Default is ``False``. +barrange : (float, float), optional + Percentile ranges or standard deviation multiples for drawing thin + error bars. The defaults are ``(-3,3)`` (i.e. +/-3 standard deviations) + when `barstd` is ``True``, and ``(0,100)`` (i.e. the full data range) + when `barstd` is ``False``. +boxrange : (float, float), optional + Percentile ranges or standard deviation multiples for drawing thick + error bars. The defaults are ``(-1,1)`` (i.e. +/-1 standard deviation) + when `boxstd` is ``True``, and ``(25,75)`` (i.e. the middle 50th + percentile) when `boxstd` is ``False``. +barcolor, boxcolor : color-spec, optional + Colors for the thick and thin error bars. Default is ``'k'``. +barlw, boxlw : float, optional + Line widths for the thin and thick error bars, in points. Default + `barlw` is ``0.7`` and default `boxlw` is ``4*barlw``. +boxmarker : bool, optional + Whether to draw a small marker in the middle of the box denoting + the mean or median position. Ignored if `boxes` is ``False``. + Default is ``True``. +boxmarkercolor : color-spec, optional + Color for the `boxmarker` marker. Default is ``'w'``. +capsize : float, optional + The cap size for thin error bars, in points. +barzorder, boxzorder : float, optional + The "zorder" for the thin and thick error bars. +lw, linewidth : float, optional + If passed, this is used for the default `barlw`. +edgecolor : float, optional + If passed, this is used for the default `barcolor` and `boxcolor`. +""" +docstring.interpd.update(add_errorbars_args=add_errorbars_args) +docstring.interpd.update(add_errorbars_kwargs=add_errorbars_kwargs) + + +def cycle_changer(self, func, *args, + cycle=None, cycle_kw=None, + markers=None, linestyles=None, + label=None, labels=None, values=None, + legend=None, legend_kw=None, + colorbar=None, colorbar_kw=None, + panel_kw=None, + **kwargs): + # See the `matplotlib source + # `_. + # The `set_prop_cycle` command modifies underlying + # `_get_lines` and `_get_patches_for_fill`. # No mutable defaults cycle_kw = cycle_kw or {} legend_kw = legend_kw or {} @@ -1695,145 +1088,73 @@ def cycle_changer( return objs[0] if is1d else (*objs,) # sensible default behavior +cycle_changer_kwargs = """ +cycle : cycle-spec, optional + The cycle specifer, passed to the `~proplot.styletools.Cycle` + constructor. If the returned list of colors is unchanged from the + current axes color cycler, the axes cycle will **not** be reset to the + first position. +cycle_kw : dict-like, optional + Passed to `~proplot.styletools.Cycle`. +label : float or str, optional + The legend label to be used for this plotted element. +labels, values : list of float or list of str, optional + Used with 2D input arrays. The legend labels or colorbar coordinates + for each column in the array. Can be numeric or string, and must match + the number of columns in the 2D array. +legend : bool, int, or str, optional + If not ``None``, this is a location specifying where to draw an *inset* + or *panel* legend from the resulting handle(s). If ``True``, the + default location is used. Valid locations are described in + `~proplot.axes.Axes.legend`. +legend_kw : dict-like, optional + Ignored if `legend` is ``None``. Extra keyword args for our call + to `~proplot.axes.Axes.legend`. +colorbar : bool, int, or str, optional + If not ``None``, this is a location specifying where to draw an *inset* + or *panel* colorbar from the resulting handle(s). If ``True``, the + default location is used. Valid locations are described in + `~proplot.axes.Axes.colorbar`. +colorbar_kw : dict-like, optional + Ignored if `colorbar` is ``None``. Extra keyword args for our call + to `~proplot.axes.Axes.colorbar`. +panel_kw : dict-like, optional + Dictionary of keyword arguments passed to + `~proplot.axes.Axes.panel`, if you are generating an + on-the-fly panel. +""" +docstring.interpd.update(cycle_changer_kwargs=cycle_changer_kwargs) + + def cmap_changer( self, func, *args, cmap=None, cmap_kw=None, extend='neither', norm=None, norm_kw=None, N=None, levels=None, values=None, centers=None, vmin=None, vmax=None, locator=None, symmetric=False, locator_kw=None, edgefix=None, labels=False, labels_kw=None, fmt=None, precision=2, - colorbar=False, colorbar_kw=None, + colorbar=False, colorbar_kw=None, panel_kw=None, lw=None, linewidth=None, linewidths=None, ls=None, linestyle=None, linestyles=None, color=None, colors=None, edgecolor=None, edgecolors=None, **kwargs ): - """ - Wraps methods that take a `cmap` argument (%(methods)s), - adds several new keyword args and features. - Uses the `~proplot.styletools.BinNorm` normalizer to bin data into - discrete color levels (see notes). - - Parameters - ---------- - cmap : colormap spec, optional - The colormap specifer, passed to the `~proplot.styletools.Colormap` - constructor. - cmap_kw : dict-like, optional - Passed to `~proplot.styletools.Colormap`. - norm : normalizer spec, optional - The colormap normalizer, used to warp data before passing it - to `~proplot.styletools.BinNorm`. This is passed to the - `~proplot.styletools.Norm` constructor. - norm_kw : dict-like, optional - Passed to `~proplot.styletools.Norm`. - extend : {'neither', 'min', 'max', 'both'}, optional - Where to assign unique colors to out-of-bounds data and draw - "extensions" (triangles, by default) on the colorbar. - levels, N : int or list of float, optional - The number of level edges, or a list of level edges. If the former, - `locator` is used to generate this many levels at "nice" intervals. - Default is :rc:`image.levels`. - - Since this function also wraps `~matplotlib.axes.Axes.pcolor` and - `~matplotlib.axes.Axes.pcolormesh`, this means they now - accept the `levels` keyword arg. You can now discretize your - colors in a ``pcolor`` plot just like with ``contourf``. - values, centers : int or list of float, optional - The number of level centers, or a list of level centers. If provided, - levels are inferred using `~proplot.utils.edges`. This will override - any `levels` input. - vmin, vmax : float, optional - Used to determine level locations if `levels` is an integer. Actual - levels may not fall exactly on `vmin` and `vmax`, but the minimum - level will be no smaller than `vmin` and the maximum level will be - no larger than `vmax`. - - If `vmin` or `vmax` is not provided, the minimum and maximum data - values are used. - locator : locator-spec, optional - The locator used to determine level locations if `levels` or `values` - is an integer and `vmin` and `vmax` were not provided. Passed to the - `~proplot.axistools.Locator` constructor. Default is - `~matplotlib.ticker.MaxNLocator` with ``levels`` or ``values+1`` - integer levels. - locator_kw : dict-like, optional - Passed to `~proplot.axistools.Locator`. - symmetric : bool, optional - Toggle this to make automatically generated levels symmetric - about zero. - edgefix : bool, optional - Whether to fix the the `white-lines-between-filled-contours \ -`__ - and `white-lines-between-pcolor-rectangles \ -`__ - issues. This slows down figure rendering by a bit. Default is - :rc:`image.edgefix`. - labels : bool, optional - For `~matplotlib.axes.Axes.contour`, whether to add contour labels - with `~matplotlib.axes.Axes.clabel`. For `~matplotlib.axes.Axes.pcolor` - or `~matplotlib.axes.Axes.pcolormesh`, whether to add labels to the - center of grid boxes. In the latter case, the text will be black - when the luminance of the underlying grid box color is >50%%, and - white otherwise (see the `~proplot.styletools` documentation). - labels_kw : dict-like, optional - Ignored if `labels` is ``False``. Extra keyword args for the labels. - For `~matplotlib.axes.Axes.contour`, passed to - `~matplotlib.axes.Axes.clabel`. For `~matplotlib.axes.Axes.pcolor` - or `~matplotlib.axes.Axes.pcolormesh`, passed to - `~matplotlib.axes.Axes.text`. - fmt : format-spec, optional - Passed to the `~proplot.styletools.Norm` constructor, used to format - number labels. You can also use the `precision` keyword arg. - precision : int, optional - Maximum number of decimal places for the number labels. - Number labels are generated with the - `~proplot.axistools.SimpleFormatter` formatter, which allows us to - limit the precision. - colorbar : bool, int, or str, optional - If not ``None``, this is a location specifying where to draw an *inset* - or *panel* colorbar from the resulting mappable. If ``True``, the - default location is used. Valid locations are described in - `~proplot.axes.Axes.colorbar`. - colorbar_kw : dict-like, optional - Ignored if `colorbar` is ``None``. Extra keyword args for our call - to `~proplot.axes.Axes.colorbar`. - - Other parameters - ---------------- - lw, linewidth, linewidths - The width of `~matplotlib.axes.Axes.contour` lines and - `~proplot.axes.Axes.parametric` lines. Also the width of lines - *between* `~matplotlib.axes.Axes.pcolor` boxes, - `~matplotlib.axes.Axes.pcolormesh` boxes, and - `~matplotlib.axes.Axes.contourf` filled contours. - ls, linestyle, linestyles - As above, but for the line style. - color, colors, edgecolor, edgecolors - As above, but for the line color. - *args, **kwargs - Passed to the matplotlib plotting method. - - Notes - ----- - The `~proplot.styletools.BinNorm` normalizer, used with all colormap - plots, makes sure that your "levels" always span the full range of colors - in the colormap, whether you are extending max, min, neither, or both. By - default, when you select `extend` not ``'both'``, matplotlib seems to just - cut off the most intense colors (reserved for coloring "out of bounds" - data), even though they are not being used. - - This could also be done by limiting the number of colors in the colormap - lookup table by selecting a smaller ``N`` (see - `~matplotlib.colors.LinearSegmentedColormap`). But I prefer the approach - of always building colormaps with hi-res lookup tables, and leaving the job - of normalizing data values to colormap locations to the - `~matplotlib.colors.Normalize` object. - - See also - -------- - `~proplot.styletools.Colormap`, `~proplot.styletools.Norm`, - `~proplot.styletools.BinNorm` - """ + # Wraps methods that take a ``cmap`` argument (%(methods)s), + # adds several new keyword args and features. + # Uses the `~proplot.styletools.BinNorm` normalizer to bin data into + # discrete color levels (see notes). + # The `~proplot.styletools.BinNorm` normalizer, used with all colormap + # plots, makes sure that your "levels" always span the full range of colors + # in the colormap, whether you are extending max, min, neither, or both. By + # default, when you select `extend` not ``'both'``, matplotlib seems to + # cut off the most intense colors (reserved for coloring "out of bounds" + # data), even though they are not being used. + + # This could also be done by limiting the number of colors in the colormap + # lookup table by selecting a smaller ``N`` (see + # `~matplotlib.colors.LinearSegmentedColormap`). But I prefer the approach + # of always building colormaps with hi-res lookup tables, and leaving the + # job of normalizing data values to colormap locations to the + # `~matplotlib.colors.Normalize` object. # No mutable defaults cmap_kw = cmap_kw or {} norm_kw = norm_kw or {} @@ -2161,6 +1482,104 @@ def cmap_changer( return obj +cmap_changer_kwargs = """ +cmap : colormap spec, optional + The colormap specifer, passed to the `~proplot.styletools.Colormap` + constructor. +cmap_kw : dict-like, optional + Passed to `~proplot.styletools.Colormap`. +norm : normalizer spec, optional + The colormap normalizer, used to warp data before passing it + to `~proplot.styletools.BinNorm`. This is passed to the + `~proplot.styletools.Norm` constructor. +norm_kw : dict-like, optional + Passed to `~proplot.styletools.Norm`. +extend : {'neither', 'min', 'max', 'both'}, optional + Where to assign unique colors to out-of-bounds data and draw + "extensions" (triangles, by default) on the colorbar. +levels, N : int or list of float, optional + The number of level edges, or a list of level edges. If the former, + `locator` is used to generate this many levels at "nice" intervals. + Default is :rc:`image.levels`. + + Since this function also wraps `~matplotlib.axes.Axes.pcolor` and + `~matplotlib.axes.Axes.pcolormesh`, this means they now + accept the `levels` keyword arg. You can now discretize your + colors in a ``pcolor`` plot just like with ``contourf``. +values, centers : int or list of float, optional + The number of level centers, or a list of level centers. If provided, + levels are inferred using `~proplot.utils.edges`. This will override + any `levels` input. +vmin, vmax : float, optional + Used to determine level locations if `levels` is an integer. Actual + levels may not fall exactly on `vmin` and `vmax`, but the minimum + level will be no smaller than `vmin` and the maximum level will be + no larger than `vmax`. + + If `vmin` or `vmax` is not provided, the minimum and maximum data + values are used. +locator : locator-spec, optional + The locator used to determine level locations if `levels` or `values` + is an integer and `vmin` and `vmax` were not provided. Passed to the + `~proplot.axistools.Locator` constructor. Default is + `~matplotlib.ticker.MaxNLocator` with ``levels`` or ``values+1`` + integer levels. +locator_kw : dict-like, optional + Passed to `~proplot.axistools.Locator`. +symmetric : bool, optional + Toggle this to make automatically generated levels symmetric about zero. +edgefix : bool, optional + Whether to fix the the `white-lines-between-filled-contours \ +`__ + and `white-lines-between-pcolor-rectangles \ +`__ + issues. This slows down figure rendering by a bit. Default is + :rc:`image.edgefix`. +labels : bool, optional + For `~matplotlib.axes.Axes.contour`, whether to add contour labels + with `~matplotlib.axes.Axes.clabel`. For `~matplotlib.axes.Axes.pcolor` + or `~matplotlib.axes.Axes.pcolormesh`, whether to add labels to the + center of grid boxes. In the latter case, the text will be black + when the luminance of the underlying grid box color is >50%%, and + white otherwise (see the `~proplot.styletools` documentation). +labels_kw : dict-like, optional + Ignored if `labels` is ``False``. Extra keyword args for the labels. + For `~matplotlib.axes.Axes.contour`, passed to + `~matplotlib.axes.Axes.clabel`. For `~matplotlib.axes.Axes.pcolor` or + `~matplotlib.axes.Axes.pcolormesh`, passed to `~matplotlib.axes.Axes.text`. +fmt : format-spec, optional + Passed to the `~proplot.styletools.Norm` constructor, used to format + number labels. You can also use the `precision` keyword arg. +precision : int, optional + Maximum number of decimal places for the number labels. + Number labels are generated with the `~proplot.axistools.SimpleFormatter` + formatter, which allows us to limit the precision. +colorbar : bool, int, or str, optional + If not ``None``, this is a location specifying where to draw an *inset* + or *panel* colorbar from the resulting mappable. If ``True``, the + default location is used. Valid locations are described in + `~proplot.axes.Axes.colorbar`. +colorbar_kw : dict-like, optional + Ignored if `colorbar` is ``None``. Extra keyword args for our call + to `~proplot.axes.Axes.colorbar`. +panel_kw : dict-like, optional + Dictionary of keyword arguments passed to + `~proplot.axes.Axes.panel`, if you are generating an + on-the-fly panel. +lw, linewidth, linewidths + The width of `~matplotlib.axes.Axes.contour` lines and + `~proplot.axes.Axes.cmapline` lines. Also the width of lines + *between* `~matplotlib.axes.Axes.pcolor` boxes, + `~matplotlib.axes.Axes.pcolormesh` boxes, and + `~matplotlib.axes.Axes.contourf` filled contours. +ls, linestyle, linestyles + As with `lw`, but for the line style. +color, colors, edgecolor, edgecolors + As with `lw`, but for the line color. +""" +docstring.interpd.update(cmap_changer_kwargs=cmap_changer_kwargs) + + def legend_wrapper( self, handles=None, labels=None, ncol=None, ncols=None, center=None, order='C', loc=None, label=None, title=None, @@ -2543,6 +1962,66 @@ def legend_wrapper( return legs[0] if len(legs) == 1 else (*legs,) +legend_kwargs = """ +handles : list of `~matplotlib.artist.Artist`, optional + List of artists instances, or list of lists of artist instances (see + the `center` keyword). If ``None``, the artists are retrieved with + `~matplotlib.axes.Axes.get_legend_handles_labels`. +labels : list of str, optional + Matching list of string labels, or list of lists of string labels (see + the `center` keywod). If ``None``, the labels are retrieved by calling + `~matplotlib.artist.Artist.get_label` on each `~matplotlib.artist.Artist` + in `handles`. +ncol, ncols : int, optional + The number of columns. `ncols` is an alias, added + for consistency with `~matplotlib.pyplot.subplots`. +order : {'C', 'F'}, optional + Whether legend handles are drawn in row-major (``'C'``) or column-major + (``'F'``) order. Analagous to `numpy.array` ordering. For some reason + ``'F'`` was the original matplotlib default. Default is ``'C'``. +center : bool, optional + Whether to center each legend row individually. If ``True``, we + actually draw successive single-row legends stacked on top of each + other. + + If ``None``, we infer this setting from `handles`. Default is ``True`` + if `handles` is a list of lists; each sublist is used as a *row* + in the legend. Otherwise, default is ``False``. +loc : int or str, optional + The legend location. The following location keys are valid. + + ================== ========================================================== + Location Valid keys + ================== ========================================================== + "best" possible ``0``, ``'best'``, ``'b'``, ``'i'``, ``'inset'`` + upper right ``1``, ``'upper right'``, ``'ur'`` + upper left ``2``, ``'upper left'``, ``'ul'`` + lower left ``3``, ``'lower left'``, ``'ll'`` + lower right ``4``, ``'lower right'``, ``'lr'`` + center left ``5``, ``'center left'``, ``'cl'`` + center right ``6``, ``'center right'``, ``'cr'`` + lower center ``7``, ``'lower center'``, ``'lc'`` + upper center ``8``, ``'upper center'``, ``'uc'`` + center ``9``, ``'center'``, ``'c'`` + ================== ========================================================== + +label, title : str, optional + The legend title. The `label` keyword is also accepted, for consistency + with `colorbar`. +fontsize, fontweight, fontcolor : optional + The font size, weight, and color for legend text. +color, lw, linewidth, marker, linestyle, dashes, markersize : property-spec, optional + Properties used to override the legend handles. For example, if you + want a legend that describes variations in line style ignoring variations + in color, you might want to use ``color='k'``. For now this does not + include `facecolor`, `edgecolor`, and `alpha`, because + `~matplotlib.axes.Axes.legend` uses these keyword args to modify the + frame properties. +""" # noqa +docstring.interpd.update(legend_args='') +docstring.interpd.update(legend_kwargs=legend_kwargs) + + def colorbar_wrapper( self, mappable, values=None, extend=None, extendsize=None, @@ -2964,6 +2443,112 @@ def colorbar_wrapper( return cb +colorbar_args = """ +mappable : mappable, list of plot handles, list of color-spec, or colormap-spec + There are four options here: + + 1. A mappable object. Basically, any object with a ``get_cmap`` method, + like the objects returned by `~matplotlib.axes.Axes.contourf` and + `~matplotlib.axes.Axes.pcolormesh`. + 2. A list of "plot handles". Basically, any object with a ``get_color`` + method, like `~matplotlib.lines.Line2D` instances. A colormap will + be generated from the colors of these objects, and colorbar levels + will be selected using `values`. If `values` is ``None``, we try + to infer them by converting the handle labels returned by + `~matplotlib.artist.Artist.get_label` to `float`. Otherwise, it is + set to ``np.linspace(0, 1, len(mappable))``. + 3. A list of hex strings, color string names, or RGB tuples. A colormap + will be generated from these colors, and colorbar levels will be + selected using `values`. If `values` is ``None``, it is set to + ``np.linspace(0, 1, len(mappable))``. + 4. A `~matplotlib.colors.Colormap` instance. In this case, a colorbar + will be drawn using this colormap and with levels determined by + `values`. If `values` is ``None``, it is set to + ``np.linspace(0, 1, cmap._N)``. +""" +colorbar_kwargs = """ +values : list of float, optional + Ignored if `mappable` is a mappable object. This maps each color or + plot handle in the `mappable` list to numeric values, from which a + colormap and normalizer are constructed. +extend : {None, 'neither', 'both', 'min', 'max'}, optional + Direction for drawing colorbar "extensions" (i.e. references to + out-of-bounds data with a unique color). These are triangles by + default. If ``None``, we try to use the ``extend`` attribute on the + mappable object. If the attribute is unavailable, we use ``'neither'``. +extendsize : float or str, optional + The length of the colorbar "extensions" in *physical units*. + If float, units are inches. If string, units are interpreted + by `~proplot.utils.units`. Default is :rc:`colorbar.insetextend` + for inset colorbars and :rc:`colorbar.extend` for outer colorbars. + + This is handy if you have multiple colorbars in one figure. + With the matplotlib API, it is really hard to get triangle + sizes to match, because the `extendsize` units are *relative*. +tickloc, ticklocation : {'bottom', 'top', 'left', 'right'}, optional + Where to draw tick marks on the colorbar. +label, title : str, optional + The colorbar label. The `title` keyword is also accepted for + consistency with `legend`. +grid : bool, optional + Whether to draw "gridlines" between each level of the colorbar. + Default is :rc:`colorbar.grid`. +tickminor : bool, optional + Whether to put minor ticks on the colorbar. Default is ``False``. +locator, ticks : locator spec, optional + Used to determine the colorbar tick mark positions. Passed to the + `~proplot.axistools.Locator` constructor. +maxn : int, optional + Used if `locator` is ``None``. Determines the maximum number of levels + that are ticked. Default depends on the colorbar length relative + to the font size. The keyword name "maxn" is meant to mimic + the `~matplotlib.ticker.MaxNLocator` class name. +maxn_minor : int, optional + As with `maxn`, but for minor tick positions. Default depends + on the colorbar length. +locator_kw : dict-like, optional + The locator settings. Passed to `~proplot.axistools.Locator`. +minorlocator, minorticks + As with `locator`, but for the minor tick marks. +minorlocator_kw + As for `locator_kw`, but for the minor locator. +formatter, ticklabels : formatter spec, optional + The tick label format. Passed to the `~proplot.axistools.Formatter` + constructor. +formatter_kw : dict-like, optional + The formatter settings. Passed to `~proplot.axistools.Formatter`. +norm : normalizer spec, optional + Ignored if `values` is ``None``. The normalizer + for converting `values` to colormap colors. Passed to the + `~proplot.styletools.Norm` constructor. As an example, if your + values are logarithmically spaced but you want the level boundaries + to appear halfway in-between the colorbar tick marks, try + ``norm='log'``. +norm_kw : dict-like, optional + The normalizer settings. Passed to `~proplot.styletools.Norm`. +edgecolor, linewidth : optional + The edge color and line width for the colorbar outline. +labelsize, labelweight, labelcolor : optional + The font size, weight, and color for colorbar label text. +ticklabelsize, ticklabelweight, ticklabelcolor : optional + The font size, weight, and color for colorbar tick labels. +fixticks : bool, optional + For complicated normalizers (e.g. `~matplotlib.colors.LogNorm`), the + colorbar minor and major ticks can appear misaligned. When `fixticks` + is ``True``, this misalignment is fixed. Default is ``False``. + + This will give incorrect positions when the colormap index does not + appear to vary "linearly" from left-to-right across the colorbar (for + example, when the leftmost colormap colors seem to be "pulled" to the + right farther than normal). In this case, you should stick with + ``fixticks=False``. +orientation : {'horizontal', 'vertical'}, optional + The colorbar orientation. You should not have to explicitly set this. +""" +docstring.interpd.update(colorbar_args=colorbar_args) +docstring.interpd.update(colorbar_kwargs=colorbar_kwargs) + + def _redirect(func): """Docorator that calls the basemap version of the function of the same name. This must be applied as innermost decorator, which means it must @@ -2996,6 +2581,7 @@ def _wrapper(self, *args, **kwargs): result = func(self, *args, **kwargs) func._has_recurred = False # cleanup, in case recursion never occurred return result + _wrapper.__doc__ = None return _wrapper @@ -3011,8 +2597,7 @@ def _wrapper_decorator(driver): cartopy_methods = ('get_extent', 'set_extent') def decorator(func): - # Define wrapper and suppress documentation - # We only document wrapper functions, not the methods they wrap + # Define wrapper @functools.wraps(func) def _wrapper(self, *args, **kwargs): return driver(self, func, *args, **kwargs) @@ -3047,20 +2632,10 @@ def _wrapper(self, *args, **kwargs): # Auto generated decorators. Each wrapper internally calls # func(self, ...) somewhere. _add_errorbars = _wrapper_decorator(add_errorbars) -_bar_wrapper = _wrapper_decorator(bar_wrapper) -_barh_wrapper = _wrapper_decorator(barh_wrapper) _default_latlon = _wrapper_decorator(default_latlon) -_boxplot_wrapper = _wrapper_decorator(boxplot_wrapper) _default_crs = _wrapper_decorator(default_crs) _default_transform = _wrapper_decorator(default_transform) _cmap_changer = _wrapper_decorator(cmap_changer) _cycle_changer = _wrapper_decorator(cycle_changer) -_fill_between_wrapper = _wrapper_decorator(fill_between_wrapper) -_fill_betweenx_wrapper = _wrapper_decorator(fill_betweenx_wrapper) -_hist_wrapper = _wrapper_decorator(hist_wrapper) -_plot_wrapper = _wrapper_decorator(plot_wrapper) -_scatter_wrapper = _wrapper_decorator(scatter_wrapper) _standardize_1d = _wrapper_decorator(standardize_1d) _standardize_2d = _wrapper_decorator(standardize_2d) -_text_wrapper = _wrapper_decorator(text_wrapper) -_violinplot_wrapper = _wrapper_decorator(violinplot_wrapper)