Skip to content

Commit 1dfdc29

Browse files
committed
FIX: brokenbarh math before units
TST: Add test of broken_barh FIX: make bar work with timedeltas TST: check bar and barh work with timedelta DOC: fix private method docstring FIX: revert list comprehension TST: fix test FIX: throw error xr not tw-tuple
1 parent af8a720 commit 1dfdc29

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

lib/matplotlib/axes/_axes.py

+43-12
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,26 @@ def step(self, x, y, *args, where='pre', data=None, **kwargs):
20112011
kwargs['drawstyle'] = 'steps-' + where
20122012
return self.plot(x, y, *args, data=data, **kwargs)
20132013

2014+
@staticmethod
2015+
def _convert_dx(dx, x0, x, convert):
2016+
"""
2017+
Small helper to do logic of width conversion flexibly.
2018+
2019+
*dx* and *x0* have units, but *x* has already been converted
2020+
to unitless. This allows the *dx* to have units that are
2021+
different from *x0*, but are still accepted by the ``__add__``
2022+
operator of *x0*.
2023+
"""
2024+
try:
2025+
# attempt to add the width to x0; this works for
2026+
# datetime+timedelta, for instance
2027+
dx = convert(x0 + dx) - x
2028+
except (TypeError, AttributeError):
2029+
# but doesn't work for 'string' + float, so just
2030+
# see if the converter works on the float.
2031+
dx = convert(dx)
2032+
return dx
2033+
20142034
@_preprocess_data()
20152035
@docstring.dedent_interpd
20162036
def bar(self, x, height, width=0.8, bottom=None, *, align="center",
@@ -2172,23 +2192,25 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
21722192
else:
21732193
raise ValueError('invalid orientation: %s' % orientation)
21742194

2195+
x, height, width, y, linewidth = np.broadcast_arrays(
2196+
# Make args iterable too.
2197+
np.atleast_1d(x), height, width, y, linewidth)
2198+
21752199
# lets do some conversions now since some types cannot be
21762200
# subtracted uniformly
21772201
if self.xaxis is not None:
2202+
x0 = x
21782203
x = self.convert_xunits(x)
2179-
width = self.convert_xunits(width)
2204+
width = self._convert_dx(width, x0, x, self.convert_xunits)
21802205
if xerr is not None:
2181-
xerr = self.convert_xunits(xerr)
2206+
xerr = self._convert_dx(xerr, x0, x, self.convert_xunits)
21822207

21832208
if self.yaxis is not None:
2209+
y0 = y
21842210
y = self.convert_yunits(y)
2185-
height = self.convert_yunits(height)
2211+
height = self._convert_dx(height, y0, y, self.convert_yunits)
21862212
if yerr is not None:
2187-
yerr = self.convert_yunits(yerr)
2188-
2189-
x, height, width, y, linewidth = np.broadcast_arrays(
2190-
# Make args iterable too.
2191-
np.atleast_1d(x), height, width, y, linewidth)
2213+
yerr = self._convert_dx(yerr, y0, y, self.convert_yunits)
21922214

21932215
# Now that units have been converted, set the tick locations.
21942216
if orientation == 'vertical':
@@ -2465,10 +2487,19 @@ def broken_barh(self, xranges, yrange, **kwargs):
24652487
self._process_unit_info(xdata=xdata,
24662488
ydata=ydata,
24672489
kwargs=kwargs)
2468-
xranges = self.convert_xunits(xranges)
2469-
yrange = self.convert_yunits(yrange)
2470-
2471-
col = mcoll.BrokenBarHCollection(xranges, yrange, **kwargs)
2490+
xranges_conv = []
2491+
for xr in xranges:
2492+
if len(xr) != 2:
2493+
raise ValueError('each range in xrange must be a sequence '
2494+
'with two elements (i.e. an Nx2 array)')
2495+
# convert the absolute values, not the x and dx...
2496+
x_conv = self.convert_xunits(xr[0])
2497+
x1 = self._convert_dx(xr[1], xr[0], x_conv, self.convert_xunits)
2498+
xranges_conv.append((x_conv, x1))
2499+
2500+
yrange_conv = self.convert_yunits(yrange)
2501+
2502+
col = mcoll.BrokenBarHCollection(xranges_conv, yrange_conv, **kwargs)
24722503
self.add_collection(col, autolim=True)
24732504
self.autoscale_view()
24742505

lib/matplotlib/tests/test_axes.py

+35
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,32 @@ def test_barh_tick_label():
14981498
align='center')
14991499

15001500

1501+
def test_bar_timedelta():
1502+
"""smoketest that bar can handle width and height in delta units"""
1503+
fig, ax = plt.subplots()
1504+
ax.bar(datetime.datetime(2018, 1, 1), 1.,
1505+
width=datetime.timedelta(hours=3))
1506+
ax.bar(datetime.datetime(2018, 1, 1), 1.,
1507+
xerr=datetime.timedelta(hours=2),
1508+
width=datetime.timedelta(hours=3))
1509+
fig, ax = plt.subplots()
1510+
ax.barh(datetime.datetime(2018, 1, 1), 1,
1511+
height=datetime.timedelta(hours=3))
1512+
ax.barh(datetime.datetime(2018, 1, 1), 1,
1513+
height=datetime.timedelta(hours=3),
1514+
yerr=datetime.timedelta(hours=2))
1515+
fig, ax = plt.subplots()
1516+
ax.barh([datetime.datetime(2018, 1, 1), datetime.datetime(2018, 1, 1)],
1517+
np.array([1, 1.5]),
1518+
height=datetime.timedelta(hours=3))
1519+
ax.barh([datetime.datetime(2018, 1, 1), datetime.datetime(2018, 1, 1)],
1520+
np.array([1, 1.5]),
1521+
height=[datetime.timedelta(hours=t) for t in [1, 2]])
1522+
ax.broken_barh([(datetime.datetime(2018, 1, 1),
1523+
datetime.timedelta(hours=1))],
1524+
(10, 20))
1525+
1526+
15011527
@image_comparison(baseline_images=['hist_log'],
15021528
remove_text=True)
15031529
def test_hist_log():
@@ -5433,6 +5459,15 @@ def test_broken_barh_empty():
54335459
ax.broken_barh([], (.1, .5))
54345460

54355461

5462+
def test_broken_barh_timedelta():
5463+
"""Check that timedelta works as x, dx pair for this method """
5464+
fig, ax = plt.subplots()
5465+
pp = ax.broken_barh([(datetime.datetime(2018, 11, 9, 0, 0, 0),
5466+
datetime.timedelta(hours=1))], [1, 2])
5467+
assert pp.get_paths()[0].vertices[0, 0] == 737007.0
5468+
assert pp.get_paths()[0].vertices[2, 0] == 737007.0 + 1 / 24
5469+
5470+
54365471
def test_pandas_pcolormesh(pd):
54375472
time = pd.date_range('2000-01-01', periods=10)
54385473
depth = np.arange(20)

0 commit comments

Comments
 (0)