Skip to content

MaxNLocator changes the scientific notation exponent with different number of tick labels #12072

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
joelostblom opened this issue Sep 9, 2018 · 8 comments · Fixed by #12086
Closed
Milestone

Comments

@joelostblom
Copy link
Contributor

Bug report

Bug summary

MaxNLocator changes the scientific notation with different number of tick labels.

Code for reproduction

I encountered this unexpected behavior when I changed the y-ticklabel size. Going from size 13 to 14 changes the scientific notation from 1e9 to 1e10, and adds an decimal offset to all the ticklabels.

import matplotlib.pyplot as plt

sci_nums = [10**9, 8*10**9]
plt.rcParams['ytick.labelsize'] = 13
fig, ax = plt.subplots()
ax.scatter(range(len(sci_nums)), sci_nums)

image

plt.rcParams['ytick.labelsize'] = 14
fig, ax = plt.subplots()
ax.scatter(range(len(sci_nums)), sci_nums)

image

This is related to that the larger font size reduces the number of tickslabels. MaxNLocator apparently adjusts the scientific notation depending on the number of ticklabels.

plt.rcParams['ytick.labelsize'] = 13
fig, ax = plt.subplots()
ax.scatter(range(len(sci_nums)), sci_nums)

import matplotlib.ticker as ticker
ax.yaxis.set_major_locator(ticker.MaxNLocator(4))

image

I dont' quite understand the rule that MaxNLocator uses. Setting 1, 2, 4 or 5 ticklabels results in 1e10. Setting 3, 6, 7, 8, or 9 ticklabels results in 1e9.

Expected outcome
I would expect that changing the number of ticklabels (either directly or via another parameter such as the font size) would not result in a change in the formatting of the scientific notation. In this case, I would expect it to remain 1e9 for all the examples above.

Matplotlib version

  • Operating system: Linux
  • Matplotlib version: 2.2.2
  • Matplotlib backend (print(matplotlib.get_backend())): module://ipykernel.pylab.backend_inline
  • Python version: 3.6.6 (conda-forge)
  • Jupyter version (if applicable): jupyterlab=0.34.3, jupyter_core=4.4.0
  • Other libraries:
@dabana
Copy link
Contributor

dabana commented Sep 10, 2018

The integer keyword in MaxNlocator should do what you want. Have you tried it?

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

sci_nums = [10**9, 8*10**9]
fig, ax = plt.subplots()
ax.scatter(range(len(sci_nums)), sci_nums)
ax.yaxis.set_major_locator(ticker.MaxNLocator(4, integer = True))

I tried it, for my part it does not seem to work in the example you provided (Matplotlib version: 3.0.0rc1.post186+g5e01393)

dabana added a commit to dabana/matplotlib that referenced this issue Sep 10, 2018
…*. A first step

addressing issue matplotlib#12072.
Added a TODO note to *ScalarFormatter._orderOfMagnitude*: the formatter *._orderOfMagnitude*
method needs to be aware of the locator internal property
*_integer* in order to display the tick labels as integers.
@jklymak
Copy link
Member

jklymak commented Sep 10, 2018

The issue is actually in ScalarFormatter and its interaction with the extra ticks made by MaxNLocator. MaxNLocator(4) actually makes six ticks that are used for the orderOfMagnitude calculation, and the order of magnitiude of that larger span changes when you use 4 ticks instead of 6.

I don't actually think there is any reason to not just set the order of magnitude by visible ticks. Fix on its way...

@jklymak
Copy link
Member

jklymak commented Sep 10, 2018

Cross ref #11004

@dabana
Copy link
Contributor

dabana commented Sep 11, 2018

I am a little concerned by the handling of the integer keyword.

   *integer*
     If True, ticks will take only integer values, provided
     at least `min_n_ticks` integers are found within the
     view limits.

How can we be sure that only integer ticks are displayed if the formatter method axis.major.formatter does not know about the integer keyword provided to the tick locator, in that case maxNLocator?

@tacaswell tacaswell added this to the v3.1 milestone Sep 11, 2018
@jklymak
Copy link
Member

jklymak commented Sep 11, 2018

@dabana the ticks being chosen by the locator are integers in both cases. It’s the formatting of those integers that needs fixing.

@dabana
Copy link
Contributor

dabana commented Sep 11, 2018

I guess I am confused with what the integer keyword is supposed to do. I thought all the tick labels would end-up greater than 1 without decimals (like 1, 2, 3 ... or 3, 6, 9, etc...).

By reading the code, I understand than only the tick labels greater than 1 will be integer.

        if self._integer:
            # For steps > 1, keep only integer values.
            igood = (steps < 1) | (np.abs(steps - np.round(steps)) < 0.001)
            steps = steps[igood]

If this is the correct behavior it might be worth rewording the doc_string to make this clear. For exemple:

integer
If True, ticks greater than 1 will take only integer values, provided
at least min_n_ticks integers are found within the
view limits.

@jklymak
Copy link
Member

jklymak commented Sep 11, 2018

Integer on the locator will simply make sure the tick locations are integers. You are conflating the scientific notation of the formatter with the job of the locator.

@joelostblom
Copy link
Contributor Author

Thank you for fixing this @jklymak !!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants