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

Skip to content

Improve bar_label annotation #22447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2598,13 +2598,25 @@ def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge",

**kwargs
Any remaining keyword arguments are passed through to
`.Axes.annotate`.
`.Axes.annotate`. The alignment parameters (
*horizontalalignment* / *ha*, *verticalalignment* / *va*) are
not supported because the labels are automatically aligned to
the bars.

Returns
-------
list of `.Text`
A list of `.Text` instances for the labels.
"""
for key in ['horizontalalignment', 'ha', 'verticalalignment', 'va']:
if key in kwargs:
Comment on lines +2611 to +2612
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might alternatively written as

Suggested change
for key in ['horizontalalignment', 'ha', 'verticalalignment', 'va']:
if key in kwargs:
unsupported = set(['horizontalalignment', 'ha', 'verticalalignment', 'va']):
for key in unsupported.intersection(kwargs):

But no strong preference. I'll leave this up to you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be:

unsupported = {'horizontalalignment', 'ha', 'verticalalignment', 'va'}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged as is since the existing code style is good enough.

raise ValueError(
f"Passing {key!r} to bar_label() is not supported.")

a, b = self.yaxis.get_view_interval()
y_inverted = a > b
c, d = self.xaxis.get_view_interval()
x_inverted = c > d

# 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
Expand Down Expand Up @@ -2633,7 +2645,7 @@ def sign(x):
annotations = []

for bar, err, dat, lbl in itertools.zip_longest(
bars, errs, datavalues, labels
bars, errs, datavalues, labels
):
(x0, y0), (x1, y1) = bar.get_bbox().get_points()
xc, yc = (x0 + x1) / 2, (y0 + y1) / 2
Expand Down Expand Up @@ -2665,18 +2677,26 @@ def sign(x):
xy = endpt, yc

if orientation == "vertical":
xytext = 0, sign(dat) * padding
y_direction = -1 if y_inverted else 1
xytext = 0, y_direction * sign(dat) * padding
else:
xytext = sign(dat) * padding, 0
x_direction = -1 if x_inverted else 1
xytext = x_direction * 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
if y_inverted:
va = 'top' if dat > 0 else 'bottom' # also handles NaN
else:
va = 'top' if dat < 0 else 'bottom' # also handles NaN
Comment on lines +2691 to +2694
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely certain this is correct, because there are no tests with NaN on an inverted axis.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Valdes-Tresanco-MS di this get addressed? If so, please move back out of draft. Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a new test added, but the problem is that the result that is being checked is the same as what occurs when you run the code on main. So I'm not certain if the test is asserting that this PR changes things correctly (and the test just happens to match main coincidentally), or the test is asserting that this PR doesn't break how things currently work (and the match is intentional).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what I guess I'm saying is it would be better if the test were paramterized across all the 4 cases here.

elif orientation == "horizontal":
ha = 'right' if dat < 0 else 'left' # also handles NaN
if x_inverted:
ha = 'right' if dat > 0 else 'left' # also handles NaN
else:
ha = 'right' if dat < 0 else 'left' # also handles NaN
va = 'center'

if np.isnan(dat):
Expand Down
67 changes: 67 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7343,6 +7343,20 @@ def test_bar_label_location_vertical():
assert labels[1].get_va() == 'top'


def test_bar_label_location_vertical_yinverted():
ax = plt.gca()
ax.invert_yaxis()
xs, heights = [1, 2], [3, -4]
rects = ax.bar(xs, heights)
labels = ax.bar_label(rects)
assert labels[0].xy == (xs[0], heights[0])
assert labels[0].get_ha() == 'center'
assert labels[0].get_va() == 'top'
assert labels[1].xy == (xs[1], heights[1])
assert labels[1].get_ha() == 'center'
assert labels[1].get_va() == 'bottom'


def test_bar_label_location_horizontal():
ax = plt.gca()
ys, widths = [1, 2], [3, -4]
Expand All @@ -7356,6 +7370,49 @@ def test_bar_label_location_horizontal():
assert labels[1].get_va() == 'center'


def test_bar_label_location_horizontal_yinverted():
ax = plt.gca()
ax.invert_yaxis()
ys, widths = [1, 2], [3, -4]
rects = ax.barh(ys, widths)
labels = ax.bar_label(rects)
assert labels[0].xy == (widths[0], ys[0])
assert labels[0].get_ha() == 'left'
assert labels[0].get_va() == 'center'
assert labels[1].xy == (widths[1], ys[1])
assert labels[1].get_ha() == 'right'
assert labels[1].get_va() == 'center'


def test_bar_label_location_horizontal_xinverted():
ax = plt.gca()
ax.invert_xaxis()
ys, widths = [1, 2], [3, -4]
rects = ax.barh(ys, widths)
labels = ax.bar_label(rects)
assert labels[0].xy == (widths[0], ys[0])
assert labels[0].get_ha() == 'right'
assert labels[0].get_va() == 'center'
assert labels[1].xy == (widths[1], ys[1])
assert labels[1].get_ha() == 'left'
assert labels[1].get_va() == 'center'


def test_bar_label_location_horizontal_xyinverted():
ax = plt.gca()
ax.invert_xaxis()
ax.invert_yaxis()
ys, widths = [1, 2], [3, -4]
rects = ax.barh(ys, widths)
labels = ax.bar_label(rects)
assert labels[0].xy == (widths[0], ys[0])
assert labels[0].get_ha() == 'right'
assert labels[0].get_va() == 'center'
assert labels[1].xy == (widths[1], ys[1])
assert labels[1].get_ha() == 'left'
assert labels[1].get_va() == 'center'


def test_bar_label_location_center():
ax = plt.gca()
ys, widths = [1, 2], [3, -4]
Expand Down Expand Up @@ -7407,6 +7464,16 @@ def test_bar_label_nan_ydata():
assert labels[0].get_va() == 'bottom'


def test_bar_label_nan_ydata_inverted():
ax = plt.gca()
ax.yaxis_inverted()
bars = ax.bar([2, 3], [np.nan, 1])
labels = ax.bar_label(bars)
assert [l.get_text() for l in labels] == ['', '1']
assert labels[0].xy == (2, 0)
assert labels[0].get_va() == 'bottom'


def test_patch_bounds(): # PR 19078
fig, ax = plt.subplots()
ax.add_patch(mpatches.Wedge((0, -1), 1.05, 60, 120, 0.1))
Expand Down