Skip to content

Commit f730235

Browse files
brifore13timhoffm
andauthored
ENH: Add support for per-label padding in bar_label (#29696)
Implement support for array-like padding in bar_label. This allows users to specify different padding values for individual labels by passing an array-like object to the padding parameter. --------- Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
1 parent f1e7d3b commit f730235

File tree

5 files changed

+41
-7
lines changed

5 files changed

+41
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``bar_label`` supports individual padding per label
2+
---------------------------------------------------
3+
``bar_label`` will now accept both a float value or an array-like for
4+
padding. The array-like defines the padding for each label individually.

lib/matplotlib/axes/_axes.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -2803,8 +2803,11 @@ def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge",
28032803
(useful for stacked bars, i.e.,
28042804
:doc:`/gallery/lines_bars_and_markers/bar_label_demo`)
28052805
2806-
padding : float, default: 0
2806+
padding : float or array-like, default: 0
28072807
Distance of label from the end of the bar, in points.
2808+
If an array-like is provided, the padding values are applied
2809+
to each label individually. Must have the same length as container.
2810+
.. versionadded:: 3.11
28082811
28092812
**kwargs
28102813
Any remaining keyword arguments are passed through to
@@ -2854,8 +2857,18 @@ def sign(x):
28542857

28552858
annotations = []
28562859

2857-
for bar, err, dat, lbl in itertools.zip_longest(
2858-
bars, errs, datavalues, labels
2860+
if np.iterable(padding):
2861+
# if padding iterable, check length
2862+
padding = np.asarray(padding)
2863+
if len(padding) != len(bars):
2864+
raise ValueError(
2865+
f"padding must be of length {len(bars)} when passed as a sequence")
2866+
else:
2867+
# single value, apply to all labels
2868+
padding = [padding] * len(bars)
2869+
2870+
for bar, err, dat, lbl, pad in itertools.zip_longest(
2871+
bars, errs, datavalues, labels, padding
28592872
):
28602873
(x0, y0), (x1, y1) = bar.get_bbox().get_points()
28612874
xc, yc = (x0 + x1) / 2, (y0 + y1) / 2
@@ -2895,10 +2908,10 @@ def sign(x):
28952908

28962909
if orientation == "vertical":
28972910
y_direction = -1 if y_inverted else 1
2898-
xytext = 0, y_direction * sign(dat) * padding
2911+
xytext = 0, y_direction * sign(dat) * pad
28992912
else: # horizontal
29002913
x_direction = -1 if x_inverted else 1
2901-
xytext = x_direction * sign(dat) * padding, 0
2914+
xytext = x_direction * sign(dat) * pad, 0
29022915

29032916
if label_type == "center":
29042917
ha, va = "center", "center"

lib/matplotlib/axes/_axes.pyi

+1-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ class Axes(_AxesBase):
256256
*,
257257
fmt: str | Callable[[float], str] = ...,
258258
label_type: Literal["center", "edge"] = ...,
259-
padding: float = ...,
259+
padding: float | ArrayLike = ...,
260260
**kwargs
261261
) -> list[Annotation]: ...
262262
def broken_barh(

lib/matplotlib/pyplot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3027,7 +3027,7 @@ def bar_label(
30273027
*,
30283028
fmt: str | Callable[[float], str] = "%g",
30293029
label_type: Literal["center", "edge"] = "edge",
3030-
padding: float = 0,
3030+
padding: float | ArrayLike = 0,
30313031
**kwargs,
30323032
) -> list[Annotation]:
30333033
return gca().bar_label(

lib/matplotlib/tests/test_axes.py

+17
Original file line numberDiff line numberDiff line change
@@ -8816,6 +8816,23 @@ def test_bar_label_nan_ydata_inverted():
88168816
assert labels[0].get_verticalalignment() == 'bottom'
88178817

88188818

8819+
def test_bar_label_padding():
8820+
"""Test that bar_label accepts both float and array-like padding."""
8821+
ax = plt.gca()
8822+
xs, heights = [1, 2], [3, 4]
8823+
rects = ax.bar(xs, heights)
8824+
labels1 = ax.bar_label(rects, padding=5) # test float value
8825+
assert labels1[0].xyann[1] == 5
8826+
assert labels1[1].xyann[1] == 5
8827+
8828+
labels2 = ax.bar_label(rects, padding=[2, 8]) # test array-like values
8829+
assert labels2[0].xyann[1] == 2
8830+
assert labels2[1].xyann[1] == 8
8831+
8832+
with pytest.raises(ValueError, match="padding must be of length"):
8833+
ax.bar_label(rects, padding=[1, 2, 3])
8834+
8835+
88198836
def test_nan_barlabels():
88208837
fig, ax = plt.subplots()
88218838
bars = ax.bar([1, 2, 3], [np.nan, 1, 2], yerr=[0.2, 0.4, 0.6])

0 commit comments

Comments
 (0)