Skip to content

[Bug]: clabels with logarithmic axes #27081

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

Open
Jean1995 opened this issue Oct 13, 2023 · 7 comments
Open

[Bug]: clabels with logarithmic axes #27081

Jean1995 opened this issue Oct 13, 2023 · 7 comments
Labels
API: default changes Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues topic: contour

Comments

@Jean1995
Copy link

Jean1995 commented Oct 13, 2023

Bug summary

I am using clabels. However, when introducing logarithmic axes, the inline placement becomes inaccurate.

Code for reproduction

import matplotlib.pyplot as plt
import numpy as np

delta = 0.025
x = np.geomspace(1e-2, 1e3, 40)
y = np.geomspace(1e-2, 1e3, 40)
X, Y = np.meshgrid(x, y)
Z = np.log(X) + np.log(Y)

fig, ax = plt.subplots(nrows=1, figsize=(3, 3))


CS = ax.contour(X, Y, Z)
ax.clabel(CS, inline=1, fontsize=10)
ax.set_xscale('log')
ax.set_yscale('log')

Actual outcome

example

Expected outcome

What is quite interesting: If I create several subplots with an identical content, using shared x and y axes, all but the first plot will show the correct behaviour.

This code:

fig, ax = plt.subplots(nrows=1, ncols=3, sharey=True, sharex=True, figsize=(10, 3.2))

CS = ax[0].contour(X, Y, Z)
ax[0].clabel(CS, inline=1, fontsize=10)
ax[0].set_xscale('log')
ax[0].set_yscale('log')

CS = ax[1].contour(X, Y, Z)
ax[1].clabel(CS, inline=1, fontsize=10)
ax[1].set_xscale('log')
ax[1].set_yscale('log')

CS = ax[2].contour(X, Y, Z)
ax[2].clabel(CS, inline=1, fontsize=10)
ax[2].set_xscale('log')
ax[2].set_yscale('log')

with the X, Y, Z values from above, lead to this plot:

working

Note that this only provides the correct results for sharex=True and sharey=True, if one (or both) are set to False, results are invalid.

Additional information

No response

Operating system

Linux and MacOS

Matplotlib Version

3.7.3

Matplotlib Backend

QtAgg

Python version

3.8.10

Jupyter version

6.0.3

Installation

pip

@rcomer
Copy link
Member

rcomer commented Oct 13, 2023

I can reproduce this with v3.8.0, but if I put the clabel call after the setting the log scales, it looks right:

test

So I think the reason the second and third plots in the shared axis figure look right is because they already have the log scale when the contour labels are calculated.

@Jean1995
Copy link
Author

I can reproduce this with v3.8.0, but if I put the clabel call after the setting the log scales, it looks right:

test

So I think the reason the second and third plots in the shared axis figure look right is because they already have the log scale when the contour labels are calculated.

Oh my! That is an embarrassingly simple solution to my problem! Thanks a lot! 😄

Does this still count as a bug so I keep the issue open, or is this expected behaviour?

@rcomer
Copy link
Member

rcomer commented Oct 13, 2023

I think making the original example work would require calculating the labels (and modifying the contours) at draw time rather than when clabel is called. Since the draw machinery is often called multiple times for a figure, that could potentially be inefficient.

It might be better to wait for a more experienced developer to weigh in though.

@jklymak
Copy link
Member

jklymak commented Oct 13, 2023

I think @rcomer is correct. In general it is safest to assume things are "drawn" when you call them. In addition to very substantial inefficiencies, it would require a whole lot of machinery to carry around the clabel info until draw time.

@anntzer
Copy link
Contributor

anntzer commented Oct 13, 2023

Per #26971 (comment) I think the proper solution is to add support for knockout groups in Matplotlib's rendering model (as described in the linked comment). Otherwise implementation would be quite complex.

@anntzer
Copy link
Contributor

anntzer commented Oct 17, 2023

One thing that would help in the case here, though, is to pass use_clabeltext=True -- at least the labels rotation is correct, even though the space removed around them is still wrong.
I actually think use_clabeltext=True should be the made default (it basically recomputes text rotations at draw time based on the actual scales and limits used) and the whole option deprecated as I'm not sure there's a good reason to use the =False case (except perhaps for a small gain in performance, but I doubt it really matters). I guess the option only exists because that functionality was added "later" (in 2009) and no one wanted to change the default back then.
Changing the default use_clabeltext should be relatively easy (except for the usual backcompat dance); adding support for KO groups is of course a much bigger endeavor.

@anntzer anntzer added API: default changes Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues topic: contour labels Oct 17, 2023
@anntzer
Copy link
Contributor

anntzer commented Aug 1, 2024

Looking at it again, a possible fix would be to only do the linebreaking temporarily in draw(), i.e. add_label() would just register that a label needs to be added at a given position, but all calculations would be deferred until the very latest point, where the correct transforms are known.
Also related to #28631.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API: default changes Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues topic: contour
Projects
None yet
Development

No branches or pull requests

4 participants