Below, I've reworked bar_label to do something similar to what you are asking, considering asymmetric yerrors.
Edit '%d[%d,%d]' % (value,err[0,1],err[1,1])
in the end of the code to show errors in whatever format you want. I.e., use '%.2f$\pm$%.2f' % (value,err[0,1])
to show symmetric errors. xerrors can also be easily taken into account by adapting err matrix indexing.
import itertools
def bar_label(container, labels=None, *, fmt="%g", label_type="edge",
padding=0, **kwargs):
# want to know whether to put label on positive or negative direction
# cannot use np.sign here because it will return 0 if x == 0
def sign(x):
return 1 if x >= 0 else -1
bars = container.patches
errorbar = container.errorbar
datavalues = container.datavalues
orientation = container.orientation
if errorbar:
# check "ErrorbarContainer" for the definition of these elements
lines = errorbar.lines # attribute of "ErrorbarContainer" (tuple)
barlinecols = lines[2] # 0: data_line, 1: caplines, 2: barlinecols
barlinecol = barlinecols[0] # the "LineCollection" of error bars
errs = barlinecol.get_segments()
else:
errs = []
if labels is None:
labels = []
annotations = []
for bar, err, dat, lbl in itertools.zip_longest(
bars, errs, datavalues, labels
):
(x0, y0), (x1, y1) = bar.get_bbox().get_points()
xc, yc = (x0 + x1) / 2, (y0 + y1) / 2
if orientation == "vertical":
extrema = max(y0, y1) if dat >= 0 else min(y0, y1)
length = abs(y0 - y1)
elif orientation == "horizontal":
extrema = max(x0, x1) if dat >= 0 else min(x0, x1)
length = abs(x0 - x1)
if err is None:
endpt = extrema
elif orientation == "vertical":
endpt = err[:, 1].max() if dat >= 0 else err[:, 1].min()
elif orientation == "horizontal":
endpt = err[:, 0].max() if dat >= 0 else err[:, 0].min()
if label_type == "center":
value = sign(dat) * length
elif label_type == "edge":
value = extrema
if label_type == "center":
xy = xc, yc
elif label_type == "edge" and orientation == "vertical":
xy = xc, endpt
elif label_type == "edge" and orientation == "horizontal":
xy = endpt, yc
if orientation == "vertical":
xytext = 0, sign(dat) * padding
else:
xytext = sign(dat) * padding, 0
if label_type == "center":
ha, va = "center", "center"
elif label_type == "edge":
if orientation == "vertical":
ha = 'center'
va = 'top' if dat < 0 else 'bottom' # also handles NaN
elif orientation == "horizontal":
ha = 'right' if dat < 0 else 'left' # also handles NaN
va = 'center'
if np.isnan(dat):
lbl = ''
annotation = plt.annotate('%d[%d,%d]' % (value,err[0,1],err[1,1]), #fmt % value if lbl is None else lbl,
xy, xytext, textcoords="offset points",
ha=ha, va=va, **kwargs)
annotations.append(annotation)
return annotations