Skip to content

Commit ab4d674

Browse files
committed
Add negpos support for bar plots
1 parent 72e11a5 commit ab4d674

1 file changed

Lines changed: 42 additions & 21 deletions

File tree

proplot/axes/plot.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,8 @@ def hist_wrapper(self, func, x, bins=None, **kwargs):
13771377
def bar_wrapper(
13781378
self, func, x=None, height=None, width=0.8, bottom=None, *, left=None,
13791379
vert=None, orientation='vertical', stacked=False,
1380-
lw=None, linewidth=0.7, edgecolor='k',
1380+
lw=None, linewidth=0.7, edgecolor='black',
1381+
negpos=False, negcolor=None, poscolor=None,
13811382
**kwargs
13821383
):
13831384
"""
@@ -1396,49 +1397,69 @@ def bar_wrapper(
13961397
# TODO: Stacked feature is implemented in `cycle_changer`, but makes more
13971398
# sense do document here; figure out way to move it here?
13981399
if left is not None:
1399-
warnings._warn_proplot(
1400-
'The "left" keyword with bar() is deprecated. Use "x" instead.'
1401-
)
1400+
warnings._warn_proplot('bar() keyword "left" is deprecated. Use "x" instead.')
14021401
x = left
14031402
if x is None and height is None:
1404-
raise ValueError(
1405-
'bar() requires at least 1 positional argument, got 0.'
1406-
)
1403+
raise ValueError('bar() requires at least 1 positional argument, got 0.')
14071404
elif height is None:
14081405
x, height = None, x
1406+
args = (x, height)
1407+
linewidth = _not_none(lw=lw, linewidth=linewidth)
1408+
kwargs.update({
1409+
'width': width, 'bottom': bottom, 'stacked': stacked,
1410+
'orientation': orientation, 'linewidth': linewidth, 'edgecolor': edgecolor,
1411+
})
14091412

14101413
# Call func
1411-
# TODO: This *must* also be wrapped by cycle_changer, which ultimately
1414+
# NOTE: This *must* also be wrapped by cycle_changer, which ultimately
14121415
# permutes back the x/bottom args for horizontal bars! Need to clean up.
1413-
lw = _not_none(lw=lw, linewidth=linewidth)
1414-
return func(
1415-
self, x, height, width=width, bottom=bottom,
1416-
linewidth=lw, edgecolor=edgecolor,
1417-
stacked=stacked, orientation=orientation,
1418-
**kwargs
1419-
)
1416+
if negpos:
1417+
# Draw negative and positive bars
1418+
# NOTE: cycle_changer makes bar widths *relative* to step size between
1419+
# x coordinates to cannot just omit data. Instead make some height nan.
1420+
message = 'bar() argument {}={!r} is incompatible with negpos=True. Ignoring.'
1421+
stacked = kwargs.pop('stacked', None)
1422+
if stacked:
1423+
warnings._warn_proplot(message.format('stacked', stacked))
1424+
height = np.asarray(height)
1425+
if height.ndim > 1:
1426+
raise ValueError('bar() heights with negpos=True must be 1D.')
1427+
height1 = height.copy().astype(np.float64)
1428+
height1[height >= 0] = np.nan
1429+
height2 = height.copy().astype(np.float64)
1430+
height2[height < 0] = np.nan
1431+
negcolor = _not_none(negcolor, rc['negcolor'])
1432+
poscolor = _not_none(poscolor, rc['poscolor'])
1433+
obj1 = func(self, x, height1, **{'color': negcolor, **kwargs})
1434+
obj2 = func(self, x, height2, **{'color': poscolor, **kwargs})
1435+
result = (obj1, obj2)
1436+
else:
1437+
# Draw simple bars
1438+
result = func(self, *args, **kwargs)
1439+
return result
14201440

14211441

1422-
@docstring.add_snippets # noqa: U100
1442+
@docstring.add_snippets
14231443
def barh_wrapper(
1424-
self, func, y=None, width=None, height=0.8, left=None, **kwargs
1444+
self, func, y=None, right=None, width=None, left=None, height=None, **kwargs
14251445
):
14261446
"""
14271447
%(axes.barh)s
14281448
"""
14291449
# Converts y-->bottom, left-->x, width-->height, height-->width.
14301450
# Convert back to (x, bottom, width, height) so we can pass stuff
14311451
# through cycle_changer.
1452+
# NOTE: ProPlot calls second positional argument 'right' so that 'width'
1453+
# means the width of *bars*.
14321454
# NOTE: You *must* do juggling of barh keyword order --> bar keyword order
14331455
# --> barh keyword order, because horizontal hist passes arguments to bar
14341456
# directly and will not use a 'barh' method with overridden argument order!
14351457
func # avoid U100 error
1458+
height = _not_none(height=height, width=width, default=0.8)
14361459
kwargs.setdefault('orientation', 'horizontal')
14371460
if y is None and width is None:
1438-
raise ValueError(
1439-
'barh() requires at least 1 positional argument, got 0.'
1440-
)
1441-
return self.bar(x=left, height=height, width=width, bottom=y, **kwargs)
1461+
raise ValueError('barh() requires at least 1 positional argument, got 0.')
1462+
return self.bar(x=left, width=right, height=height, bottom=y, **kwargs)
14421463

14431464

14441465
def boxplot_wrapper(

0 commit comments

Comments
 (0)