32
32
import matplotlib .ticker as mticker
33
33
import matplotlib .transforms as mtransforms
34
34
import matplotlib .tri as mtri
35
+ import matplotlib .units as munits
35
36
from matplotlib .container import BarContainer , ErrorbarContainer , StemContainer
36
37
from matplotlib .axes ._base import _AxesBase , _process_plot_format
37
38
@@ -2012,56 +2013,27 @@ def step(self, x, y, *args, where='pre', data=None, **kwargs):
2012
2013
return self .plot (x , y , * args , data = data , ** kwargs )
2013
2014
2014
2015
@staticmethod
2015
- def _convert_dx ( dx , x0 , xconv , convert ):
2016
+ def _convert_dz ( axis , z , z_conv , dz ):
2016
2017
"""
2017
- Small helper to do logic of width conversion flexibly.
2018
-
2019
- *dx* and *x0* have units, but *xconv* has already been converted
2020
- to unitless (and is an ndarray). This allows the *dx* to have units
2021
- that are different from *x0*, but are still accepted by the
2022
- ``__add__`` operator of *x0*.
2018
+ Convert potentially unitized *dz* into unitless values using the
2019
+ *axis*' converter and *z*/*z_conv* as values (respectively unitized and
2020
+ unitless) over which *dz* apply.
2023
2021
"""
2024
-
2025
- # x should be an array...
2026
- assert type (xconv ) is np .ndarray
2027
-
2028
- if xconv .size == 0 :
2029
- # xconv has already been converted, but maybe empty...
2030
- return convert (dx )
2031
-
2032
- try :
2033
- # attempt to add the width to x0; this works for
2034
- # datetime+timedelta, for instance
2035
-
2036
- # only use the first element of x and x0. This saves
2037
- # having to be sure addition works across the whole
2038
- # vector. This is particularly an issue if
2039
- # x0 and dx are lists so x0 + dx just concatenates the lists.
2040
- # We can't just cast x0 and dx to numpy arrays because that
2041
- # removes the units from unit packages like `pint` that
2042
- # wrap numpy arrays.
2043
- try :
2044
- x0 = x0 [0 ]
2045
- except (TypeError , IndexError , KeyError ):
2046
- x0 = x0
2047
-
2048
- try :
2049
- x = xconv [0 ]
2050
- except (TypeError , IndexError , KeyError ):
2051
- x = xconv
2052
-
2053
- delist = False
2054
- if not np .iterable (dx ):
2055
- dx = [dx ]
2056
- delist = True
2057
- dx = [convert (x0 + ddx ) - x for ddx in dx ]
2058
- if delist :
2059
- dx = dx [0 ]
2060
- except (ValueError , TypeError , AttributeError ):
2061
- # if the above fails (for any reason) just fallback to what
2062
- # we do by default and convert dx by iteslf.
2063
- dx = convert (dx )
2064
- return dx
2022
+ dz_orig = dz
2023
+ if axis .converter is not None : # z was unitized.
2024
+ if munits .ConversionInterface .is_numlike (dz ): # Scalar width.
2025
+ if np .size (z_conv ) == 1 :
2026
+ dz = np .diff (axis .get_view_interval ()) * dz
2027
+ else :
2028
+ dz = np .array (dz ) * np .ptp (z_conv ) / (np .size (z_conv ) - 1 )
2029
+ else : # Unitized.
2030
+ dz = [axis .convert_units (_z + _dz ) - axis .convert_units (_z )
2031
+ for _z , _dz in zip (
2032
+ # Same as atleast_1d, but keeps units.
2033
+ cbook .flatten ([z ]), cbook .flatten ([dz ]))]
2034
+ if np .ndim (dz_orig ) == 0 and np .ndim (dz ) > 0 :
2035
+ dz , = dz
2036
+ return dz
2065
2037
2066
2038
@_preprocess_data ()
2067
2039
@docstring .dedent_interpd
@@ -2088,7 +2060,8 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
2088
2060
The height(s) of the bars.
2089
2061
2090
2062
width : scalar or array-like, optional
2091
- The width(s) of the bars (default: 0.8).
2063
+ The width(s) of the bars (default: 0.8, but see note below if *x*
2064
+ has units).
2092
2065
2093
2066
bottom : scalar or array-like, optional
2094
2067
The y coordinate(s) of the bars bases (default: 0).
@@ -2166,14 +2139,19 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
2166
2139
*xerr*, and *yerr* can be either scalars or sequences of
2167
2140
length equal to the number of bars. This enables you to use
2168
2141
bar as the basis for stacked bar charts, or candlestick plots.
2169
- Detail: *xerr* and *yerr* are passed directly to
2170
- :meth:`errorbar`, so they can also have shape 2xN for
2171
- independent specification of lower and upper errors.
2142
+
2143
+ If *x* has units, then *width* should normally also be specified with
2144
+ units (so that they can be meaningfully added). However, if a unitless
2145
+ *width* is used (in particular, for the default *width*), then it
2146
+ is interpreted as a fraction of the average inter-bar spacing. For
2147
+ example, if the bars are equally spaced, then a *width* of 1 makes the
2148
+ bars contiguous. As a special case, if there is a *single* bar, then
2149
+ a unitless *width* is interpreted as a fraction of the axis (current)
2150
+ view span.
2172
2151
2173
2152
Other optional kwargs:
2174
2153
2175
2154
%(Rectangle)s
2176
-
2177
2155
"""
2178
2156
kwargs = cbook .normalize_kwargs (kwargs , mpatches .Patch ._alias_map )
2179
2157
color = kwargs .pop ('color' , None )
@@ -2226,18 +2204,16 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
2226
2204
2227
2205
# lets do some conversions now since some types cannot be
2228
2206
# subtracted uniformly
2229
- if self .xaxis is not None :
2230
- x0 = x
2231
- x = np .asarray (self .convert_xunits (x ))
2232
- width = self ._convert_dx (width , x0 , x , self .convert_xunits )
2233
- if xerr is not None :
2234
- xerr = self ._convert_dx (xerr , x0 , x , self .convert_xunits )
2235
- if self .yaxis is not None :
2236
- y0 = y
2237
- y = np .asarray (self .convert_yunits (y ))
2238
- height = self ._convert_dx (height , y0 , y , self .convert_yunits )
2239
- if yerr is not None :
2240
- yerr = self ._convert_dx (yerr , y0 , y , self .convert_yunits )
2207
+ x0 = x
2208
+ x = self .convert_xunits (x )
2209
+ width = self ._convert_dz (self .xaxis , x0 , x , width )
2210
+ if xerr is not None :
2211
+ xerr = self ._convert_dz (self .xaxis , x0 , x , xerr )
2212
+ y0 = y
2213
+ y = self .convert_xunits (y )
2214
+ height = self ._convert_dz (self .yaxis , y0 , y , height )
2215
+ if yerr is not None :
2216
+ yerr = self ._convert_dz (self .yaxis , y0 , y , yerr )
2241
2217
2242
2218
x , height , width , y , linewidth = np .broadcast_arrays (
2243
2219
# Make args iterable too.
@@ -2376,8 +2352,9 @@ def barh(self, y, width, height=0.8, left=None, *, align="center",
2376
2352
width : scalar or array-like
2377
2353
The width(s) of the bars.
2378
2354
2379
- height : sequence of scalars, optional, default: 0.8
2380
- The heights of the bars.
2355
+ height : sequence of scalars, optional
2356
+ The heights of the bars (default: 0.8, but see note below if *y*
2357
+ has units).
2381
2358
2382
2359
left : sequence of scalars
2383
2360
The x coordinates of the left sides of the bars (default: 0).
@@ -2452,14 +2429,19 @@ def barh(self, y, width, height=0.8, left=None, *, align="center",
2452
2429
*xerr*, and *yerr* can be either scalars or sequences of
2453
2430
length equal to the number of bars. This enables you to use
2454
2431
bar as the basis for stacked bar charts, or candlestick plots.
2455
- Detail: *xerr* and *yerr* are passed directly to
2456
- :meth:`errorbar`, so they can also have shape 2xN for
2457
- independent specification of lower and upper errors.
2432
+
2433
+ If *y* has units, then *height* should normally also be specified with
2434
+ units (so that they can be meaningfully added). However, if a unitless
2435
+ *height* is used (in particular, for the default *height*), then it is
2436
+ interpreted as a fraction of the average inter-bar spacing. For
2437
+ example, if the bars are equally spaced, then a *height* of 1 makes the
2438
+ bars contiguous. As a special case, if there is a *single* bar, then
2439
+ a unitless *height* is interpreted as a fraction of the axis (current)
2440
+ view span.
2458
2441
2459
2442
Other optional kwargs:
2460
2443
2461
2444
%(Rectangle)s
2462
-
2463
2445
"""
2464
2446
kwargs .setdefault ('orientation' , 'horizontal' )
2465
2447
patches = self .bar (x = left , height = height , width = width , bottom = y ,
@@ -2535,9 +2517,8 @@ def broken_barh(self, xranges, yrange, **kwargs):
2535
2517
'with two elements (i.e. an Nx2 array)' )
2536
2518
# convert the absolute values, not the x and dx...
2537
2519
x_conv = np .asarray (self .convert_xunits (xr [0 ]))
2538
- x1 = self ._convert_dx ( xr [ 1 ] , xr [0 ], x_conv , self . convert_xunits )
2520
+ x1 = self ._convert_dz ( self . xaxis , xr [0 ], x_conv , xr [ 1 ] )
2539
2521
xranges_conv .append ((x_conv , x1 ))
2540
-
2541
2522
yrange_conv = self .convert_yunits (yrange )
2542
2523
2543
2524
col = mcoll .BrokenBarHCollection (xranges_conv , yrange_conv , ** kwargs )
0 commit comments