Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f5ef8bd

Browse files
author
Vlas Sokolov
committed
fix compatibility issues with 2d errorbar version (WIP)
* Integrate errorbar 2d changes since 2017 * Integrate the merged updated errorevery behavior * Remove the deprecation warning for fmt=None * Fix ecolor working for lines but not for caps * Fix broken cycler + ecolor behavior
1 parent c30c276 commit f5ef8bd

2 files changed

Lines changed: 123 additions & 118 deletions

File tree

doc/users/whats_new/errorbars_3d.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Errorbar method for mplot3d
2-
------------------------
2+
---------------------------
33

44
The errorbar function :meth:`matplotlib.axes._axes.Axes.errorbar` is ported
55
into the `mplot3d` framework in its entirety. Supports features such as custom

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 122 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
from collections import defaultdict
1414
from functools import reduce
15-
import logging
1615
import math
1716
import textwrap
1817

@@ -34,8 +33,6 @@
3433
from . import proj3d
3534
from . import axis3d
3635

37-
_log = logging.getLogger(__name__)
38-
3936

4037
@cbook.deprecated("3.2", alternative="Bbox.unit()")
4138
def unit_bbox():
@@ -2779,97 +2776,95 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
27792776
ylolims=False, yuplims=False, zlolims=False, zuplims=False,
27802777
**kwargs):
27812778
"""
2782-
Draws error bars on an Axis3D instance.
2779+
Plot lines and/or markers with errorbars around them.
2780+
2781+
*x*/*y*/*z* define the data locations, and *xerr*/*yerr*/*zerr* define
2782+
the errorbar sizes. By default, this draws the data markers/lines as
2783+
well the errorbars. Use fmt='none' to draw errorbars only.
27832784
27842785
Parameters
27852786
----------
2786-
x : scalar
2787-
y : scalar
2788-
z : scalar
2789-
2790-
xerr/yerr/zerr : scalar or array-like, shape(n,1) or shape(2,n).
2791-
If a scalar number, len(N) array-like object, or an Nx1
2792-
array-like object, errorbars are drawn at +/-value relative
2793-
to the data. Default is None.
2794-
2795-
If a sequence of shape 2xN, errorbars are drawn at -row1
2796-
and +row2 relative to the data.
2797-
2798-
fmt : plot format string, optional, default: None
2799-
The plot format symbol. If fmt is 'none' (case-insensitive),
2800-
only the errorbars are plotted. This is used for adding
2801-
errorbars to a bar plot, for example. Default is '',
2802-
an empty plot format string; properties are
2803-
then identical to the defaults for :meth:`plot`.
2804-
2805-
ecolor : mpl color, optional, default: None
2806-
A matplotlib color arg which gives the color the errorbar lines;
2807-
if None, use the color of the line connecting the markers.
2808-
2809-
elinewidth : scalar, optional, default: None
2810-
The linewidth of the errorbar lines. If None, use the linewidth.
2811-
2812-
capsize : scalar, optional, default: None
2813-
The length of the error bar caps in points; if None, it will
2814-
take the value from ``errorbar.capsize``
2815-
:data:`rcParam<matplotlib.rcParams>`.
2816-
2817-
capthick : scalar, optional, default: None
2818-
An alias kwarg to markeredgewidth (a.k.a. - mew). This
2819-
setting is a more sensible name for the property that
2787+
x, y, z : scalar or array-like
2788+
The data positions.
2789+
2790+
xerr, yerr, zerr : scalar or array-like, optional
2791+
The errorbar sizes:
2792+
- scalar: Symmetric +/- values for all data points.
2793+
- shape(N,): Symmetric +/-values for each data point.
2794+
- shape(2, N): Separate - and + values for each bar. First row
2795+
contains the lower errors, the second row contains the upper
2796+
errors.
2797+
- *None*: No errorbar.
2798+
2799+
fmt : str, default: ''
2800+
The format for the data points / data lines. See `.plot` for
2801+
details.
2802+
2803+
Use 'none' (case insensitive) to plot errorbars without any data
2804+
markers.
2805+
2806+
ecolor : color, default: None
2807+
The color of the errorbar lines. If None, use the color of the
2808+
line connecting the markers.
2809+
2810+
elinewidth : scalar, default: None
2811+
The linewidth of the errorbar lines. If None, the linewidth of
2812+
the current style is used.
2813+
2814+
capsize : scalar, default: :rc:`errorbar.capsize`
2815+
The length of the error bar caps in points.
2816+
2817+
capthick : scalar, default: None
2818+
An alias to the keyword argument *markeredgewidth* (a.k.a. *mew*).
2819+
This setting is a more sensible name for the property that
28202820
controls the thickness of the error bar cap in points. For
2821-
backwards compatibility, if mew or markeredgewidth are given,
2822-
then they will over-ride capthick. This may change in future
2821+
backwards compatibility, if *mew* or *markeredgewidth* are given,
2822+
then they will over-ride *capthick*. This may change in future
28232823
releases.
28242824
2825-
barsabove : bool, optional, default: False
2826-
if True , will plot the errorbars above the plot
2825+
barsabove : bool, default: False
2826+
If True, will plot the errorbars above the plot
28272827
symbols. Default is below.
28282828
2829-
xlolims / ylolims / zlolims : bool, optional, default:None
2829+
xlolims, ylolims, zlolims : bool, default: False
28302830
These arguments can be used to indicate that a value gives
28312831
only lower limits. In that case a caret symbol is being
28322832
drawn to indicate this. lims-arguments may be of the same
28332833
type as *xerr* and *yerr*.
28342834
2835-
xuplims / yuplims / zuplims : bool, optional, default:None
2835+
xuplims, yuplims, zuplims : bool, default: False
28362836
Same as above, but for controlling the upper limits.
28372837
2838-
errorevery: positive integer or tuple of integers
2839-
draws error bars on a subset of the data. errorevery=skip draws
2840-
error bars on the [::skip] intervals. If a tuple of integers,
2841-
errorevery=(skip,shift) draws error bars on the points
2842-
selected as [skip%%shift::skip]. e.g. errorevery=(6,3)
2843-
adds error bars to the data at (x[3], x[9], x[15], x[21], ...).
2838+
errorevery : int or (int, int), default: 1
2839+
draws error bars on a subset of the data. *errorevery* =N draws
2840+
error bars on the points (x[::N], y[::N]).
2841+
*errorevery* =(start, N) draws error bars on the points
2842+
(x[start::N], y[start::N]). e.g. errorevery=(6, 3)
2843+
adds error bars to the data at (x[6], x[9], x[12], x[15], ...).
2844+
Used to avoid overlapping error bars when two series share x-axis
2845+
values.
28442846
28452847
Additional keyword arguments for styling errorbar lines are passed to
2846-
:func:`~mpl_toolkits.mplot3d.art3d.Line3DCollection`
2848+
`~mpl_toolkits.mplot3d.art3d.Line3DCollection`
28472849
"""
28482850
had_data = self.has_data()
28492851

2850-
if not np.iterable(x):
2851-
x = [x]
2852-
if not np.iterable(y):
2853-
y = [y]
2854-
if not np.iterable(z):
2855-
z = [z]
2856-
2857-
if fmt is None:
2858-
fmt = 'none'
2859-
msg = ('Use of None object as fmt keyword argument to ' +
2860-
'suppress plotting of data values is deprecated ' +
2861-
'since 1.4; use the string "none" instead.')
2862-
_log.warning(msg)
2863-
28642852
plot_line = (fmt.lower() != 'none')
28652853
label = kwargs.pop("label", None)
28662854

2867-
fmt_style_kwargs = {k: v for k, v in
2868-
zip(('linestyle', 'marker', 'color'),
2869-
_process_plot_format(fmt)) if v is not None}
2855+
if fmt == '':
2856+
fmt_style_kwargs = {}
2857+
else:
2858+
fmt_style_kwargs = {k: v for k, v in
2859+
zip(('linestyle', 'marker', 'color'),
2860+
_process_plot_format(fmt))
2861+
if v is not None}
2862+
2863+
if fmt == 'none':
2864+
# Remove alpha=0 color that _process_plot_format returns
2865+
fmt_style_kwargs.pop('color')
28702866

2871-
if ('color' in kwargs or 'color' in fmt_style_kwargs or
2872-
ecolor is not None):
2867+
if ('color' in kwargs or 'color' in fmt_style_kwargs):
28732868
base_style = {}
28742869
if 'color' in kwargs:
28752870
base_style['color'] = kwargs.pop('color')
@@ -2882,9 +2877,28 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
28822877
if ecolor is None:
28832878
ecolor = base_style['color']
28842879

2880+
# make sure all the args are iterable; use lists not arrays to
2881+
# preserve units
2882+
x = x if np.iterable(x) else [x]
2883+
y = y if np.iterable(y) else [y]
2884+
z = z if np.iterable(z) else [z]
2885+
2886+
# make the style dict for the 'normal' plot line
2887+
if 'zorder' not in kwargs:
2888+
kwargs['zorder'] = 2
2889+
plot_line_style = {
2890+
**base_style,
2891+
**kwargs,
2892+
'zorder': (kwargs['zorder'] - .1 if barsabove else
2893+
kwargs['zorder'] + .1),
2894+
}
2895+
28852896
# make the style dict for the line collections (the bars)
28862897
eb_lines_style = dict(base_style)
28872898
eb_lines_style.pop('marker', None)
2899+
eb_lines_style.pop('markerfacecolor', None)
2900+
eb_lines_style.pop('markeredgewidth', None)
2901+
eb_lines_style.pop('markeredgecolor', None)
28882902
eb_lines_style.pop('linestyle', None)
28892903
eb_lines_style['color'] = ecolor
28902904

@@ -2897,29 +2911,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
28972911
if key in kwargs:
28982912
eb_lines_style[key] = kwargs[key]
28992913

2900-
try:
2901-
errorevery, offset = errorevery
2902-
except TypeError:
2903-
offset = 0
2904-
2905-
int_msg = 'errorevery must be positive integer or tuple of integers'
2906-
if errorevery < 1 or int(errorevery) != errorevery:
2907-
raise ValueError(int_msg)
2908-
if int(offset) != offset:
2909-
raise ValueError(int_msg)
2910-
2911-
everymask = (np.arange(len(x)) - offset) % errorevery == 0
2912-
2913-
plot_line_style = dict(base_style)
2914-
plot_line_style.update(**kwargs)
2915-
if 'zorder' not in kwargs.keys():
2916-
kwargs['zorder'] = 2
2917-
if barsabove:
2918-
plot_line_style['zorder'] = kwargs['zorder'] - .1
2919-
else:
2920-
plot_line_style['zorder'] = kwargs['zorder'] + .1
2921-
2922-
# set up cap style dictionary
2914+
# make the style dict for cap collections (the "hats")
29232915
eb_cap_style = dict(base_style)
29242916
# eject any marker information from format string
29252917
eb_cap_style.pop('marker', None)
@@ -2931,16 +2923,25 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
29312923
eb_cap_style['markersize'] = 2. * capsize
29322924
if capthick is not None:
29332925
eb_cap_style['markeredgewidth'] = capthick
2926+
eb_cap_style['color'] = ecolor
29342927

29352928
if plot_line:
29362929
data_line = art3d.Line3D(x, y, z, **plot_line_style)
29372930
self.add_line(data_line)
29382931

2939-
def _bool_asarray_helper(d, expected):
2940-
if not np.iterable(d):
2941-
return np.asarray([d] * expected, bool)
2932+
try:
2933+
offset, errorevery = errorevery
2934+
except TypeError:
2935+
offset = 0
2936+
2937+
if errorevery < 1 or int(errorevery) != errorevery:
2938+
raise ValueError(
2939+
'errorevery must be positive integer or tuple of integers')
2940+
if int(offset) != offset:
2941+
raise ValueError("errorevery's starting index must be an integer")
29422942

2943-
return np.asarray(d, bool)
2943+
everymask = np.zeros(len(x), bool)
2944+
everymask[offset::errorevery] = True
29442945

29452946
def _mask_lists(xs, ys, zs, mask=None):
29462947
""" Applies a mask to three lists. """
@@ -2949,8 +2950,8 @@ def _mask_lists(xs, ys, zs, mask=None):
29492950
zs = [l for l, m in zip(zs, mask) if m]
29502951
return xs, ys, zs
29512952

2952-
# TODO: currently, only a scalar number or len(N) objects are ok...
2953-
def _unpack_errs(data, err, lomask, himask):
2953+
# TODO: errors can be only a scalar number or len(N) array-like
2954+
def _unpack_errs(err, data, lomask, himask):
29542955
lows = [d - e if m else d for d, e, m in zip(data, err, lomask)]
29552956
highs = [d + e if m else d for d, e, m in zip(data, err, himask)]
29562957
return lows, highs
@@ -2959,6 +2960,17 @@ def _unpack_errs(data, err, lomask, himask):
29592960
# List of endpoint coordinates, used for auto-scaling
29602961
coorderrs = []
29612962

2963+
# define the markers used for errorbar caps and limits below
2964+
# the dictionary key is the current ticking value of `i_xyz`
2965+
capmarker = {0: '|', 1: '|', 2: '_'}
2966+
limmarker = {0: {'lower': art3d.lines.CARETRIGHT,
2967+
'upper': art3d.lines.CARETLEFT},
2968+
1: {'lower': art3d.lines.CARETRIGHT,
2969+
'upper': art3d.lines.CARETLEFT},
2970+
2: {'lower': art3d.lines.CARETUP,
2971+
'upper': art3d.lines.CARETDOWN}}
2972+
2973+
# i_xyz determines which coordinate is currently being looped over
29622974
for data, err, i_xyz, lolims, uplims in zip(
29632975
[x, y, z], [xerr, yerr, zerr], range(3),
29642976
[xlolims, ylolims, zlolims], [xuplims, yuplims, zuplims]):
@@ -2968,39 +2980,31 @@ def _unpack_errs(data, err, lomask, himask):
29682980

29692981
if not np.iterable(err):
29702982
err = [err] * len(data)
2971-
lolims = _bool_asarray_helper(lolims, len(x))
2972-
uplims = _bool_asarray_helper(uplims, len(x))
2983+
2984+
# FIXME: err data is not supposed to be transformed into arrays!
2985+
err = np.atleast_1d(err)
2986+
2987+
# arrays fine here, they are booleans and hence not units
2988+
lolims = np.broadcast_to(lolims, len(data)).astype(bool)
2989+
uplims = np.broadcast_to(uplims, len(data)).astype(bool)
29732990

29742991
nolims = ~(lolims | uplims)
29752992

29762993
# NOTE: care needs to be taken here - using numpy is fine,
29772994
# as long as the actual data plotted stays as lists.
29782995
# This is due to unit preservation issues
29792996
# (c.f. the 2d errorbar case).
2980-
rolling_mask = np.roll([1., 0., 0.], i_xyz)
2981-
# TODO: why is this here?
2982-
if err is not None:
2983-
err = np.atleast_1d(err)
2997+
rolling_mask = np.roll([1, 0, 0], i_xyz)
29842998

29852999
# a nested list structure that expands to (xl,xh),(yl,yh),(zl,zh),
29863000
# where x/y/z and l/h correspond to dimensions and low/high
29873001
# positions of errorbars in a dimension we're looping over
29883002
coorderr = [
2989-
_unpack_errs(coord, err * rolling_mask[i],
3003+
_unpack_errs(err * rolling_mask[i], coord,
29903004
~lolims & everymask, ~uplims & everymask)
29913005
for i, coord in enumerate([x, y, z])]
29923006
(xl, xh), (yl, yh), (zl, zh) = coorderr
29933007

2994-
# define the markers used for errorbar caps and limits below
2995-
# the dicitonary key is the current ticking value of `i_xyz`
2996-
capmarker = {0: '|', 1: '|', 2: '_'}
2997-
limmarker = {0: {'lower': art3d.lines.CARETRIGHT,
2998-
'upper': art3d.lines.CARETLEFT},
2999-
1: {'lower': art3d.lines.CARETRIGHT,
3000-
'upper': art3d.lines.CARETLEFT},
3001-
2: {'lower': art3d.lines.CARETUP,
3002-
'upper': art3d.lines.CARETDOWN}}
3003-
30043008
if nolims.any():
30053009
if capsize > 0:
30063010
lo_caps_xyz = _mask_lists(xl, yl, zl, nolims & everymask)
@@ -3029,7 +3033,7 @@ def _unpack_errs(data, err, lomask, himask):
30293033
# the markers around... However, this solution is
30303034
# spiritually close to that of 2d errorbar function
30313035
limits = [
3032-
_unpack_errs(coord, err*rolling_mask[i], uplims, lolims)
3036+
_unpack_errs(err*rolling_mask[i], coord, uplims, lolims)
30333037
for i, coord in enumerate([x, y, z])]
30343038
(xlo, xup), (ylo, yup), (zlo, zup) = limits
30353039

@@ -3056,6 +3060,7 @@ def _unpack_errs(data, err, lomask, himask):
30563060

30573061
coorderrs = np.array(coorderrs)
30583062

3063+
# TODO: errors can be only a scalar number or len(N) array-like
30593064
def _digout_minmax(err_arr, coord_label):
30603065
key = {'x': 0, 'y': 1, 'z': 2}
30613066
return (np.nanmin(err_arr[:, key[coord_label], :, :]),

0 commit comments

Comments
 (0)