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]: axis set_xscale('log') interferes with set_xticks #27701

Closed
evanberkowitz opened this issue Jan 25, 2024 · 17 comments · Fixed by #27799
Closed

[Bug]: axis set_xscale('log') interferes with set_xticks #27701

evanberkowitz opened this issue Jan 25, 2024 · 17 comments · Fixed by #27799

Comments

@evanberkowitz
Copy link
Contributor

evanberkowitz commented Jan 25, 2024

Bug summary

Setting ax.set_xscale('log') introduces unwanted minor ticks, even when set_xticks specifies minor=False.

Code for reproduction

import matplotlib.pyplot as plt
import numpy as np

N = np.array([3, 5, 7, 9])
s = np.ones_like(N)

fig, ax = plt.subplots()
ax.plot(1/N, s)
ax.set_xlabel('1/N')
ax.set_xscale('log')
ax.set_xticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=False)
# ax.minorticks_off()

Actual outcome

A figure with a constant dataset, with xticks at 1/9, 1/7, 1/5, and 1/3, but ALSO at 3x10^-1, which is unwanted.
Screenshot 2024-01-25 at 13 12 11

What is that extra tick doing there?

Expected outcome

The figure that results from uncommenting the minorticks_off command in the example,

Screenshot 2024-01-25 at 13 13 12

Additional information

Happens with errorbar instead of plot. Haven't tested other ways to draw.

In the above example if you put the set_xscale after the set_xticks then the requested xticks are completely ignored.

If you comment out the log scale, the ticks are as expected (but of course the scale isn't what is desired).

Operating system

MacOS 14.2.1 (M2 from 2022)

Matplotlib Version

3.5.2

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

Python 3.11.7

Jupyter version

4.0.5

Installation

conda

@evanberkowitz
Copy link
Contributor Author

In fact it's not x-axis related, the same issue happens with the y-axis.

import matplotlib.pyplot as plt
import numpy as np

N = np.array([3, 5, 7, 9])
s = np.ones_like(N)

fig, ax = plt.subplots()
ax.plot(1/N, 1/N)
ax.set_xlabel('1/N')
ax.set_xscale('log')
ax.set_xticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=False)

ax.set_yscale('log')
ax.set_yticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=False)

# ax.minorticks_off()

Screenshot 2024-01-25 at 13 29 21

@ShivamPathak99
Copy link
Contributor

If you use minorticks_off() the minor ticks get disabled

import matplotlib.pyplot as plt
import numpy as np

N = np.array([3, 5, 7, 9])
s = np.ones_like(N)

fig, ax = plt.subplots()
ax.plot(1/N, s)
ax.set_xlabel('1/N')
ax.set_xscale('log')
ax.set_xticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=False) # Default Value: minor = False 
ax.minorticks_off()      # Using minorticks_off() 

But the question still arises regarding the meaning of minor = False

Here is the copy of documentation

image

Another thing I noticed, if you apply

- ax.set_xticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=False)
+ ax.set_xticks(1./N, labels=[f'1/{each_n}' for each_n in N], minor=True)

Then the minor ticks vanishes.

@evanberkowitz
Copy link
Contributor Author

Indeed; that's why I included the (commented) call to minorticks_off in my example.

I see. Either way it's strange to me that overriding the default ticks leaves the minor ticks alone.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

minor=False means the set_xticks works on the major ticks only. This seems to be working as expected?

@evanberkowitz
Copy link
Contributor Author

If you remove the minor=False from the example you still get the unwanted minor ticks.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

logscale uses a formatter that labels the minor ticks if there is one or fewer major ticks. This is not super clear in the docs, but the alternative is a plot with no ticks if the user puts in a small dynamic range.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

See https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.LogFormatter, but I think that explanation could be considerably improved.

@ShivamPathak99
Copy link
Contributor

If you remove the minor=False from the example you still get the unwanted minor ticks.

Because minor= False is the default value.

@evanberkowitz
Copy link
Contributor Author

logscale uses a formatter that labels the minor ticks if there is one or fewer major ticks. This is not super clear in the docs, but the alternative is a plot with no ticks if the user puts in a small dynamic range.

What I'm getting at is that if the user explicitly overrides the major ticks, it seems pretty weird to leave the minor ticks alone.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

logscale uses a formatter that labels the minor ticks if there is one or fewer major ticks. This is not super clear in the docs, but the alternative is a plot with no ticks if the user puts in a small dynamic range.

What I'm getting at is that if the user explicitly overrides the major ticks, it seems pretty weird to leave the minor ticks alone.

What behaviour would you expect?

@evanberkowitz
Copy link
Contributor Author

I would expect there to be no minor ticks at all, just as in the linear scale with set_{x,y}ticks.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

ax.set_xticks(..., minor=False) doesn't remove any minor ticks that may exist otherwise.

ax.set_xticks(np.arange(30), minor=True)
ax.set_xticks(np.arange(30, 10), minor=False)

doesn't remove the minor ticks.

@evanberkowitz
Copy link
Contributor Author

Just forget that I used minor=True in my starting example. Just like a linear scale would show no minor ticks if I did

ax.set_yticks(1./N, labels=[f'1/{each_n}' for each_n in N])  # no minor kwarg at all

I would have expected the same for the log scale.

@jklymak
Copy link
Member

jklymak commented Jan 25, 2024

Linear scales do not have minor ticks, by default. Logarithmic scales do have minor ticks, by default.

@timhoffm
Copy link
Member

timhoffm commented Jan 25, 2024

Just like a linear scale would show no minor ticks if I did [...]
I would have expected the same for the log scale.

The reason you don't see minor ticks for the linear scale is that the default AutoLocator does not use minor ticks.

The issue here is that LogLocator may introduce minor ticks for good reason, as explained above. And that set_ticks exclusively addresses major or minor ticks respectively. That's a bit odd, but I recommend to live with that oddity and manually deactivate minor ticks.

There are only two alternative mitigation strategies, both of which are not feasible:

  • One could rewrite the tick mechanism to fundamentally handle major and minor ticks together. But that'd be a major effort and possibly also has backward compatibility problems (we can't just simply make set_ticks(...) remove minor ticks).
  • One could try to special case the present case. But that can get complex as well: Let set_ticks remove minor ticks if you use a LogLocator. But then there's the danger that somebody explicitly set the minor ticks before the major ticks and of course these should not be removed. So maybe one would have to track whether the minor ticks come from the LogLocator or were explicitly set. I strongly advise against going down this rabbit whole.

I'm sorry there is no better answer, but these are the constraints of the, current design.

@timhoffm
Copy link
Member

timhoffm commented Jan 25, 2024

We should possibly explicitly state in set_ticks that the respective other kind of ticks is not changed.

@evanberkowitz
Copy link
Contributor Author

OK, it seems like this is expected behavior, or at least wont-be-changed-behavior. Then I would consider this issue resolved if the documentation of set_scale and set_ticks both explained what is going on.

timhoffm added a commit to timhoffm/matplotlib that referenced this issue Feb 16, 2024
timhoffm added a commit to timhoffm/matplotlib that referenced this issue Feb 16, 2024
timhoffm added a commit to timhoffm/matplotlib that referenced this issue Feb 16, 2024
timhoffm added a commit to timhoffm/matplotlib that referenced this issue Feb 18, 2024
@QuLogic QuLogic added this to the v3.9.0 milestone Feb 21, 2024
Impaler343 pushed a commit to Impaler343/matplotlib that referenced this issue Mar 8, 2024
Impaler343 pushed a commit to Impaler343/matplotlib that referenced this issue Mar 14, 2024
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.

7 participants