diff --git a/doc/api/api_changes/2017-08-27-TAC.rst b/doc/api/api_changes/2017-08-27-TAC.rst new file mode 100644 index 000000000000..82f709169eb8 --- /dev/null +++ b/doc/api/api_changes/2017-08-27-TAC.rst @@ -0,0 +1,22 @@ +Change to signatures of :meth:`~matplotlib.axes.Axes.bar` & :meth:`~matplotlib.axes.Axes.barh` +---------------------------------------------------------------------------------------------- + +For 2.0 the :ref:`default value of *align* ` changed to +``'center'``. However this caused the signature of +:meth:`~matplotlib.axes.Axes.bar` and +:meth:`~matplotlib.axes.Axes.barh` to be misleading as the first parameters were +still *left* and *bottom* respectively:: + + bar(left, height, *, align='center', **kwargs) + barh(bottom, width, *, align='center', **kwargs) + +despite behaving as the center in both cases. The methods now take ``*args, **kwargs`` +is input and are documented to have the primary signatures of:: + + bar(x, height, *, align='center', **kwargs) + barh(y, width, *, align='center', **kwargs) + +Passing *left* and *bottom* as keyword arguments to +:meth:`~matplotlib.axes.Axes.bar` and +:meth:`~matplotlib.axes.Axes.barh` respectively will warn. +Support will be removed in Matplotlib 3.0. diff --git a/doc/users/dflt_style_changes.rst b/doc/users/dflt_style_changes.rst index 2035b71773a5..06984253e72b 100644 --- a/doc/users/dflt_style_changes.rst +++ b/doc/users/dflt_style_changes.rst @@ -599,6 +599,8 @@ The default value of the ``linecolor`` kwarg for `~matplotlib.Axes.hexbin` has changed from ``'none'`` to ``'face'``. If 'none' is now supplied, no line edges are drawn around the hexagons. +.. _barbarh_align: + ``bar`` and ``barh`` -------------------- diff --git a/examples/pie_and_polar_charts/nested_pie.py b/examples/pie_and_polar_charts/nested_pie.py index 5355bd615611..7dd77a1f531d 100644 --- a/examples/pie_and_polar_charts/nested_pie.py +++ b/examples/pie_and_polar_charts/nested_pie.py @@ -44,17 +44,17 @@ left_middle = np.arange(0.0, 2 * np.pi, 2 * np.pi / 12) left_outer = np.arange(0.0, 2 * np.pi, 2 * np.pi / 9) -ax.bar(left=left_inner, +ax.bar(x=left_inner, width=2 * np.pi / 6, bottom=0, color='C0', linewidth=2, edgecolor='w', height=np.zeros_like(left_inner) + 5) -ax.bar(left=left_middle, +ax.bar(x=left_middle, width=2 * np.pi / 12, bottom=5, color='C1', linewidth=2, edgecolor='w', height=np.zeros_like(left_middle) + 2) -ax.bar(left=left_outer, +ax.bar(x=left_outer, width=2 * np.pi / 9, bottom=7, color='C2', linewidth=2, edgecolor='w', height=np.zeros_like(left_outer) + 3) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 1b8870f3a573..00488a134097 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1463,7 +1463,7 @@ def _replacer(data, key): def _preprocess_data(replace_names=None, replace_all_args=False, - label_namer=None, positional_parameter_names=None): + label_namer=None, positional_parameter_names=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must include the ax argument at the first position :: @@ -1720,7 +1720,7 @@ def inner(ax, *args, **kwargs): if len(replace_names) != 0: _repl = "* All arguments with the following names: '{names}'." if replace_all_args: - _repl += "\n* All positional arguments." + _repl += "\n * All positional arguments." _repl = _repl.format(names="', '".join(sorted(replace_names))) inner.__doc__ = (pre_doc + _DATA_DOC_APPENDIX.format(replaced=_repl)) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 15ba96030a89..402382587ccf 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1868,25 +1868,49 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) - @_preprocess_data(replace_names=["left", "height", "width", "bottom", + @_preprocess_data(replace_names=["x", "left", + "height", "width", + "y", "bottom", "color", "edgecolor", "linewidth", "tick_label", "xerr", "yerr", "ecolor"], - label_namer=None) + label_namer=None, + replace_all_args=True + ) @docstring.dedent_interpd - def bar(self, left, height, width=0.8, bottom=None, **kwargs): + def bar(self, *args, **kwargs): """ Make a bar plot. - Make a bar plot with rectangles bounded by: + Call signatures:: + + bar(x, height, *, align='center', **kwargs) + bar(x, height, width, *, align='center', **kwargs) + bar(x, height, width, bottom, *, align='center', **kwargs) + + Make a bar plot with rectangles bounded by + + .. math:: + + (x - width/2, x + width/2, bottom, bottom + height) + + (left, right, bottom and top edges) by default. *x*, + *height*, *width*, and *bottom* can be either scalars or + sequences. + + The *align* and *orientation* kwargs control the interpretation of *x* + and *bottom* - `left`, `left` + `width`, `bottom`, `bottom` + `height` - (left, right, bottom and top edges) + The *align* keyword-only argument controls if *x* is interpreted + as the center or the left edge of the rectangle. Parameters ---------- - left : sequence of scalars - the x coordinates of the left sides of the bars + x : sequence of scalars + the x coordinates of the bars. + + *align* controls if *x* is the bar center (default) or + left edge. height : scalar or sequence of scalars the height(s) of the bars @@ -1899,6 +1923,21 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): the y coordinate(s) of the bars default: None + align : {'center', 'edge'}, optional, default: 'center' + If 'center', interpret the *x* argument as the coordinates + of the centers of the bars. If 'edge', aligns bars by + their left edges + + To align the bars on the right edge pass a negative + *width* and ``align='edge'`` + + Returns + ------- + bars : matplotlib.container.BarContainer + Container with all of the bars + errorbars + + Other Parameters + ---------------- color : scalar or array-like, optional the colors of the bar faces @@ -1935,32 +1974,28 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): dictionary of kwargs to be passed to errorbar method. *ecolor* and *capsize* may be specified here rather than as independent kwargs. - align : {'center', 'edge'}, optional, default: 'center' - If 'edge', aligns bars by their left edges (for vertical bars) and - by their bottom edges (for horizontal bars). If 'center', interpret - the `left` argument as the coordinates of the centers of the bars. - To align on the align bars on the right edge pass a negative - `width`. - - orientation : {'vertical', 'horizontal'}, optional - The orientation of the bars. - log : boolean, optional If true, sets the axis to be log scale. default: False - Returns - ------- - bars : matplotlib.container.BarContainer - Container with all of the bars + errorbars + orientation : {'vertical', 'horizontal'}, optional + + This is for internal use, please do not directly use this, + call `barh` instead. + + The orientation of the bars. + + See also + -------- + barh: Plot a horizontal bar plot. Notes ----- - The optional arguments `color`, `edgecolor`, `linewidth`, - `xerr`, and `yerr` can be either scalars or sequences of + The optional arguments *color*, *edgecolor*, *linewidth*, + *xerr*, and *yerr* can be either scalars or sequences of length equal to the number of bars. This enables you to use bar as the basis for stacked bar charts, or candlestick plots. - Detail: `xerr` and `yerr` are passed directly to + Detail: *xerr* and *yerr* are passed directly to :meth:`errorbar`, so they can also have shape 2xN for independent specification of lower and upper errors. @@ -1968,11 +2003,35 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): %(Rectangle)s - See also - -------- - barh: Plot a horizontal bar plot. """ kwargs = cbook.normalize_kwargs(kwargs, mpatches._patch_alias_map) + # this is using the lambdas to do the arg/kwarg unpacking rather + # than trying to re-implement all of that logic our selves. + matchers = [ + (lambda x, height, width=0.8, bottom=None, **kwargs: + (False, x, height, width, bottom, kwargs)), + (lambda left, height, width=0.8, bottom=None, **kwargs: + (True, left, height, width, bottom, kwargs)), + ] + exps = [] + for matcher in matchers: + try: + dp, x, height, width, y, kwargs = matcher(*args, **kwargs) + except TypeError as e: + # This can only come from a no-match as there is + # no other logic in the matchers. + exps.append(e) + else: + break + else: + raise exps[0] + # if we matched the second-case, then the user passed in + # left=val as a kwarg which we want to deprecate + if dp: + warnings.warn( + "The *left* kwarg to `bar` is deprecated use *x* instead. " + "Support for *left* will be removed in Matplotlib 3.0", + mplDeprecation, stacklevel=2) if not self._hold: self.cla() color = kwargs.pop('color', None) @@ -2002,38 +2061,39 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): label = kwargs.pop('label', '') tick_labels = kwargs.pop('tick_label', None) - _left = left - _bottom = bottom - left, height, width, bottom = np.broadcast_arrays( - # Make args iterable too. - np.atleast_1d(left), height, width, bottom) - adjust_ylim = False adjust_xlim = False + if orientation == 'vertical': - self._process_unit_info(xdata=left, ydata=height, kwargs=kwargs) - if log: - self.set_yscale('log', nonposy='clip') - # size width and bottom according to length of left - if _bottom is None: + if y is None: if self.get_yscale() == 'log': adjust_ylim = True - bottom = np.zeros_like(bottom) + y = 0 + + elif orientation == 'horizontal': + if x is None: + if self.get_xscale() == 'log': + adjust_xlim = True + x = 0 + + x, height, width, y, linewidth = np.broadcast_arrays( + # Make args iterable too. + np.atleast_1d(x), height, width, y, linewidth) + + if orientation == 'vertical': + self._process_unit_info(xdata=x, ydata=height, kwargs=kwargs) + if log: + self.set_yscale('log', nonposy='clip') tick_label_axis = self.xaxis - tick_label_position = left + tick_label_position = x elif orientation == 'horizontal': - self._process_unit_info(xdata=width, ydata=bottom, kwargs=kwargs) + self._process_unit_info(xdata=width, ydata=y, kwargs=kwargs) if log: self.set_xscale('log', nonposx='clip') - # size left and height according to length of bottom - if _left is None: - if self.get_xscale() == 'log': - adjust_xlim = True - left = np.zeros_like(left) tick_label_axis = self.yaxis - tick_label_position = bottom + tick_label_position = y else: raise ValueError('invalid orientation: %s' % orientation) @@ -2051,24 +2111,30 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): # lets do some conversions now since some types cannot be # subtracted uniformly if self.xaxis is not None: - left = self.convert_xunits(left) + x = self.convert_xunits(x) width = self.convert_xunits(width) if xerr is not None: xerr = self.convert_xunits(xerr) if self.yaxis is not None: - bottom = self.convert_yunits(bottom) + y = self.convert_yunits(y) height = self.convert_yunits(height) if yerr is not None: yerr = self.convert_yunits(yerr) + # We will now resolve the alignment and really have + # left, bottom, width, height vectors if align == 'center': if orientation == 'vertical': - left = left - width / 2 + left = x - width / 2 + bottom = y elif orientation == 'horizontal': - bottom = bottom - height / 2 - - elif align != 'edge': + bottom = y - height / 2 + left = x + elif align == 'edge': + left = x + bottom = y + else: raise ValueError('invalid alignment: %s' % align) patches = [] @@ -2096,18 +2162,17 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): if xerr is not None or yerr is not None: if orientation == 'vertical': # using list comps rather than arrays to preserve unit info - x = [l + 0.5 * w for l, w in zip(left, width)] - y = [b + h for b, h in zip(bottom, height)] + ex = [l + 0.5 * w for l, w in zip(left, width)] + ey = [b + h for b, h in zip(bottom, height)] elif orientation == 'horizontal': # using list comps rather than arrays to preserve unit info - x = [l + w for l, w in zip(left, width)] - y = [b + 0.5 * h for b, h in zip(bottom, height)] + ex = [l + w for l, w in zip(left, width)] + ey = [b + 0.5 * h for b, h in zip(bottom, height)] - if "label" not in error_kw: - error_kw["label"] = '_nolegend_' + error_kw.setdefault("label", '_nolegend_') - errorbar = self.errorbar(x, y, + errorbar = self.errorbar(ex, ey, yerr=yerr, xerr=xerr, fmt='none', **error_kw) else: @@ -2143,23 +2208,37 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): return bar_container @docstring.dedent_interpd - def barh(self, bottom, width, height=0.8, left=None, **kwargs): + def barh(self, *args, **kwargs): """ Make a horizontal bar plot. - Make a horizontal bar plot with rectangles bounded by: + Call signatures:: - `left`, `left` + `width`, `bottom`, `bottom` + `height` - (left, right, bottom and top edges) + bar(y, width, *, align='center', **kwargs) + bar(y, width, height, *, align='center', **kwargs) + bar(y, width, height, left, *, align='center', **kwargs) + + Make a horizontal bar plot with rectangles by default bounded by + + .. math:: + + (left, left + width, y - height/2, y + height/2) + + (left, right, bottom and top edges) by default. *y*, *width*, + *height*, and *left* can be either scalars or sequences. + + The *align* keyword-only argument controls if *y* is interpreted + as the center or the bottom edge of the rectangle. - `bottom`, `width`, `height`, and `left` can be either scalars - or sequences Parameters ---------- - bottom : scalar or array-like + y : scalar or array-like the y coordinate(s) of the bars + *align* controls if *y* is the bar center (default) + or bottom edge. + width : scalar or array-like the width(s) of the bars @@ -2169,6 +2248,14 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): left : sequence of scalars the x coordinates of the left sides of the bars + align : {'center', 'edge'}, optional, default: 'center' + If 'center', interpret the *y* argument as the coordinates + of the centers of the bars. If 'edge', aligns bars by + their bottom edges + + To align the bars on the top edge pass a negative + *height* and ``align='edge'`` + Returns ------- `matplotlib.patches.Rectangle` instances. @@ -2206,23 +2293,20 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): dictionary of kwargs to be passed to errorbar method. `ecolor` and `capsize` may be specified here rather than as independent kwargs. - align : {'center', 'edge'}, optional, default: 'center' - If 'edge', aligns bars by their left edges (for vertical - bars) and by their bottom edges (for horizontal bars). If - 'center', interpret the `bottom` argument as the - coordinates of the centers of the bars. To align on the - align bars on the top edge pass a negative 'height'. - log : boolean, optional, default: False If true, sets the axis to be log scale + See also + -------- + bar: Plot a vertical bar plot. + Notes ----- - The optional arguments `color`, `edgecolor`, `linewidth`, - `xerr`, and `yerr` can be either scalars or sequences of + The optional arguments *color*, *edgecolor*, *linewidth*, + *xerr*, and *yerr* can be either scalars or sequences of length equal to the number of bars. This enables you to use bar as the basis for stacked bar charts, or candlestick plots. - Detail: `xerr` and `yerr` are passed directly to + Detail: *xerr* and *yerr* are passed directly to :meth:`errorbar`, so they can also have shape 2xN for independent specification of lower and upper errors. @@ -2230,13 +2314,36 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): %(Rectangle)s - See also - -------- - bar: Plot a vertical bar plot. """ - - patches = self.bar(left=left, height=height, width=width, - bottom=bottom, orientation='horizontal', **kwargs) + # this is using the lambdas to do the arg/kwarg unpacking rather + # than trying to re-implement all of that logic our selves. + matchers = [ + (lambda y, width, height=0.8, left=None, **kwargs: + (False, y, width, height, left, kwargs)), + (lambda bottom, width, height=0.8, left=None, **kwargs: + (True, bottom, width, height, left, kwargs)), + ] + excs = [] + for matcher in matchers: + try: + dp, y, width, height, left, kwargs = matcher(*args, **kwargs) + except TypeError as e: + # This can only come from a no-match as there is + # no other logic in the matchers. + excs.append(e) + else: + break + else: + raise excs[0] + + if dp: + warnings.warn( + "The *bottom* kwarg to `barh` is deprecated use *y* instead. " + "Support for *bottom* will be removed in Matplotlib 3.0", + mplDeprecation, stacklevel=2) + kwargs.setdefault('orientation', 'horizontal') + patches = self.bar(x=left, height=height, width=width, + bottom=y, **kwargs) return patches @_preprocess_data(label_namer=None) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 85b5ea47b657..7b46dc3179d4 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2622,20 +2622,19 @@ def axvspan(xmin, xmax, ymin=0, ymax=1, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.bar) -def bar(left, height, width=0.8, bottom=None, hold=None, data=None, **kwargs): +def bar(*args, **kwargs): ax = gca() # Deprecated: allow callers to override the hold state # by passing hold=True|False washold = ax._hold - + hold = kwargs.pop('hold', None) if hold is not None: ax._hold = hold from matplotlib.cbook import mplDeprecation warnings.warn("The 'hold' keyword argument is deprecated since 2.0.", mplDeprecation) try: - ret = ax.bar(left, height, width=width, bottom=bottom, data=data, - **kwargs) + ret = ax.bar(*args, **kwargs) finally: ax._hold = washold @@ -2644,19 +2643,19 @@ def bar(left, height, width=0.8, bottom=None, hold=None, data=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.barh) -def barh(bottom, width, height=0.8, left=None, hold=None, **kwargs): +def barh(*args, **kwargs): ax = gca() # Deprecated: allow callers to override the hold state # by passing hold=True|False washold = ax._hold - + hold = kwargs.pop('hold', None) if hold is not None: ax._hold = hold from matplotlib.cbook import mplDeprecation warnings.warn("The 'hold' keyword argument is deprecated since 2.0.", mplDeprecation) try: - ret = ax.barh(bottom, width, height=height, left=left, **kwargs) + ret = ax.barh(*args, **kwargs) finally: ax._hold = washold @@ -2993,10 +2992,10 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hist) -def hist(x, bins=None, range=None, normed=False, weights=None, cumulative=False, +def hist(x, bins=None, range=None, density=None, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, - hold=None, data=None, **kwargs): + normed=None, hold=None, data=None, **kwargs): ax = gca() # Deprecated: allow callers to override the hold state # by passing hold=True|False @@ -3008,11 +3007,11 @@ def hist(x, bins=None, range=None, normed=False, weights=None, cumulative=False, warnings.warn("The 'hold' keyword argument is deprecated since 2.0.", mplDeprecation) try: - ret = ax.hist(x, bins=bins, range=range, normed=normed, + ret = ax.hist(x, bins=bins, range=range, density=density, weights=weights, cumulative=cumulative, bottom=bottom, histtype=histtype, align=align, orientation=orientation, rwidth=rwidth, log=log, color=color, label=label, - stacked=stacked, data=data, **kwargs) + stacked=stacked, normed=normed, data=data, **kwargs) finally: ax._hold = washold diff --git a/lib/matplotlib/testing/determinism.py b/lib/matplotlib/testing/determinism.py index 972df062075e..aca4572b2e5b 100644 --- a/lib/matplotlib/testing/determinism.py +++ b/lib/matplotlib/testing/determinism.py @@ -43,8 +43,8 @@ def _determinism_save(objects='mhi', format="pdf", usetex=False): if 'h' in objects: # also use different hatch patterns ax2 = fig.add_subplot(1, 6, 2) - bars = ax2.bar(range(1, 5), range(1, 5)) + \ - ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5)) + bars = (ax2.bar(range(1, 5), range(1, 5)) + + ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5))) ax2.set_xticks([1.5, 2.5, 3.5, 4.5]) patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.') diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 7a0e8f326fc2..1d0aeee0a8e5 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5266,6 +5266,38 @@ def test_twinx_knows_limits(): assert((xtwin.viewLim.intervalx == ax2.viewLim.intervalx).all()) +@pytest.mark.style('mpl20') +@pytest.mark.parametrize('args, kwargs, warning_count', + [((1, 1), {'width': 1, 'bottom': 1}, 0), + ((1, ), {'height': 1, 'bottom': 1}, 0), + ((), {'x': 1, 'height': 1}, 0), + ((), {'left': 1, 'height': 1}, 1)]) +def test_bar_signature(args, kwargs, warning_count): + fig, ax = plt.subplots() + with warnings.catch_warnings(record=True) as w: + r, = ax.bar(*args, **kwargs) + + assert r.get_width() == kwargs.get('width', 0.8) + assert r.get_y() == kwargs.get('bottom', 0) + assert len(w) == warning_count + + +@pytest.mark.style('mpl20') +@pytest.mark.parametrize('args, kwargs, warning_count', + [((1, 1), {'height': 1, 'left': 1}, 0), + ((1, ), {'width': 1, 'left': 1}, 0), + ((), {'y': 1, 'width': 1}, 0), + ((), {'bottom': 1, 'width': 1}, 1)]) +def test_barh_signature(args, kwargs, warning_count): + fig, ax = plt.subplots() + with warnings.catch_warnings(record=True) as w: + r, = ax.barh(*args, **kwargs) + + assert r.get_height() == kwargs.get('height', 0.8) + assert r.get_x() == kwargs.get('left', 0) + assert len(w) == warning_count + + def test_zero_linewidth(): # Check that setting a zero linewidth doesn't error plt.plot([0, 1], [0, 1], ls='--', lw=0) diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index de3b30cb5966..6d8d8f0f73ee 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -31,7 +31,7 @@ def test_simple(): # pickle.dump(ax, BytesIO(), pickle.HIGHEST_PROTOCOL) plt.figure() - plt.bar(left=np.arange(10), height=np.arange(10)) + plt.bar(x=np.arange(10), height=np.arange(10)) pickle.dump(plt.gca(), BytesIO(), pickle.HIGHEST_PROTOCOL) fig = plt.figure() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy