1212
1313from collections import defaultdict
1414from functools import reduce
15- import logging
1615import math
1716import textwrap
1817
3433from . import proj3d
3534from . import axis3d
3635
37- _log = logging .getLogger (__name__ )
38-
3936
4037@cbook .deprecated ("3.2" , alternative = "Bbox.unit()" )
4138def 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