-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
5 minor divisions when major ticks are 2.5 units apart #13069
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
5 minor divisions when major ticks are 2.5 units apart #13069
Conversation
As a side note, I personally feel that major ticks spaced 1 unit apart are more naturally divided into 4 minor divisions (spaced 0.25 units apart) instead of the current 5 minor divisions (spaced 0.2 units apart). If others agree, I can adjust that in this PR. |
I‘m also positive on on changing the subdivision of 1 unit from 5 to 4. |
Thanks @timhoffm. I'll wait a bit to see if there's anyone opposed. |
I'm not necessarily "opposed", but this seems to be special-casing a major tick interval you had to specify by hand. I don't know that its reasonable to special case all the possible manual tick intervals folks might give us in the minor tick code. If you specify the major ticks by hand, I think its reasonable to specify the minor ticks by hand. But maybe I'm missing a subtlety whereby this is more useful in general. |
Ah, sorry, I see that example "masks" the fact that 2.5 is one of the default major tick intervals.
It's just more "crowded" so I tried to make it clearer, sacrificing the point that 2.5 major tick interval is one of the defaults. |
Ah, OK, thats different. Thanks! |
lib/matplotlib/ticker.py
Outdated
majorlocs_no_exponent = 10 ** (np.log10(majorstep) % 1) | ||
|
||
# round to closest 0.5 | ||
majorlocs_no_exponent = round(majorlocs_no_exponent * 2) / 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we want to round 2.4 (for instance) to 2.5 do we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
majorlocs_no_exponent
is only used to decide if there should be 4 or 5 minor intervals. The rounding is there to take care of floating point inaccuracies so the major tick step interval can be searched for in the following line (number 2627).
There should be no effect on the major ticks (if that's your concern).
P.S. I just realized that the name of that variable is wrong - it should be majorstep_no_exponent
. I'll fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it’s that you are rounding 2.4 to 2.5 so if that was the major tick the minor ticks will now be different. I think you want to use np.isclose or some such to check and allow some float inaccuracies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, ok. Thanks. Replaced the round with np.isclose.
lib/matplotlib/tests/test_ticker.py
Outdated
|
||
# make sure we're checking all major steps in ticker.AutoLocator | ||
majorsteps = [x[0] for x in majorstep_minordivisions] | ||
assert np.allclose(majorsteps, mticker.AutoLocator()._steps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to make sure that the major steps in majorstep_minordivisions
are the ones in AutoLocator()._steps with the goal that test_number_of_minor_ticks
will fail if that's not true. So I added this assert outside any test function.
But if this assert fails pytest emits an error in the collection phase and doesn't run any tests.
Any suggestions how to improve this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just move it to its own test with the comment that it's really intended to test that the next test is correctly parametrized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - thanks.
I created a staticmethod, because it's the only way I found for the parameters to be accessible both in a test and by the @pytest.mark.parametrize
decorator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just make it a class variable like params
just above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - done.
55135d6
to
d079d67
Compare
lib/matplotlib/ticker.py
Outdated
|
||
majorstep_no_exponent = 10 ** (np.log10(majorstep) % 1) | ||
|
||
# The part after the 'or' is for #13069 to make minimal changes to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make the comment understandable without refering to github (perhaps just describe the desired behavior instead of refering to the behavior change).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed comment as it's no longer relevant.
lib/matplotlib/ticker.py
Outdated
# behaviour | ||
if np.isclose(majorstep_no_exponent, 2.5) or ( | ||
(not np.isclose(majorstep_no_exponent, [1, 10]).any() and | ||
int(np.round(majorstep_no_exponent)) in [1, 5, 10])): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two conditions (not close to 1 or 10, but possibly in [1 or 10]) seem contradictory.
Given that you are already going to use round(), I would just use its ability to round to one decimal point:
majorstep_no_exponent = round(10**(np.log10(majorstep) % 1), 1)
# (in fact the following looks more natural to me, but heh)
majorstep_no_exponent = round(majorstep / 10**int(np.log(majorstep)), 1)
if majorstep_no_exponent in [2.5, 5]: ...
else: ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @anntzer for the comments.
- The conditions are not close to 1 or 10 and when rounded are in [1 or 10], so not always contradictory (for example 1.1 satisfies both).
I was trying to make as minimal a change to the number of minor ticks calculation.
Rounding to 1 decimal place:
if round(majorstep_no_exponent, 1) in [2.5, 5]:
ndivs = 5
else:
ndivs = 4
will change the number of minor divisions from 5 to 4 for major ticks spaced 1.1 units apart (for example), compared to the situation before this PR. @jklymak indicated this is undesirable. The code is a bit ugly because of this, though.
- I think the calculation used
%
in order to guarantee 1 <= majorstep_no_exponent <= 10 because that's the range of the steps in AutoLocator.majorstep / 10**int(np.log10(majorstep))
can return values outside this range (for example if marjostep is 0.5).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re 1: I personally think if we're already changing the minorstep mapping for the common case of 1/5/10-spaced ticks (which appear by default), it's really not a big leap to also change the case of 1.1-spaced ticks (which don't even appear by default). This just needs to be documented... (plus, I'm pretty sure there's a lot of other places in the code which probably don't work so well with "weird" tick spacings like 1.1)
Re 2: Sorry, that should have been m/10**np.floor(np.log10(m))
(int rounds towards zero, but I want to round down), which works for m<1 too. Anyways, the formulas are equivalent, no big deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplified condition according to @anntzer's suggestion.
I went ahead and adjusted the number of minor ticks from 5 to 4 when the major ticks are spaced 1 or 10 units apart. |
Needs a changelog entry too. |
a769bef
to
cfa5393
Compare
I added an entry in doc/users/next_whats_new/. I hope that's the right place for it. I think that addresses all current comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving this open for comments for a bit longer as this is a behavior change, but looks reasonable to me.
lib/matplotlib/ticker.py
Outdated
|
||
majorstep_no_exponent = 10 ** (np.log10(majorstep) % 1) | ||
|
||
if np.isclose(majorstep_no_exponent, [2.5, 5.0]).any(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think that removing 1, 10 from this list is a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to the original 5 divisions for 1, 10.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put 1, 10 back in the list for 5 minor ticks.
Agreed during the call that this could be merged after restoring the old behavior for 1 & 10. |
My preference for 4 divisions for 1 and 10 is not strong (and might be just because that's what I'm used to). But I'd be interested to know why the consensus is that 5 divisions is better for 1 and 10. |
|
cfa5393
to
67b22e4
Compare
Thanks for the explanation @efiring. |
Split and expand related test.
67b22e4
to
4e227bd
Compare
Anything else I can do to make this PR more mergeable? |
The test failure was unrelated, so I went ahead with the merge. Thank you for the contribution. |
PR Summary
Currently when the major ticks are spaced 2.5 units apart there are 3 minor ticks spaced 0.625 units apart.
This PR increases the number of minor ticks to 4, spaced 0.5 units apart. To me, this is a much more natural spacing.
Before:

After:

PR Checklist