Skip to content
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

[Bug]: LogLocator with subs argument fragile. #24092

Open
peterdsharpe opened this issue Oct 4, 2022 · 5 comments
Open

[Bug]: LogLocator with subs argument fragile. #24092

peterdsharpe opened this issue Oct 4, 2022 · 5 comments
Labels
Difficulty: Hard Good first issue Open a pull request against these issues if there are no active ones! topic: ticks axis labels
Milestone

Comments

@peterdsharpe
Copy link

peterdsharpe commented Oct 4, 2022

Bug summary

Logarithmic tick markers do not appear if the y-axis scales a sufficient range, with the subs keyword argument of ticker.LogLocator set to non-trivial values.

Code for reproduction

import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np

ll = matplotlib.ticker.LogLocator(subs=(1, 2, 5))

### The following code produces a plot with y-axis ticks at the expected locations.

fig, ax = plt.subplots()
x = np.arange(8)
plt.semilogy(x, 10 ** x)
ax.yaxis.set_major_locator(ll)
ax.yaxis.set_minor_locator(ll)
plt.title("Good Plot")
plt.show()

### The following code produces a plot with no y-axis ticks, which is unexpected and undesired.

fig, ax = plt.subplots()
x = np.arange(9)  # The only change is this line
plt.semilogy(x, 10 ** x)
ax.yaxis.set_major_locator(ll)
ax.yaxis.set_minor_locator(ll)
plt.title("Bad Plot")
plt.show()

### The problem is isolated to here, which returns correct values in the first case, but np.array([]) in the second case:
print(ll.tick_values(1, 1e7))
print(ll.tick_values(1, 1e8))

Actual outcome

image

image

Expected outcome

I expect to see ticks in both cases, as appears in the "Good Plot".

Additional information

The problem is isolated to ticker.LogLocator.tick_values(). This returns correct values in the first case (e.g., np.array([1.e-01 2.e-01 5.e-01 1.e+00 2.e+00 5.e+00 1.e+01 2.e+01 5.e+01 1.e+02 2.e+02 5.e+02])), but np.array([]) in the second case.

Operating system

Windows

Matplotlib Version

3.5.2

Matplotlib Backend

module://backend_interagg

Python version

3.9.13

Jupyter version

No response

Installation

conda

@jklymak jklymak changed the title [Bug]: [Bug]: LogLocator with subs argument fragile. Oct 4, 2022
@peterdsharpe
Copy link
Author

peterdsharpe commented Oct 13, 2022

Hi all, any thoughts on this? This is causing breaking behavior in a downstream application, and it'd be helpful to know whether Matplotlib maintainers think a fix will be quick, or if I should invest resources in working around this.

@jklymak
Copy link
Member

jklymak commented Oct 13, 2022

I think we would accept a fix if one were forthcoming. I suspect the is in the range where we tick every two decades and that is clashing with subs.

@tacaswell tacaswell added Good first issue Open a pull request against these issues if there are no active ones! Difficulty: Hard labels Oct 13, 2022
@tacaswell
Copy link
Member

tacaswell commented Oct 13, 2022

If you pan the "bad" example up or down you can make the ticks show up. They seem to alternate in and out based on the limits (not just the range) if you zoom out further they never appear.

There is clearly something very wrong in the logic of what sub does, but looking at the code I can not quickly understand it...

I'm labeling this as "good first issue" as I think the bug is clear (we should never not have tick labels!) but "hard" because the logic in the tick_values method is a bit convoluted (for a bunch of reasons, some historical, some because we are using the same code for major and minor ticks, some because people have very strong views about what log tick "should" be that are very conditional on the values involved). Any changes will have to be very careful about unintended consequences and understanding why the code was the way it was (likely will require some git/github archaeology) and adding a bunch more tests.

@tacaswell tacaswell added this to the v3.7.0 milestone Oct 13, 2022
@tacaswell
Copy link
Member

tacaswell commented Oct 13, 2022

Also milestoning for 3.7 as we need to fix this, but I doubt (but we should check) that this is a regression in 3.6 and expect the fix to be somewhat high-risk so we should not backport it. If I am wrong about either of those, then we can re-milestone and backport.

@Hannibal404
Copy link

Hannibal404 commented Oct 20, 2022

In ticker.py, as the difference between vmin and vmax increases, numdec increases and thus makes stride to be greater than 1 (in the else condition).

        stride = (max(math.ceil(numdec / (numticks - 1)), 1)
              if mpl.rcParams['_internal.classic_mode'] else
              (numdec + 1) // numticks + 1)

In the case of stride > 1, ticklocs gets assigned a blank array which I believe to be the root of the problem.

    if hasattr(self, '_transform'):
        ticklocs = self._transform.inverted().transform(decades)
        if have_subs:
            if stride == 1:
                ticklocs = np.ravel(np.outer(subs, ticklocs))
            else:
                # No ticklocs if we have >1 decade between major ticks.
                ticklocs = np.array([])

The ticks appearing upon panning may be explained by numticks increasing due to staggering and thus stride being reduced to 1 again, as upon plotting the bad plot for x = np.arange(10) even panning does not make the ticks appear.

Hope this helps. I would be glad to help fix this if you could guide me a little. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Hard Good first issue Open a pull request against these issues if there are no active ones! topic: ticks axis labels
Projects
None yet
Development

No branches or pull requests

4 participants