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

Skip to content

Commit a92d478

Browse files
committed
Interpret unitless bar widths for unitized data as relative to spacing.
1 parent c22847b commit a92d478

File tree

1 file changed

+54
-73
lines changed

1 file changed

+54
-73
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 54 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import matplotlib.ticker as mticker
3333
import matplotlib.transforms as mtransforms
3434
import matplotlib.tri as mtri
35+
import matplotlib.units as munits
3536
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer
3637
from matplotlib.axes._base import _AxesBase, _process_plot_format
3738

@@ -2012,56 +2013,27 @@ def step(self, x, y, *args, where='pre', data=None, **kwargs):
20122013
return self.plot(x, y, *args, data=data, **kwargs)
20132014

20142015
@staticmethod
2015-
def _convert_dx(dx, x0, xconv, convert):
2016+
def _convert_dz(axis, z, z_conv, dz):
20162017
"""
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.
20232021
"""
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
20652037

20662038
@_preprocess_data()
20672039
@docstring.dedent_interpd
@@ -2088,7 +2060,8 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
20882060
The height(s) of the bars.
20892061
20902062
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).
20922065
20932066
bottom : scalar or array-like, optional
20942067
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",
21662139
*xerr*, and *yerr* can be either scalars or sequences of
21672140
length equal to the number of bars. This enables you to use
21682141
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.
21722151
21732152
Other optional kwargs:
21742153
21752154
%(Rectangle)s
2176-
21772155
"""
21782156
kwargs = cbook.normalize_kwargs(kwargs, mpatches.Patch._alias_map)
21792157
color = kwargs.pop('color', None)
@@ -2226,18 +2204,16 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
22262204

22272205
# lets do some conversions now since some types cannot be
22282206
# 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)
22412217

22422218
x, height, width, y, linewidth = np.broadcast_arrays(
22432219
# Make args iterable too.
@@ -2376,8 +2352,9 @@ def barh(self, y, width, height=0.8, left=None, *, align="center",
23762352
width : scalar or array-like
23772353
The width(s) of the bars.
23782354
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).
23812358
23822359
left : sequence of scalars
23832360
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",
24522429
*xerr*, and *yerr* can be either scalars or sequences of
24532430
length equal to the number of bars. This enables you to use
24542431
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.
24582441
24592442
Other optional kwargs:
24602443
24612444
%(Rectangle)s
2462-
24632445
"""
24642446
kwargs.setdefault('orientation', 'horizontal')
24652447
patches = self.bar(x=left, height=height, width=width, bottom=y,
@@ -2535,9 +2517,8 @@ def broken_barh(self, xranges, yrange, **kwargs):
25352517
'with two elements (i.e. an Nx2 array)')
25362518
# convert the absolute values, not the x and dx...
25372519
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])
25392521
xranges_conv.append((x_conv, x1))
2540-
25412522
yrange_conv = self.convert_yunits(yrange)
25422523

25432524
col = mcoll.BrokenBarHCollection(xranges_conv, yrange_conv, **kwargs)

0 commit comments

Comments
 (0)