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

Skip to content

[Bug]: Labels don't get wrapped when set_yticks() is used in subplots #28358

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

Closed
soogui opened this issue Jun 7, 2024 · 8 comments · Fixed by #28377 or #28401
Closed

[Bug]: Labels don't get wrapped when set_yticks() is used in subplots #28358

soogui opened this issue Jun 7, 2024 · 8 comments · Fixed by #28377 or #28401

Comments

@soogui
Copy link

soogui commented Jun 7, 2024

Bug summary

When plotting bar charts in subplots with very long labels, the option of wrapping text only works on the first plotted subplot, despite passing wrap=True to set_yticks() in both cases.

Code for reproduction

import matplotlib.pyplot as plt
import numpy as np

long_text_label = 'very long category label i want to wrap'
labels = [f"{long_text_label}_{i}" for i in range(5)]
values = np.arange(1, 6)

fig, axes = plt.subplots(1, 2)

axes[0].barh(np.arange(len(labels)), values)
axes[0].set_yticks(np.arange(len(labels)), labels=labels, wrap=True)

axes[1].barh(np.arange(len(labels)), values)
axes[1].set_yticks(np.arange(len(labels)), labels=labels, wrap=True)

Actual outcome

output

Expected outcome

The label text on the y axis should appear wrapped on both subplots

Additional information

No response

Operating system

macOS 14.4 (23E214)

Matplotlib Version

3.8.3

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

Python 3.11.8

Jupyter version

4.2.0

Installation

pip

@rcomer
Copy link
Member

rcomer commented Jun 9, 2024

Thanks for the clear report @soogui. I confirm that I have reproduced this with our main development branch.

@jklymak
Copy link
Member

jklymak commented Jun 9, 2024

I'm not clear what the correct behaviour is here though. Currently it's wrapping on the edge of the figure. What would the inner axes wrap on? The axes to the left presumably, but that isn't conceptually straight forward as an axes knows about its figure but not about its neighbours, and it knows its own spine position but doesn't try to reserve space for itself outside those spines. Note we usually do the opposite and make the axes further apart to accommodate the ytick labels.

@timhoffm
Copy link
Member

For the current architecture, this is the expected behavior. We should better document what warp can or can't do.

I see two possible ways to improve:

  • hard: Assign each axes a bounding box. This should work well with subplots/subplot_mosaic, but I'm unclear how other axes creation methods would handle this. Up to now, the Axes is defined via the data area, ticks and labels just spill outside as far as they need.
  • medium: Expand the wrap functionality to allow wrapping after N characters and/or specifying a maximal width.

@jklymak
Copy link
Member

jklymak commented Jun 11, 2024

I think the conceptually simplest is to not allow auto wrap for tick labels. Folks can manually wrap if they need to.

@timhoffm
Copy link
Member

timhoffm commented Jun 11, 2024

Well, the case of one subplot with long tick labels works. IMHO we should not break that.

Just documenting that wrapping is limited to the figure boundary is good enough to manage expectations on the current behavior.

timhoffm added a commit to timhoffm/matplotlib that referenced this issue Jun 12, 2024
Closes matplotlib#28358 by documenting the effect of wrap.
@timhoffm
Copy link
Member

timhoffm commented Jun 12, 2024

@jklymak When #28177 is in, we could switch the wrapping box (in Text._get_wrap_line_width) to subfigure instead of figure. IMHO this boundary makes more sense. And it would at least allow to get what the OP wants using subfigures:

import matplotlib.pyplot as plt
import numpy as np

long_text_label = 'very long category label i want to wrap'
labels = [f"{long_text_label}_{i}" for i in range(5)]
values = np.arange(1, 6)

fig = plt.figure()
subfigs = fig.subfigures(1, 2)
ax0 = subfigs[0].subplots()
ax1 = subfigs[1].subplots()

ax0.barh(np.arange(len(labels)), values)
ax0.set_yticks(np.arange(len(labels)), labels=labels, wrap=True)

ax1.barh(np.arange(len(labels)), values)
ax1.set_yticks(np.arange(len(labels)), labels=labels, wrap=True)

--> Created a separate issue for this #28378.

@jklymak
Copy link
Member

jklymak commented Jun 12, 2024

That's likely fine. However it should be noted that even in the single subplot situation the wrap is incompatible with layout management since that adjusts the size of the axes to account for the size of the labels versus wrapping the labels.

As stated I am mildly opposed to us jumping through hoops to allow wrapping tick labels because I don't think it s a generally useful thing to do. However if the issues with wrapping tick labels also extends to other text boxes in subfigures, that might merit some effort to fix.

@QuLogic QuLogic added this to the v3.9.1 milestone Jun 14, 2024
timhoffm added a commit to timhoffm/matplotlib that referenced this issue Jun 15, 2024
`_get_dist_to_box()` assumed that the figure box (x0, y0) coordinates
are 0, which was correct at the time of writing, but does not hold
anymore since the introduction of subfigures.

Closes matplotlib#28378
Closes matplotlib#28358
timhoffm added a commit to timhoffm/matplotlib that referenced this issue Jun 15, 2024
`_get_dist_to_box()` assumed that the figure box (x0, y0) coordinates
are 0, which was correct at the time of writing, but does not hold
anymore since the introduction of subfigures.

Closes matplotlib#28378
Closes matplotlib#28358
@timhoffm
Copy link
Member

When #28401 is in, one can write the original plot using subfigures:

import matplotlib.pyplot as plt
import numpy as np

long_text_label = 'very long category label i want to wrap'
labels = [f"{long_text_label}_{i}" for i in range(5)]
values = np.arange(1, 6)

fig = plt.figure(layout="constrained")
fig1, fig2 = fig.subfigures(1, 2)
ax1 = fig1.add_subplot()
ax2 = fig2.add_subplot()


ax1.barh(np.arange(len(labels)), values)
ax1.set_yticks(np.arange(len(labels)), labels=labels, wrap=True)

ax2.barh(np.arange(len(labels)), values)
ax2.set_yticks(np.arange(len(labels)), labels=labels, wrap=True)
plt.show()

grafik

timhoffm added a commit to timhoffm/matplotlib that referenced this issue Jun 18, 2024
`_get_dist_to_box()` assumed that the figure box (x0, y0) coordinates
are 0, which was correct at the time of writing, but does not hold
anymore since the introduction of subfigures.

Closes matplotlib#28378
Closes matplotlib#28358
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants